在 Pandas DataFrame 中查找每行的第一个非零值

12

我有一个 Pandas DataFrame:

import pandas as pd

df = pd.DataFrame([[0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
                   [1.0, 0.0, 1.0, 3.0, 1.0, 1.0, 7.0, 0.0],
                   [0.0, 0.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0]
                  ]
                  , columns=['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'])

     A    B     C     D     E     F     G     H
0  0.0  2.0   3.0   4.0   5.0   6.0   7.0   8.0
1  1.0  0.0   1.0   3.0   1.0   1.0   7.0   0.0
2  0.0  0.0  13.0  14.0  15.0  16.0  17.0  18.0

我想返回每一行中第一个非零值的系列(而不是列表)。这样可以实现,但是lookup返回的是一个列表,而不是一个系列(我知道我可以将列表转换为系列),但我认为应该有更好的方法:

first_nonzero_colnames = (df > 0).idxmax(axis=1, skipna=True)
df.lookup(first_nonzero_colnames.index, first_nonzero_colnames.values)

[  2.   1.  13.]

我可以使用.apply,但我想避免它。

5个回答

13

试试这个:

res = df[df != 0.0].bfill(axis=1)['A']

我所做的只是将所有非零值替换为NaN,然后从右侧填充它们,这将强制第一列中所有结果值成为该行中第一个非零值。

或者,如@piRSquared建议的更快捷的方式:

df.replace(0, np.nan).bfill(1).iloc[:, 0]

1
这个答案真好。我还在努力写我的。但是我会用df.replace(0, np.nan).bfill(1).iloc[:, 0] - piRSquared
@piRSquared:是的,我也有类似的东西,df.mask(df == 0).bfill(1).iloc[:,0],但是我只是被打败了一点点! - root
2
刚刚运行了 %%timeit,我的建议只需要三分之一的时间,但是概念是相同的。我不介意您把它包含在您的答案中。 - piRSquared

7

@acushner的回答更好。我只是想提一下。

使用idxmaxapply

m = (df != 0).idxmax(1)
df.T.apply(lambda x: x[m[x.name]])

0     2.0
1     1.0
2    13.0
dtype: float64

这也可以工作:

m = (df != 0).idxmax(1)
t = zip(m.index, m.values)

df.stack().loc[t].reset_index(1, drop=True)

他们在pandas中没有类似于高级索引的东西吗?比如df[np.arange(3),m]之类的?或者这样做没有意义,因为这不会保持数据框的2D格式? - Divakar

6

这似乎有效:

df[df!=0].cumsum(axis=1).min(axis=1)
Out[74]: 
0     2.0
1     1.0
2    13.0
dtype: float64

3

我不确定是否应该称之为“更好”。但它可以在一行中返回一个系列。

df.apply(lambda x: x[np.where(x > 0)[0][0]], axis=1)
>>>
0     2.0
1     1.0
2    13.0
dtype: float64

是的,我正在尝试避免使用 apply - slaw

0

这里有一个非常快的方法,使用 .apply 和 .nonzero()

 df2.apply(lambda x: x.iloc[x.nonzero()[0][0]], axis=1)
 >>>
 0     2.0
 1     1.0
 2    13.0
 dtype: float64

性能:

%%timeit
df2.apply(lambda x: x.iloc[x.nonzero()[0][0]], axis=1)
>>>
190 µs ± 8.18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接