我正在尝试转换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
方法)。但是,我认为现在大熊猫不可能。
答案 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