그룹 별 총 팬더 비율
이것은 명백히 간단하지만, 새로운 초보자로서 나는 붙어 있습니다.
주, 사무실 ID 및 해당 사무실에 대한 3 개의 열이 포함 된 CSV 파일이 있습니다.
특정 주에서 사무실 당 판매 비율을 계산하려고합니다 (각 주에서 모든 백분율의 총계는 100 % 임).
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': range(1, 7) * 2,
'sales': [np.random.randint(100000, 999999)
for _ in range(12)]})
df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
이것은 다음을 반환합니다.
sales
state office_id
AZ 2 839507
4 373917
6 347225
CA 1 798585
3 890850
5 454423
CO 1 819975
3 202969
5 614011
WA 2 163942
4 369858
6 959285
나는에 "까지 도달"방법을 알아낼 수없는 것 state
의 수준 groupby
을 총 sales
전체에 대한 state
비율을 계산합니다.
폴 H의 대답은 두 번째해야 할 것이다 것이 맞다 groupby
객체를,하지만 당신은 간단한 방법으로 비율을 계산할 수 있습니다 - 다만 과 분열 의 합에 의해 열을. Paul H의 답변 시작 부분 복사 :groupby
state_office
sales
# From Paul H
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': list(range(1, 7)) * 2,
'sales': [np.random.randint(100000, 999999)
for _ in range(12)]})
state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
# Change: groupby state_office and divide by sum
state_pcts = state_office.groupby(level=0).apply(lambda x:
100 * x / float(x.sum()))
보고:
sales
state office_id
AZ 2 16.981365
4 19.250033
6 63.768601
CA 1 19.331879
3 33.858747
5 46.809373
CO 1 36.851857
3 19.874290
5 43.273852
WA 2 34.707233
4 35.511259
6 29.781508
상태별로 그룹화하는 두 번째 groupby 오브젝트를 작성하고 div
메소드 를 사용해야합니다 .
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': list(range(1, 7)) * 2,
'sales': [np.random.randint(100000, 999999) for _ in range(12)]})
state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
state = df.groupby(['state']).agg({'sales': 'sum'})
state_office.div(state, level='state') * 100
sales
state office_id
AZ 2 16.981365
4 19.250033
6 63.768601
CA 1 19.331879
3 33.858747
5 46.809373
CO 1 36.851857
3 19.874290
5 43.273852
WA 2 34.707233
4 35.511259
6 29.781508
level='state'
에서 kwarg이 div
방송에 팬더를 알려줍니다 /를의 값에 dataframes 기반을 결합 state
인덱스의 수준.
간결성을 위해 SeriesGroupBy를 사용합니다.
In [11]: c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
In [12]: c
Out[12]:
state office_id
AZ 2 925105
4 592852
6 362198
CA 1 819164
3 743055
5 292885
CO 1 525994
3 338378
5 490335
WA 2 623380
4 441560
6 451428
Name: count, dtype: int64
In [13]: c / c.groupby(level=0).sum()
Out[13]:
state office_id
AZ 2 0.492037
4 0.315321
6 0.192643
CA 1 0.441573
3 0.400546
5 0.157881
CO 1 0.388271
3 0.249779
5 0.361949
WA 2 0.411101
4 0.291196
6 0.297703
Name: count, dtype: float64
여러 그룹의 경우 Radical df를 사용하여 변환을 사용해야합니다 .
In [21]: c = df.groupby(["Group 1","Group 2","Final Group"])["Numbers I want as percents"].sum().rename("count")
In [22]: c / c.groupby(level=[0, 1]).transform("sum")
Out[22]:
Group 1 Group 2 Final Group
AAHQ BOSC OWON 0.331006
TLAM 0.668994
MQVF BWSI 0.288961
FXZM 0.711039
ODWV NFCH 0.262395
...
Name: count, dtype: float64
이것은 다른 답변보다 약간 더 성능이 좋은 것으로 보입니다 (Radiocal의 답변 속도의 두 배 미만, 나에게 ~ 0.08 초).
벤치마킹이 필요하다고 생각합니다. OP의 원본 DataFrame을 사용하여
df = pd.DataFrame({
'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': range(1, 7) * 2,
'sales': [np.random.randint(100000, 999999) for _ in range(12)]
})
1 앤디 헤이든
그의 답변에 대해 언급 한 바와 같이 Andy는 벡터화 및 팬더 색인 생성을 최대한 활용합니다.
c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
c / c.groupby(level=0).sum()
루프 당 3.42ms ± 16.7µs
(평균 7 번의 런, 평균 100 개의 루프)
둘째 폴 H
state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
state = df.groupby(['state']).agg({'sales': 'sum'})
state_office.div(state, level='state') * 100
4.66 MS ± 24.4 μs의 루프 당
(평균 ± 표준. DEV. 7 개 실행의 100 개 루프 각)
세 번째 exp1orer
이것은 레벨 0에서 x.sum()
각각 x
에 대해 계산할 때 가장 느린 답변 입니다.
나에게 이것은 현재 유용한 형식은 아니지만 여전히 유용한 대답입니다. 작은 데이터 세트에서 빠른 EDA apply
를 수행하려면 메소드 체인 을 사용 하여이를 한 줄로 작성할 수 있습니다 . 따라서 변수 이름에 대한 결정이 필요하지 않습니다. 실제로 가장 귀중한 리소스 (뇌)에 계산 비용이 많이 듭니다 .
여기 수정 사항이 있습니다.
(
df.groupby(['state', 'office_id'])
.agg({'sales': 'sum'})
.groupby(level=0)
.apply(lambda x: 100 * x / float(x.sum()))
)
10.6 MS ± 81.5 μs의 루프 당
(평균 ± 표준. DEV. 7 개 실행의 100 개 루프 각)
따라서 작은 데이터 세트에서 아무도 6ms를 신경 쓰지 않습니다. 그러나 이것은 3 배 빠른 속도이며 높은 카디널리티 그룹을 가진 더 큰 데이터 세트에서 큰 차이를 만들 것입니다.
위의 코드에 추가하여 14412 상태 범주와 600 office_ids로 모양 (12,000,000, 3)의 DataFrame을 만듭니다.
import string
import numpy as np
import pandas as pd
np.random.seed(0)
groups = [
''.join(i) for i in zip(
np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
)
]
df = pd.DataFrame({'state': groups * 400,
'office_id': list(range(1, 601)) * 20000,
'sales': [np.random.randint(100000, 999999)
for _ in range(12)] * 1000000
})
앤디를 사용하여
루프 당 2 초 ± 10.4ms
(평균 7 회 실행, 각 1 루프의 평균 ± 표준)
exp1orer
루프 당 19 초 ± 77.1ms
(평균 7 회 실행, 각 1 회 루프 평균 ± 표준)
이제 큰 카디널리티 데이터 세트에서 x10 속도가 향상되었습니다.
당신이 이것을 UV라면이 3 가지 대답을 UV로해라!
나는 이것이 오래된 질문이라는 것을 알고 있지만 exp1orer의 대답은 많은 고유 그룹 (아마 람다 때문에)이있는 데이터 세트의 경우 매우 느립니다. 나는 그것들을 배열 계산으로 바꾸기 위해 그들의 답을 얻었으므로 이제는 매우 빠릅니다! 아래는 예제 코드입니다.
50,000 개의 고유 그룹으로 테스트 데이터 프레임 생성
import random
import string
import pandas as pd
import numpy as np
np.random.seed(0)
# This is the total number of groups to be created
NumberOfGroups = 50000
# Create a lot of groups (random strings of 4 letters)
Group1 = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups/10)]*10
Group2 = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups/2)]*2
FinalGroup = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups)]
# Make the numbers
NumbersForPercents = [np.random.randint(100, 999) for _ in range(NumberOfGroups)]
# Make the dataframe
df = pd.DataFrame({'Group 1': Group1,
'Group 2': Group2,
'Final Group': FinalGroup,
'Numbers I want as percents': NumbersForPercents})
그룹화하면 다음과 같습니다.
Numbers I want as percents
Group 1 Group 2 Final Group
AAAH AQYR RMCH 847
XDCL 182
DQGO ALVF 132
AVPH 894
OVGH NVOO 650
VKQP 857
VNLY HYFW 884
MOYH 469
XOOC GIDS 168
HTOY 544
AACE HNXU RAXK 243
YZNK 750
NOYI NYGC 399
ZYCI 614
QKGK CRLF 520
UXNA 970
TXAR MLNB 356
NMFJ 904
VQYG NPON 504
QPKQ 948
...
[50000 rows x 1 columns]
백분율을 찾는 배열 방법 :
# Initial grouping (basically a sorted version of df)
PreGroupby_df = df.groupby(["Group 1","Group 2","Final Group"]).agg({'Numbers I want as percents': 'sum'}).reset_index()
# Get the sum of values for the "final group", append "_Sum" to it's column name, and change it into a dataframe (.reset_index)
SumGroup_df = df.groupby(["Group 1","Group 2"]).agg({'Numbers I want as percents': 'sum'}).add_suffix('_Sum').reset_index()
# Merge the two dataframes
Percents_df = pd.merge(PreGroupby_df, SumGroup_df)
# Divide the two columns
Percents_df["Percent of Final Group"] = Percents_df["Numbers I want as percents"] / Percents_df["Numbers I want as percents_Sum"] * 100
# Drop the extra _Sum column
Percents_df.drop(["Numbers I want as percents_Sum"], inplace=True, axis=1)
이 방법은 약 0.15 초가 소요됩니다
최고 답변 방법 (람다 함수 사용) :
state_office = df.groupby(['Group 1','Group 2','Final Group']).agg({'Numbers I want as percents': 'sum'})
state_pcts = state_office.groupby(level=['Group 1','Group 2']).apply(lambda x: 100 * x / float(x.sum()))
이 방법은 동일한 결과를 생성하는 데 약 21 초가 걸립니다.
결과:
Group 1 Group 2 Final Group Numbers I want as percents Percent of Final Group
0 AAAH AQYR RMCH 847 82.312925
1 AAAH AQYR XDCL 182 17.687075
2 AAAH DQGO ALVF 132 12.865497
3 AAAH DQGO AVPH 894 87.134503
4 AAAH OVGH NVOO 650 43.132050
5 AAAH OVGH VKQP 857 56.867950
6 AAAH VNLY HYFW 884 65.336290
7 AAAH VNLY MOYH 469 34.663710
8 AAAH XOOC GIDS 168 23.595506
9 AAAH XOOC HTOY 544 76.404494
나는 이미 여기에 좋은 대답이 있다는 것을 알고 있습니다.
그럼에도 불구하고, 나는 이와 같이 초등하고 간단한 질문을 느끼기 때문에 내 자신을 기고하고 싶습니다. 한눈에 이해할 수있는 짧은 해결책이 있어야합니다.
또한 백분율을 새 열로 추가하여 나머지 데이터 프레임을 그대로 유지하는 방식으로 작동해야합니다. 마지막으로, 그룹화 수준이 둘 이상인 경우 (예 : 주가 아닌 주와 국가) 명백한 방식으로 일반화해야합니다.
다음 스 니펫은 이러한 기준을 충족합니다.
df['sales_ratio'] = df.groupby(['state'])['sales'].transform(lambda x: x/x.sum())
여전히 Python 2를 사용하는 경우 람다 항의 분모에서 x를 float (x)로 바꿔야합니다.
sum
전체를 전체 DataFrame
로 나눌 수 있습니다 state
.
# Copying setup from Paul H answer
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': list(range(1, 7)) * 2,
'sales': [np.random.randint(100000, 999999) for _ in range(12)]})
# Add a column with the sales divided by state total sales.
df['sales_ratio'] = (df / df.groupby(['state']).transform(sum))['sales']
df
보고
office_id sales state sales_ratio
0 1 405711 CA 0.193319
1 2 535829 WA 0.347072
2 3 217952 CO 0.198743
3 4 252315 AZ 0.192500
4 5 982371 CA 0.468094
5 6 459783 WA 0.297815
6 1 404137 CO 0.368519
7 2 222579 AZ 0.169814
8 3 710581 CA 0.338587
9 4 548242 WA 0.355113
10 5 474564 CO 0.432739
11 6 835831 AZ 0.637686
그러나 이것은 state
숫자 이외의 모든 열 이 전체 DataFrame의 합계를 가능하게 하기 때문에 작동합니다 . 예를 들어, office_id
대신 문자 인 경우 오류가 발생합니다.
df.office_id = df.office_id.astype(str)
df['sales_ratio'] = (df / df.groupby(['state']).transform(sum))['sales']
TypeError : / : 'str'및 'str'에 대해 지원되지 않는 피연산자 유형
나는 이것이 한 줄로 트릭을 할 것이라고 생각합니다.
df.groupby(['state', 'office_id']).sum().transform(lambda x: x/np.sum(x)*100)
열이나 인덱스에서 백분율을 찾는 가장 우아한 방법은을 사용하는 것 pd.crosstab
입니다.
샘플 데이터
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': list(range(1, 7)) * 2,
'sales': [np.random.randint(100000, 999999) for _ in range(12)]})
출력 데이터 프레임은 다음과 같습니다
print(df)
state office_id sales
0 CA 1 764505
1 WA 2 313980
2 CO 3 558645
3 AZ 4 883433
4 CA 5 301244
5 WA 6 752009
6 CO 1 457208
7 AZ 2 259657
8 CA 3 584471
9 WA 4 122358
10 CO 5 721845
11 AZ 6 136928
인덱스, 열 및 집계 할 값을 지정하십시오. normalize 키워드는 컨텍스트에 따라 인덱스 또는 열에서 %를 계산합니다.
result = pd.crosstab(index=df['state'],
columns=df['office_id'],
values=df['sales'],
aggfunc='sum',
normalize='index').applymap('{:.2f}%'.format)
print(result)
office_id 1 2 3 4 5 6
state
AZ 0.00% 0.20% 0.00% 0.69% 0.00% 0.11%
CA 0.46% 0.00% 0.35% 0.00% 0.18% 0.00%
CO 0.26% 0.00% 0.32% 0.00% 0.42% 0.00%
WA 0.00% 0.26% 0.00% 0.10% 0.00% 0.63%
내가 사용한 간단한 방법은 2 그룹 바이가 간단한 나누기를 한 후에 병합하는 것입니다.
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': list(range(1, 7)) * 2,
'sales': [np.random.randint(100000, 999999) for _ in range(12)]})
state_office = df.groupby(['state', 'office_id'])['sales'].sum().reset_index()
state = df.groupby(['state'])['sales'].sum().reset_index()
state_office = state_office.merge(state, left_on='state', right_on ='state', how = 'left')
state_office['sales_ratio'] = 100*(state_office['sales_x']/state_office['sales_y'])
state office_id sales_x sales_y sales_ratio
0 AZ 2 222579 1310725 16.981365
1 AZ 4 252315 1310725 19.250033
2 AZ 6 835831 1310725 63.768601
3 CA 1 405711 2098663 19.331879
4 CA 3 710581 2098663 33.858747
5 CA 5 982371 2098663 46.809373
6 CO 1 404137 1096653 36.851857
7 CO 3 217952 1096653 19.874290
8 CO 5 474564 1096653 43.273852
9 WA 2 535829 1543854 34.707233
10 WA 4 548242 1543854 35.511259
11 WA 6 459783 1543854 29.781508
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': list(range(1, 7)) * 2,
'sales': [np.random.randint(100000, 999999)
for _ in range(12)]})
grouped = df.groupby(['state', 'office_id'])
100*grouped.sum()/df[["state","sales"]].groupby('state').sum()
보고:
sales
state office_id
AZ 2 54.587910
4 33.009225
6 12.402865
CA 1 32.046582
3 44.937684
5 23.015735
CO 1 21.099989
3 31.848658
5 47.051353
WA 2 43.882790
4 10.265275
6 45.851935
(이 솔루션은이 기사 https://pbpython.com/pandas_transform.html 에서 영감을 받았습니다 )
다음 솔루션을 사용하는 것이 가장 간단하고 아마도 가장 빠릅니다 transformation
.
Transformation: While aggregation must return a reduced version of the data, transformation can return some transformed version of the full data to recombine. For such a transformation, the output is the same shape as the input.
So using transformation
, the solution is 1-liner:
df['%'] = 100 * df['sales'] / df.groupby('state')['sales'].transform('sum')
And if you print:
print(df.sort_values(['state', 'office_id']).reset_index(drop=True))
state office_id sales %
0 AZ 2 195197 9.844309
1 AZ 4 877890 44.274352
2 AZ 6 909754 45.881339
3 CA 1 614752 50.415708
4 CA 3 395340 32.421767
5 CA 5 209274 17.162525
6 CO 1 549430 42.659629
7 CO 3 457514 35.522956
8 CO 5 280995 21.817415
9 WA 2 828238 35.696929
10 WA 4 719366 31.004563
11 WA 6 772590 33.298509
참고URL : https://stackoverflow.com/questions/23377108/pandas-percentage-of-total-with-groupby
'programing tip' 카테고리의 다른 글
JavaScript로 길게 누르시겠습니까? (0) | 2020.08.03 |
---|---|
PHP 현재 디렉토리 이름 가져 오기 (0) | 2020.08.03 |
JPA 2에서 CriteriaQuery를 사용하여 결과를 계산하는 방법 (0) | 2020.08.03 |
div에서 가로 스크롤 막대를 어떻게 제거합니까? (0) | 2020.08.03 |
ASP.NET MVC3 Razor에서 읽기 전용 텍스트 상자를 만드는 방법 (0) | 2020.08.03 |