使用groupby对象pandas编辑数据帧条目

2024-10-01 11:30:32 发布

您现在位置:Python中文网/ 问答频道 /正文

考虑以下数据帧:

     index      count     signal
       1          1         1
       2          1        NAN
       3          1        NAN
       4          1        -1
       5          1        NAN
       6          2        NAN
       7          2        -1
       8          2        NAN
       9          3        NAN
       10         3        NAN
       11         3        NAN
       12         4        1
       13         4        NAN
       14         4        NAN

我需要在'signal'中'ffill'nan,并且具有不同'count'值的值不应相互影响。这样我就可以得到以下数据帧:

^{pr2}$

现在,我在groupbyobject中迭代每个数据帧,填充NAN值,然后复制到一个新的数据帧:

new_table = np.array([]); 
for key, group in df.groupby('count'):
    group['signal'] = group['signal'].fillna(method='ffill')
    group1 = group.copy()
    if new_table.shape[0]==0:
        new_table = group1
    else:
        new_table = pd.concat([new_table,group1])

哪种方法可行,但考虑到数据帧很大,速度非常慢。我想知道是否有其他方法可以使用或不使用groupby方法。谢谢!在

编辑:

感谢Alexander和jwilner提供了替代方法。不过,这两种方法对于我的大数据帧来说都非常慢,它有80万行数据。在


Tags: 数据方法newindexsignalcounttablegroup
3条回答

假设数据已经在df['index']上进行了预排序,请尝试改用loc

for c in df['count'].unique():
    df.loc[df['count'] == c, 'signal'] = df[df['count'] == c].ffill()

>>> df
    index  count signal
0       1      1      1
1       2      1      1
2       3      1      1
3       4      1     -1
4       5      1     -1
5       6      2    NaN
6       7      2     -1
7       8      2     -1
8       9      3    NaN
9      10      3    NaN
10     11      3    NaN
11     12      4      1
12     13      4      1
13     14      4      1

使用apply方法。在

In [56]: df = pd.DataFrame({"count": [1] * 4 + [2] * 5 + [3] * 2 , "signal": [1] + [None] * 4 + [-1] + [None] * 5})

In [57]: df
Out[57]:
    count  signal
0       1       1
1       1     NaN
2       1     NaN
3       1     NaN
4       2     NaN
5       2      -1
6       2     NaN
7       2     NaN
8       2     NaN
9       3     NaN
10      3     NaN

[11 rows x 2 columns]

In [58]: def ffill_signal(df):
   ....:     df["signal"] = df["signal"].ffill()
   ....:     return df
   ....:

In [59]: df.groupby("count").apply(ffill_signal)
Out[59]:
    count  signal
0       1       1
1       1       1
2       1       1
3       1       1
4       2     NaN
5       2      -1
6       2      -1
7       2      -1
8       2      -1
9       3     NaN
10      3     NaN

[11 rows x 2 columns]

但是,请注意,groupby会重新排序。如果count列不是始终保持不变或增加,而是可能有重复的值,groupby可能会有问题。也就是说,给定一个count系列,比如[1, 1, 2, 2, 1]groupby会这样分组:[1, 1, 1], [2, 2],这可能会对你的前向填充产生不良影响。如果这是不需要的,那么您必须创建一个新的序列来与groupby一起使用,该序列始终保持不变或根据计数序列的变化而增加,可能使用pd.Series.diffpd.Series.cumsum

另一种解决方案是创建一个透视表,向前填充值,然后将它们映射回原始数据帧。在

df2 = df.pivot(columns='count', values='signal', index='index').ffill()
df['signal'] = [df2.at[i, c] 
                for i, c in zip(df2.index, df['count'].tolist())]
>>> df
    count  index  signal
0       1      1       1
1       1      2       1
2       1      3       1
3       1      4      -1
4       1      5      -1
5       2      6     NaN
6       2      7      -1
7       2      8      -1
8       3      9     NaN
9       3     10     NaN
10      3     11     NaN
11      4     12       1
12      4     13       1
13      4     14       1

对于800k行数据,这种方法的有效性取决于“count”中有多少个唯一值。在

与我之前的回答相比:

^{pr2}$

最后,您可以简单地使用groupby,尽管它比前面的方法慢:

df.groupby('count').ffill()
Out[191]: 
    index  signal
0       1       1
1       2       1
2       3       1
3       4      -1
4       5      -1
5       6     NaN
6       7      -1
7       8      -1
8       9     NaN
9      10     NaN
10     11     NaN
11     12       1
12     13       1
13     14       1

%%timeit
df.groupby('count').ffill()

100 loops, best of 3: 3.55 ms per loop

相关问题 更多 >