Pandas:在包含列表对象的系列上重叠前向填充

Pandas: Forward fill with overlap on Series containing List Objects

我有一个 Series/DataFrame 就像这个。其中包含的元素是一个或多个值的列表:

0      NaN
1     [40]
2      NaN
3      NaN
4      NaN
5      NaN
6      NaN
7      NaN
8      NaN
9     [35]
10     NaN
11     NaN
12    [28]
13     NaN
14     NaN
15     NaN
16     NaN
17     NaN
Name: tags, dtype: object

我想用最多连续五个条目的最新值来填充缺失值。限制为 5 的 ffill 是最合适的。但是我的用例是这样的,我希望前向填充重叠。我的预期输出看起来像这样:

0          NaN
1         [40]
2         [40]
3         [40]
4         [40]
5         [40]
6         [40]
7          NaN
8          NaN
9         [35]
10        [35]
11        [35]
12        [28]
13    [35, 28]
14    [35, 28]
15        [28]
16        [28]
17        [28]
Name: tags, dtype: object

上面的例子是为了简单起见,我描述的这个函数是一个更大的 pd.groupby 操作的一部分,带有更多的标签,因此 python 循环不是'没什么帮助。我不关心带有标签本身的索引,只有那些 filled 对我来说很重要。也许使用 pandas cumsum 并根据索引差异进行切片的方法在这里可行吗?

解决这个问题的任何想法都会对我有极大的帮助。提前致谢!

# init the DataFrame
temp = pd.DataFrame({"tags":[
    np.nan, [40], np.nan, np.nan, np.nan, 
    np.nan, np.nan, np.nan, np.nan, [35], 
    np.nan, np.nan, [28], np.nan, np.nan, 
    np.nan, np.nan, np.nan]})

# initialize the result with empty lists for list concatenation
temp['ctags'] = temp['tags'].apply(lambda x: [] if type(x) == float else x)

window = 5
for i in range(1, window):
    temp['ctags'] = temp['ctags'] + temp['tags'].shift(i).apply(lambda x: [] if type(x) == float else x)

temp['ctags']

给出输出:

0           []
1         [40]
2         [40]
3         [40]
4         [40]
5         [40]
6           []
7           []
8           []
9         [35]
10        [35]
11        [35]
12    [28, 35]
13    [28, 35]
14        [28]
15        [28]
16        [28]
17          []

我能够为我的问题想出这个快速解决方案。但这里的问题是它没有我希望的那样高效,而且如果我将填充限制增加到 10,它的效率会比现在更低。

编辑: 添加循环以实现可重用性。累积解决方案,因此内存效率更高。

你可以试试:

# fill na by empty list 
df['tags'] = [[] if na else s for s, na in zip(df['tags'], df['tags'].isna())]

# compute rolling windows
df['res'] = [[l for ls in window for l in ls] for window in df['tags'].rolling(5)]
print(df)

输出

    tags       res
0     []        []
1   [40]      [40]
2     []      [40]
3     []      [40]
4     []      [40]
5     []      [40]
6     []        []
7     []        []
8     []        []
9   [35]      [35]
10    []      [35]
11    []      [35]
12  [28]  [35, 28]
13    []  [35, 28]
14    []      [28]
15    []      [28]
16    []      [28]
17    []        []

作为替代方案,您可以使用 chain.from_iterable:

from itertools import chain

# compute rolling windows
df['res'] = [list(chain.from_iterable(window)) for window in df['tags'].rolling(5)]

请参阅此 以比较 pandas 中列表展平的几种方法。