5

我在处理 pandas 中的分组对象时遇到了一些麻烦。具体来说,我希望能够将列中的第一行设置为 0,同时保持其他行不变。

例如:

df = pd.DataFrame({'A': ['foo', 'bar', 'baz'] * 2,
                        'B': rand.randn(6),
                        'C': rand.rand(6) > .5})

给我

    A         B      C
0  foo  1.624345  False
1  bar -0.611756   True
2  baz -0.528172  False
3  foo -1.072969   True
4  bar  0.865408  False
5  baz -2.301539   True

我将它们按 A 分组并按 B 排序:

f = lambda x: x.sort('B', ascending=True)
sort_df = df.groupby('A',sort=False).apply(f)

得到这个:

         A         B      C
    A                          
foo 3  foo -1.072969   True
    0  foo  1.624345  False
bar 1  bar -0.611756   True
    4  bar  0.865408  False
baz 5  baz -2.301539   True
    2  baz -0.528172  False

现在我有了组,我希望能够将每个组中的第一个元素设置为 0。我该怎么做?

像这样的东西会起作用,但我想要一种更优化的方法:

for group in sort_df.groupby(level=0).groups:
    sort_df.loc[(group,sort_df.loc[group].index[0]),'B']=0

任何帮助将不胜感激,谢谢!

4

3 回答 3

3

这是执行此操作的矢量化方式。会快得多

In [26]: pd.set_option('max_rows',10)

创建一个具有 2 级多索引的框架,按 A 排序(此处任意)

In [27]: df = DataFrame(dict(A = np.random.randint(0,100,size=N),B=np.random.randint(0,100,size=N),C=np.random.randn(N))).sort(columns=['A'])

In [28]: df
Out[28]: 
        A   B         C
61474   0  40 -0.731163
82386   0  18 -1.316136
63372   0  28  0.112819
49666   0  13 -0.649491
31631   0  89 -0.835208
...    ..  ..       ...
42178  99  28 -0.029800
59529  99  31 -0.733588
13503  99  60  0.672754
20961  99  18  0.252714
31882  99  22  0.083340

[100000 rows x 3 columns]

重置索引以捕获索引值。根据 B 找到第一个值

In [29]: grouped = df.reset_index().groupby('B').first()

In [30]: grouped
Out[30]: 
    index  A         C
B                     
0   26576  0  1.123605
1   38311  0  0.128966
2   45135  0 -0.039886
3   38434  0 -1.284028
4   82088  0 -0.747440
..    ... ..       ...
95  82620  0 -1.197625
96  63278  0 -0.625400
97  23226  0 -0.497609
98  82520  0 -0.828773
99  35902  0 -0.199752

[100 rows x 3 columns]

这为您提供了一个进入框架的索引器。

In [31]: df.loc[grouped['index']] = 0

In [32]: df
Out[32]: 
        A   B         C
61474   0   0  0.000000
82386   0   0  0.000000
63372   0   0  0.000000
49666   0   0  0.000000
31631   0   0  0.000000
...    ..  ..       ...
42178  99  28 -0.029800
59529  99  31 -0.733588
13503  99  60  0.672754
20961  99  18  0.252714
31882  99  22  0.083340

[100000 rows x 3 columns]

如果你想

In [33]: df.sort_index()
Out[33]: 
        A   B         C
0      40  56 -1.223941
1      24  77 -0.039775
2       7  83  0.741013
3      48  38 -1.795053
4      62  15 -2.734968
...    ..  ..       ...
99995  20  25 -0.286300
99996  27  21 -0.120430
99997   0   4  0.607524
99998  38  31  0.717069
99999  33  63 -0.226888

[100000 rows x 3 columns]

这种方法

In [34]: %timeit df.loc[grouped['index']] = 0
100 loops, best of 3: 7.33 ms per loop

你的原创

In [37]: %timeit df.groupby('A',sort=False).apply(f)
10 loops, best of 3: 109 ms per loop

如果您有更多组,则此性能差异将扩大。

于 2014-08-12T18:31:31.813 回答
0

这是你要找的吗?

sort_df.B[::2]=0

例如:

sort_df

          A         B      C
A                          
foo 0  foo  0.192347   True
    3  foo  0.295985   True
bar 1  bar  0.012400  False
    4  bar  0.628488   True
baz 5  baz  0.180934   True
    2  baz  0.328735   True


sort_df.B[::2]=0

sort_df
         A         B      C
A                          
foo 0  foo  0.000000   True
    3  foo  0.295985   True
bar 1  bar  0.000000  False
    4  bar  0.628488   True
baz 5  baz  0.000000   True
    2  baz  0.328735   True

仅当all(df.A.value_counts()==df.A.value_counts()[0])为 True 时才有效。

于 2014-08-12T17:27:17.927 回答
0

你已经在应用一个函数来做一些工作。为什么不把它包括在内?

而不是使用

lambda f: ...

只需使用:

def f(x):
    x = x.sort('B', ascending=True)
    x.iloc[0, 1] = 0
    return x

sort_df = df.groupby('A',sort=False).apply(f)
于 2014-08-12T17:34:11.450 回答