Python Pandas 滚动聚合一列列表

Posted

技术标签:

【中文标题】Python Pandas 滚动聚合一列列表【英文标题】:Python Pandas rolling aggregate a column of lists 【发布时间】:2019-07-06 03:30:35 【问题描述】:

我有一个简单的数据框 df,其中有一列列表 lists。我想根据lists 生成一个额外的列。

df 看起来像:

import pandas as pd
lists=1:[[1]],2:[[1,2,3]],3:[[2,9,7,9]],4:[[2,7,3,5]]
#create test dataframe
df=pd.DataFrame.from_dict(lists,orient='index')
df=df.rename(columns=0:'lists')
df

          lists
1           [1]
2     [1, 2, 3]
3  [2, 9, 7, 9]
4  [2, 7, 3, 5]

我希望df 看起来像这样:

df
Out[9]: 
          lists                 rolllists
1           [1]                       [1]
2     [1, 2, 3]              [1, 1, 2, 3]
3  [2, 9, 7, 9]     [1, 2, 3, 2, 9, 7, 9]
4  [2, 7, 3, 5]  [2, 9, 7, 9, 2, 7, 3, 5]

基本上我想对滚动的 2 个列表进行“总和”/append。请注意第 1 行,因为我只有 1 个列表 1,所以 rolllists 就是那个列表。但在第 2 行,我有 2 个要附加的列表。然后对于第三行,附加df[2].listsdf[3].lists 等。我以前做过类似的事情,参考这个:Pandas Dataframe, Column of lists, Create column of sets of cumulative lists, and record by record differences。 此外,如果我们可以在上面得到这部分,那么我想在groupby 中执行此操作(例如,下面的示例将是 1 组,例如,dfgroupby 中可能看起来像这样):

  Group         lists                 rolllists
1     A           [1]                       [1]
2     A     [1, 2, 3]              [1, 1, 2, 3]
3     A  [2, 9, 7, 9]     [1, 2, 3, 2, 9, 7, 9]
4     A  [2, 7, 3, 5]  [2, 9, 7, 9, 2, 7, 3, 5]
5     B           [1]                       [1]
6     B     [1, 2, 3]              [1, 1, 2, 3]
7     B  [2, 9, 7, 9]     [1, 2, 3, 2, 9, 7, 9]
8     B  [2, 7, 3, 5]  [2, 9, 7, 9, 2, 7, 3, 5]

我尝试了各种方法,例如 df.lists.rolling(2).sum(),但出现此错误:

TypeError: cannot handle this type -> object 

在 Pandas 0.24.1 中,不幸的是在 Pandas 0.22.0 中,该命令不会出错,而是返回与 lists 中完全相同的值。所以看起来新版本的 Pandas 不能汇总列表?这是次要问题。

喜欢任何帮助!玩得开心!

【问题讨论】:

df.lists.cumsum() 可以处理列表,但它不会为您提供滚动窗口。像这样组合列表并不是 Pandas 的真正目的...... @JoshFriedlander,你能补充一些关于为什么 Pandas 不是为此而构建的细节吗?我发现它对许多类型的列/行数据操作很有用。有很多例子,您存储列表并希望以某种方式组合它们。 Pandas 是为数据操作和时间序列而构建的,数据帧可以保存列表、集合等。事实上 cumsum 可以做到这一点,如果 rolling 可以的话那就太好了。也许是一种取和减去两个偏移累积和的方法? 抱歉,这是不正确的,因为列表不是 Pandas 价值观的“一等公民”。请参阅 Jeff Reback 的评论 here。 我确实读过。不幸的是,因为列表非常 Pythonic,并且可以处理您需要将数字或字符串组作为一个整体处理的情况。必须有办法做到这一点。就算丑 哦,我确定有 a 方法。祝你好运! 【参考方案1】:

你可以从

开始
import pandas as pd
mylists=1:[[1]],2:[[1,2,3]],3:[[2,9,7,9]],4:[[2,7,3,5]]
mydf=pd.DataFrame.from_dict(mylists,orient='index')
mydf=mydf.rename(columns=0:'lists')
mydf = pd.concat([mydf, mydf], axis=0, ignore_index=True)
mydf['group'] = ['A']*4 + ['B']*4

# initialize your new series
mydf['newseries'] = mydf['lists']

# define the function that appends lists overs rows
def append_row_lists(data):
    for i in data.index:
        try: data.loc[i+1, 'newseries'] = data.loc[i, 'lists'] + data.loc[i+1, 'lists']
        except: pass
    return data

# loop over your groups
for gp in mydf.group.unique():
    condition = mydf.group == gp
    mydf[condition] = append_row_lists(mydf[condition])

输出

          lists Group                 newseries
0           [1]     A                       [1]
1     [1, 2, 3]     A              [1, 1, 2, 3]
2  [2, 9, 7, 9]     A     [1, 2, 3, 2, 9, 7, 9]
3  [2, 7, 3, 5]     A  [2, 9, 7, 9, 2, 7, 3, 5]
4           [1]     B                       [1]
5     [1, 2, 3]     B              [1, 1, 2, 3]
6  [2, 9, 7, 9]     B     [1, 2, 3, 2, 9, 7, 9]
7  [2, 7, 3, 5]     B  [2, 9, 7, 9, 2, 7, 3, 5]

【讨论】:

这行得通。所以如果我想“滚动”7 个周期,我需要继续在函数中添加。希望有一种方法可以通过变量简化它。我可以问为什么异常中的“通过”吗?您能预见例外情况或只是小心吗?或者是“第一种情况”的通行证,其中没有正确的列表数量可以组合?我现在对此表示赞同,看看是否还有其他事情发生。它有效,如果有更好的解决方案避开我们,我将选择正确答案... 了解try|except,从函数中删除它们并更新函数。您将遇到来自pandasindexing 错误,因为最后一个indexer 不存在于数据框中。因此,如果发生 any 错误,只需 pass【参考方案2】:

这个怎么样?

rolllists = [df.lists[1].copy()]
for row in df.iterrows():
    index, values = row
    if index > 1:  # or > 0 if zero-indexed
        rolllists.append(df.loc[index - 1, 'lists'] + values['lists'])
df['rolllists'] = rolllists

或者作为一个更可扩展的函数:

lists=1:[[1]],2:[[1,2,3]],3:[[2,9,7,9]],4:[[2,7,3,5]]
df=pd.DataFrame.from_dict(lists,orient='index')
df=df.rename(columns=0:'lists')

def rolling_lists(df, roll_period=2):
    new_roll, rolllists = [], [df.lists[1].copy()] * (roll_period - 1)
    for row in df.iterrows():
        index, values = row
        if index > roll_period - 1:  # or -2 if zero-indexed
            res = []
            for i in range(index - roll_period, index):
                res.append(df.loc[i + 1, 'lists'])  # or i if 0-indexed
            rolllists.append(res)
    for li in rolllists:
        while isinstance(li[0], list):
            li = [item for sublist in li for item in sublist]  # flatten nested list
        new_roll.append(li)
    df['rolllists'] = new_roll
    return df

也很容易扩展到groupby,只需将其包装在一个函数中并使用df.apply(rolling_lists)。您可以提供任意数量的滚动行以用作roll_period。希望这会有所帮助!

【讨论】:

这是否涉及滚动“周期”?我错过了什么吗?即 2 个周期滚动,还是 5 个?我认为这仅适用于 2。 编辑我的答案以扩展到nroll_periods...检查一下 接下来我要试试这个。 @j。 doe 版本需要大量时间。 不幸的是,这个版本也花了相当长的时间。但工作如此赞成,但会给 J.Doe 接受的答案,因为 IMO 稍微容易一些。也就是说,对于我想要做的事情,我创建了一个解决方案,将列表转换为字符串,创建一个巨大的字符串作为卷,然后将该字符串转换回列表。无限快。例如,我的中型数据集的 J.Does 解决方案耗时 82 分钟。我的字符串返回列表花了 0.05 分钟...谢谢大家...

以上是关于Python Pandas 滚动聚合一列列表的主要内容,如果未能解决你的问题,请参考以下文章

20-pandas聚合

按多列分组,但只返回一行。仅聚合一列

根据 Presto/Hive 中的列值聚合列

通过来自两个文件的另一列中的对应值聚合一列中的值

使用 Featuretools 聚合一天中的每个时间

python--pandas分组聚合