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

问题描述 投票:0回答:5

我正在尝试转换 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 

这是使用 count 函数进行聚合的逆过程。有没有一种简单的方法可以在 pandas 中实现它(不使用 for 循环或列表理解)?

一种可能是允许

DataFrame.applymap
函数返回多行(类似于
apply
GroupBy
方法)。然而,我认为现在在 pandas 中这是不可能的。

python pandas dataframe
5个回答
31
投票

你可以使用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

您可以随意修改结果的索引


7
投票

甚至还有一种更简单、更高效的解决方案。 我必须对大约 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

5
投票

我知道这是一个老问题,但我很难让 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 列 x 约 6k 行,扩展时约 100,000 行需要约 7 秒。在这种情况下,我不确定分组是否必要或明智,因为它会将所有列分组,但是嘿,无论如何,只有 7 秒。


2
投票

这个问题很老了,答案并不反映pandas的现代能力。您可以使用

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 的好处。


0
投票

这个问题非常非常古老,现有的答案并不能反映 pandas 的现代能力。

import pandas as pd

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

def replicate_row(row):
    _class = row.to_dict()['class']
    return [{'class': _class}] * row['count']

result = df.apply(replicate_row, axis=1)
flat_list = [item for sublist in result for item in sublist]
new_df = pd.DataFrame(flat_list)
print(new_df)

输出:

class
0     A
1     C
2     C
© www.soinside.com 2019 - 2024. All rights reserved.