熊猫占总数的百分比

eri*_*kcw 115 python pandas

这显然很简单,但作为一个笨拙的新人我会陷入困境.

我有一个CSV文件,其中包含3个列,State,Office ID和该办公室的Sales.

我想计算某个州的每个办公室的销售百分比(每个州的所有百分比总和为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'})
Run Code Online (Sandbox Code Playgroud)

返回:

                  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
Run Code Online (Sandbox Code Playgroud)

我似乎无法弄清楚如何"高达"的state水平groupby与总起来sales对整个state计算分数.

exp*_*rer 172

保罗轰的答案是正确的,你将不得不作出第二个groupby对象,但你可以在一个简单的方式计算百分比-只是groupbystate_office和划分sales其总和列.复制保罗H答案的开头:

# 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()))
Run Code Online (Sandbox Code Playgroud)

返回:

                     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
Run Code Online (Sandbox Code Playgroud)

  • @dhardy`state_office`是一个带有多索引的系列 - 所以它只是一列,其值都是数字.在执行groupby之后,每个`x`都是该列的子集.那有意义吗? (4认同)
  • 这里发生了什么?据我了解,“x”是某种表格,因此“100 * x”直观上没有意义(特别是当某些单元格包含“AZ”等字符串时,...)。 (2认同)
  • 它可能,但它对我不起作用。Python 3 中的 Pandas 的工作方式是否有所不同? (2认同)
  • ‘level=0’是什么意思? (2认同)
  • @Veenit表示您是按索引的第一级而不是其中一个列进行分组。 (2认同)

Pau*_*l H 44

您需要创建第二个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
Run Code Online (Sandbox Code Playgroud)

level='state'在kwarg div告诉大熊猫广播/在连接上的值dataframes基state索引的水平.

  • 如果你有3个索引,这个方法有用吗?我第一次在3列上做了一个组合.然后我只用2做了第二组,并计算总和.然后我尝试使用`div`但是使用`level = ["index1","index2"]`但是它告诉我"两个MultiIndex对象之间的连接是不明确的". (4认同)

Can*_*ner 36

(这个解决方案的灵感来自这篇文章https://pbpython.com/pandas_transform.html

我发现以下解决方案是最简单的(可能也是最快的)使用transformation

转换:虽然聚合必须返回数据的简化版本,但转换可以返回完整数据的一些转换版本以重新组合。对于这样的转换,输出与输入的形状相同。

因此,使用transformation,解决方案是 1-liner:

df['%'] = 100 * df['sales'] / df.groupby('state')['sales'].transform('sum')
Run Code Online (Sandbox Code Playgroud)

如果你打印:

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
Run Code Online (Sandbox Code Playgroud)

  • @Cancer这是我最喜欢的答案,因为它将 df 保留为 df (不转换为系列)并且仅添加 % 列。谢谢 (5认同)

And*_*den 26

为简明起见,我将使用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
Run Code Online (Sandbox Code Playgroud)

对于多个组,您必须使用transform(使用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
Run Code Online (Sandbox Code Playgroud)

这似乎比其他答案稍微高一点(仅比Radical的答案速度低两倍,对我来说〜0.08s).

  • 这超级快.我建议将此作为首选的熊猫方法.真正利用numpy的矢量化和熊猫索引. (4认同)

Lit*_*les 17

我认为这需要基准测试.使用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)]
})
Run Code Online (Sandbox Code Playgroud)

第一个安迪海登

正如他对答案的评论,Andy充分利用了矢量化和熊猫索引.

c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
c / c.groupby(level=0).sum()
Run Code Online (Sandbox Code Playgroud)

每回路3.42 ms ±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
Run Code Online (Sandbox Code Playgroud)

每循环4.66 ms ±24.4μs
(平均值±标准偏差,7次运行,每次100次循环)


第3次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()))
)
Run Code Online (Sandbox Code Playgroud)

每回路10.6 ms ±81.5μs
(平均值±标准偏差,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
})
Run Code Online (Sandbox Code Playgroud)

使用Andy的,

每回路2 s ±10.4 ms
(平均值±标准偏差,7次运行,每次1次循环)

和exp1orer

每回路19 s ±77.1 ms
(平均值±标准偏差,7次运行,每次1次循环)

所以现在我们看到x10在大型高基数数据集上加速.


如果你用紫外线这一个,一定要把这三个答案给紫外线!


Rad*_*ard 9

我知道这是一个老问题,但对于具有大量唯一组(可能是因为lambda)的数据集,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})
Run Code Online (Sandbox Code Playgroud)

分组后,它看起来像:

                             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]
Run Code Online (Sandbox Code Playgroud)

查找百分比的数组方法:

# 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)
Run Code Online (Sandbox Code Playgroud)

该方法大约需要0.15秒

最佳答案方法(使用lambda函数):

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()))
Run Code Online (Sandbox Code Playgroud)

该方法需要约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
Run Code Online (Sandbox Code Playgroud)


ajk*_*hol 8

查找跨列或索引的百分比的最优雅方法是使用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)]})
Run Code Online (Sandbox Code Playgroud)

输出数据帧是这样的

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
Run Code Online (Sandbox Code Playgroud)

只需指定要聚合的索引、列和值。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%
Run Code Online (Sandbox Code Playgroud)


igg*_*ggy 6

您可以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
Run Code Online (Sandbox Code Playgroud)

退货

    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
Run Code Online (Sandbox Code Playgroud)

但请注意,这仅适用于除state数字以外的所有列,从而可以对整个 DataFrame 求和。例如,如果office_id改为字符,则会出现错误:

df.office_id = df.office_id.astype(str)
df['sales_ratio'] = (df / df.groupby(['state']).transform(sum))['sales']
Run Code Online (Sandbox Code Playgroud)

类型错误:不支持 / 的操作数类型:'str' 和 'str'


Mig*_*ous 6

我意识到这里已经有了很好的答案。

尽管如此,我还是想贡献自己的力量,因为我对这样一个基本的,简单的问题感到,应该有一眼就能理解的简短解决方案。

它也应该以可以将百分比添加为新列的方式工作,而其余数据框保持不变。最后但并非最不重要的一点是,它应该以明显的方式推广到存在多个分组级别的情况(例如,州和国家而不是仅州)。

以下代码段满足这些条件:

df['sales_ratio'] = df.groupby(['state'])['sales'].transform(lambda x: x/x.sum())
Run Code Online (Sandbox Code Playgroud)

请注意,如果您仍在使用Python 2,则必须用float(x)替换lambda项的分母中的x。

  • 如果您有多个级别,这将不起作用。 (2认同)
  • 这个答案很棒。它不涉及创建临时的“groupby”对象,非常简洁,并且从左到右阅读非常合乎逻辑。 (2认同)

Vis*_*han 6

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)]})

df.groupby(['state', 'office_id'])['sales'].sum().rename("weightage").groupby(level = 0).transform(lambda x: x/x.sum())
df.reset_index()
Run Code Online (Sandbox Code Playgroud)

输出:

    state   office_id   weightage
0   AZ  2   0.169814
1   AZ  4   0.192500
2   AZ  6   0.637686
3   CA  1   0.193319
4   CA  3   0.338587
5   CA  5   0.468094
6   CO  1   0.368519
7   CO  3   0.198743
8   CO  5   0.432739
9   WA  2   0.347072
10  WA  4   0.355113
11  WA  6   0.297815

Run Code Online (Sandbox Code Playgroud)