Python Pandas按多列分组,创建字符串列表但将数字求和

时间:2018-07-14 11:27:31

标签: python pandas pandas-groupby

当前我的数据框看起来类似于:

     ID  Year   Str1     Str2     Value
0    1   2014   high     black    120
1    1   2015   high     blue     20
2    2   2014   medium   red      10
3    2   2014   medium   blue     50
4    3   2015   low      blue     30
5    3   2015   high     blue     .5
6    3   2015   high     red      10

所需:

     ID  Year   Str1        Str2          Value
0    1   2014   high        black         120
1    1   2015   high        blue          20
2    2   2014   medium      red, blue     60
3    3   2015   low, high   blue, red     40.5

尝试按ID和Name列分组,然后获取数字总和,但列出字符串。如果像示例中那样可以删除重复的字符串,那将有所帮助,但这不是必需的。

此操作将对约100个数据帧执行,ID和Year是在每个数据帧中都可以找到的唯一列名。数据框确实略有不同:它们具有value列,str列或两者都有。

我浏览了很多stackoverflow并尝试过:

df.groupby(['ID', 'Year'], as_index=False).agg(lambda x: x.sum() if x.dtype=='int64' else ', '.join(x))

给出错误的DataFrame对象没有属性dtype(这很有意义,因为按多列分组会返回更多数据帧)。

我还尝试过逐一循环列,然后,如果列中有数字,它将计算总和,否则创建一个列表:

for col in df:
    if col in ['ID', 'Year']:
        continue 

    if df[col].dtype.kind == 'i' or df[col].dtype.kind == 'f':
         df = df.groupby(['ID', 'Year'])[col].apply(sum)
    else:
         df = df.groupby(['ID', 'Year'])[col].unique().reset_index()

但是,第一次执行该操作后,它摆脱了所有其他列。

谢谢。

2 个答案:

答案 0 :(得分:2)

您需要检查numeric列,例如由this solution

df = (df.groupby(['ID', 'Year'], as_index=False)
       .agg(lambda x: x.sum() if np.issubdtype(x.dtype, np.number) else ', '.join(x)))
print (df)
   ID  Year             Str1             Str2  Value
0   1  2014             high            black  120.0
1   1  2015             high             blue   20.0
2   2  2014   medium, medium        red, blue   60.0
3   3  2015  low, high, high  blue, blue, red   40.5

from pandas.api.types import is_numeric_dtype

df = (df.groupby(['ID', 'Year'], as_index=False)
        .agg(lambda x: x.sum() if is_numeric_dtype(x) else ', '.join(x)))

答案 1 :(得分:1)

我有一个类似的问题,所以说我有一个这样的数据,我想通过电子邮件对列进行分组,并对不同的列执行不同的agg函数,因此标准的groupby函数还不够好。

无论如何,这是一个虚拟数据集:

    Email            Phone          State
0   email@gmail.com 123-456-7890    NY
1   email@gmail.com 321-654-0987    LA
2   person@gmail.com    123-789-4567    WA
3   dummy@gmail.com 873-345-3456    MN
4   dummy@gmail.com 123-345-3456    NY
5   email@gmail.com 000-000-0000    KY

知道哪个是第一个受骗物品会很有用,因此我们将其处理而忽略其他物品。所以首先,我要标记第一个重复的项目。

这看起来很复杂,但是它的作用是:得到一个list of True vals for all the dupes并与一个list of True vals for all first dupes.进行“与”运算

df["first_dupe"] = df.duplicated("Email", keep=False) & ~df.duplicated("Email", keep="first")

然后将此功能应用于数据框:

def combine_rows(row, key="Email", cols_to_combine=["Phone", "State"]):
    """takes in a row, looks at the key column
        if its the first dupe, combines the data in cols_to_combine with the other rows with same key
        needs a dataframe with a bool column first_dupe with True if the row is the first dupe"""

    if row["first_dupe"] == True:
        # making a df of dupes item
        dupes = df[df[key]==row[key]]

        for i, dupe_row in dupes.iloc[1:].iterrows():   # skipping the first row, since thats our first_dupe
            for col in cols_to_combine:
                row[col] += ", " + dupe_row[col]
        # make sure first_dupe doesn't get processed again
        row.first_dupe = False  
    return row

df = df.apply(combine_rows, axis=1, result_type=None)

您可以修改合并行功能以对不同的列执行不同的操作。