熊猫分组和组合行

时间:2018-05-15 00:21:14

标签: python pandas dataframe group-by pandas-groupby

我有一个大型的DataFrame,如下所示:

+----------------------------------------------------------+
| Date           Category        Location       ImpactRate |
+----------------------------------------------------------+
| 2018-04-22     Outage          MT               0.05194  |
| 2018-04-22     Outage          ND               0.02552  |
| 2018-04-22     Outage          SD               0.09962  |
| 2018-04-24     Transport       TX               0.03111  |
+----------------------------------------------------------+

我要做的是创建以下输出:

+-----------------------------------------------------------------------------------+
|   Date           Category        ImpactRate        Break Down             |
+-----------------------------------------------------------------------------------+
| 2018-04-22     Outage          0.17708           MT (29.3%) SD (14.4%) ND (56.3%) |
| 2018-04-24    Transport       0.03111            TX (100.0%)                      |
+-----------------------------------------------------------------------------------+

首次尝试 - GroupBy

grouped_df = df.groupby('Date')['ImpactRate'].apply(list).apply(pd.Series).rename(columns=df['Location'])'

这会创建一个包含NaN的DataFrame,其中每个Location都不存在,并创建需要格式化的其他列。

第二次尝试 - 使用itertuples()循环:

r = []
for item in df.itertuples():
    temp_x = df.loc[((df['Category'] == item[2]) & (df['Date'] == item[1]))
    for i in range(temp_x.shape[0]):
        r.append(temp_x['ImpactRate'].iloc[i])

这会创建一个巨大的ImpactRate列表 - 这会让我回到原点。

我不知道如何解决这个问题。我猜我应该在每个迭代的列表中列出一个列表,但我一直在圈子里。如何以最pythonic的方式实现此输出? (请解释,以便我可以学习!)

2 个答案:

答案 0 :(得分:1)

这是使用围绕groupby.transform的矢量化功能的一种方式。我已将Breakdown系列定义为元组列表,因为这是最灵活的格式。如果您愿意,可以应用特定的字符串格式。

import pandas as pd

df = pd.DataFrame({'Date': ['2018-04-22', '2018-04-22', '2018-04-22', '2018-04-24'],
                   'Category': ['Outage', 'Outage', 'Outage', 'Transport'],
                   'Location': ['MT', 'ND', 'SD', 'TX'],
                   'ImpactRate': [0.05194, 0.02552, 0.09962, 0.03111]})

# apply ratio
df['Total'] = df.groupby(['Date', 'Category'])['ImpactRate'].transform('sum')
df['ImpactRate'] /= df['Total']

# create tuple column
df['Breakdown'] = list(zip(df.Location, df.ImpactRate))

# groupby to list
df = df.groupby(['Category', 'Date', 'Total'])['Breakdown'].apply(list).reset_index()

结果:

print(df)

    Category        Date    Total  \
0     Outage  2018-04-22  0.17708   
1  Transport  2018-04-24  0.03111   

                                           Breakdown  
0  [(MT, 0.293313756494), (ND, 0.144115653942), (...  
1                                        [(TX, 1.0)]  

答案 1 :(得分:1)

我对此感觉不太好......但是很好。

d = pd.Series(
    df.ImpactRate.values,
    [list(zip(df.Date, df.Category)), df.Location],
    name='Impact Rate'
)

s = d.sum(level=0)
t = d.div(s, level=0).rename('Break Down')

f = lambda t: ' '.join(f'{l} ({v*100:0.2f}%)' for (_, l), v in t.items())

pd.DataFrame(pd.concat(
    [s, t.groupby(level=0).apply(f)], axis=1
).to_dict()).rename_axis(['Date', 'Category']).reset_index()

         Date   Category                           Break Down  Impact Rate
0  2018-04-22     Outage  MT (29.33%) ND (14.41%) SD (56.26%)      0.17708
1  2018-04-24  Transport                         TX (100.00%)      0.03111
相关问题