将函数应用于可以返回多行的pandas DataFrame

时间:2012-10-24 13:14:11

标签: python pandas dataframe

我正在尝试转换DataFrame,这样一些行将被复制给定次数。例如:

df = pd.DataFrame({'class': ['A', 'B', 'C'], 'count':[1,0,2]})

  class  count
0     A      1
1     B      0
2     C      2

应转换为:

  class 
0     A   
1     C   
2     C 

这与计数功能的聚合相反。是否有一种简单的方法可以在熊猫中实现它(不使用for循环或列表推导)?

一种可能性是允许DataFrame.applymap函数返回多行(类似apply GroupBy方法)。但是,我认为现在大熊猫不可能。

5 个答案:

答案 0 :(得分:20)

您可以使用groupby:

def f(group):
    row = group.irow(0)
    return DataFrame({'class': [row['class']] * row['count']})
df.groupby('class', group_keys=False).apply(f)

所以你得到了

In [25]: df.groupby('class', group_keys=False).apply(f)
Out[25]: 
  class
0     A
0     C
1     C

您可以根据需要修改结果的索引

答案 1 :(得分:3)

我知道这是一个老问题,但我遇到了Wes'回答数据框中多列的工作,所以我使他的代码更通用。以为我会分享以防其他人在遇到同样问题时遇到这个问题。

您只需基本指定哪个列具有其中的计数,然后您将获得扩展的数据帧。

import pandas as pd
df = pd.DataFrame({'class 1': ['A','B','C','A'],
                   'class 2': [ 1,  2,  3,  1], 
                   'count':   [ 3,  3,  3,  1]})
print df,"\n"

def f(group, *args):
    row = group.irow(0)
    Dict = {}
    row_dict = row.to_dict()
    for item in row_dict: Dict[item] = [row[item]] * row[args[0]]
    return pd.DataFrame(Dict)

def ExpandRows(df,WeightsColumnName):
    df_expand = df.groupby(df.columns.tolist(), group_keys=False).apply(f,WeightsColumnName).reset_index(drop=True)
    return df_expand


df_expanded = ExpandRows(df,'count')
print df_expanded

返回:

  class 1  class 2  count
0       A        1      3
1       B        2      3
2       C        3      3
3       A        1      1 

  class 1  class 2  count
0       A        1      1
1       A        1      3
2       A        1      3
3       A        1      3
4       B        2      3
5       B        2      3
6       B        2      3
7       C        3      3
8       C        3      3
9       C        3      3

关于速度,我的基础df是10列~6k行,当扩展时~100,000行需要~7秒。在这种情况下,我不确定分组是必要的还是明智的,因为它将所有列分组,但是只要7秒就可以了。

答案 2 :(得分:1)

repeated_items = [list(row[1]*row[2]) for row in df.itertuples()]

将创建一个嵌套列表:

[['A'], [], ['C', 'C']]

然后您可以使用列表推导进行迭代以创建新的数据框:

new_df = pd.DataFrame({"class":[j for i in repeated_items for j in i]})

当然,如果您愿意,也可以在一行中完成:

new_df = pd.DataFrame({"class":[j for i in [list(row[1]*row[2]) for row in df.itertuples()] for j in i]})

答案 3 :(得分:1)

这个问题非常陈旧,答案并不能反映熊猫的现代能力。您可以使用iterrows遍历每一行,然后使用DataFrame构造函数创建具有正确行数的新DataFrame。最后,使用pd.concat将所有行连接在一起。

pd.concat([pd.DataFrame(data=[row], index=range(row['count'])) 
           for _, row in df.iterrows()], ignore_index=True)

  class  count
0     A      1
1     C      2
2     C      2

这样可以使用任何大小的DataFrame。

答案 4 :(得分:1)

甚至有一个更简单,更有效的解决方案。 我必须对大约350万行的表进行类似的修改,并且以前建议的解决方案非常慢。

一种更好的方法是使用numpy的 repeat 过程生成一个新索引,其中每个行索引根据其给定的计数重复多次,并使用 iloc 进行根据此索引选择原始表的行:

import pandas as pd
import numpy as np

df = pd.DataFrame({'class': ['A', 'B', 'C'], 'count': [1, 0, 2]})
spread_ixs = np.repeat(range(len(df)), df['count'])
spread_ixs 

array([0, 2, 2])

df.iloc[spread_ixs, :].drop(columns='count').reset_index(drop=True)

  class
0     A
1     C
2     C