如何在 Pandas 数据框中统计每列的非 NaN 值数量?

4

我的数据看起来像这样:

            Close   a   b   c   d   e   Time    
2015-12-03  2051.25 5   4   3   1   1   05:00:00    
2015-12-04  2088.25 5   4   3   1   NaN 06:00:00
2015-12-07  2081.50 5   4   3   NaN NaN 07:00:00
2015-12-08  2058.25 5   4   NaN NaN NaN 08:00:00
2015-12-09  2042.25 5   NaN NaN NaN NaN 09:00:00

我需要水平计算列('a' 至 'e')中不是NaN的值。所以结果应该是这样:

df['Count'] = .....
df

            Close   a   b   c   d   e   Time     Count
2015-12-03  2051.25 5   4   3   1   1   05:00:00 5  
2015-12-04  2088.25 5   4   3   1   NaN 06:00:00 4
2015-12-07  2081.50 5   4   3   NaN NaN 07:00:00 3
2015-12-08  2058.25 5   4   NaN NaN NaN 08:00:00 2
2015-12-09  2042.25 5   NaN NaN NaN NaN 09:00:00 1

谢谢


3
您所期望的数据框与起始数据框完全不同,从第二行到最后一行开始出现额外的“NaN”值。 - EdChum
感谢,已更正拼写错误。 - hernanavella
3个回答

9
您可以从数据框中进行子选择,并调用 count 函数并传递参数 axis=1:
In [24]:
df['count'] = df[list('abcde')].count(axis=1)
df

Out[24]:
              Close  a   b   c   d   e      Time  count
2015-12-03  2051.25  5   4   3   1   1  05:00:00      5
2015-12-04  2088.25  5   4   3   1 NaN  06:00:00      4
2015-12-07  2081.50  5   4   3 NaN NaN  07:00:00      3
2015-12-08  2058.25  5   4 NaN NaN NaN  08:00:00      2
2015-12-09  2042.25  5 NaN NaN NaN NaN  09:00:00      1

时间安排

In [25]:
%timeit df[['a', 'b', 'c', 'd', 'e']].apply(lambda x: sum(x.notnull()), axis=1)
%timeit df.drop(['Close', 'Time'], axis=1).count(axis=1)
%timeit df[list('abcde')].count(axis=1)

100 loops, best of 3: 3.28 ms per loop
100 loops, best of 3: 2.76 ms per loop
100 loops, best of 3: 2.98 ms per loop

apply 是最慢的,这并不奇怪,drop 版本略微快一些,但从语义上讲,我更喜欢只传递感兴趣的列列表,并调用 count 以提高可读性。

嗯,现在我一直得到不同的时间:

In [27]:
%timeit df[['a', 'b', 'c', 'd', 'e']].apply(lambda x: sum(x.notnull()), axis=1)
%timeit df.drop(['Close', 'Time'], axis=1).count(axis=1)
%timeit df[list('abcde')].count(axis=1)
%timeit df[['a', 'b', 'c', 'd', 'e']].count(axis=1)

100 loops, best of 3: 3.33 ms per loop
100 loops, best of 3: 2.7 ms per loop
100 loops, best of 3: 2.7 ms per loop
100 loops, best of 3: 2.57 ms per loop

更多时间设置

In [160]:
%timeit df[['a', 'b', 'c', 'd', 'e']].apply(lambda x: sum(x.notnull()), axis=1)
%timeit df.drop(['Close', 'Time'], axis=1).count(axis=1)
%timeit df[list('abcde')].count(axis=1)
%timeit df[['a', 'b', 'c', 'd', 'e']].count(axis=1)
%timeit df[list('abcde')].notnull().sum(axis=1) 

1000 loops, best of 3: 1.4 ms per loop
1000 loops, best of 3: 1.14 ms per loop
1000 loops, best of 3: 1.11 ms per loop
1000 loops, best of 3: 1.11 ms per loop
1000 loops, best of 3: 1.05 ms per loop

看起来在这个数据集上,测试 notnull 并求和(因为 notnull 会产生一个布尔掩码)更快。

在一个有 50000 行的数据框中,最后一种方法略微更快:

In [172]:
%timeit df[['a', 'b', 'c', 'd', 'e']].apply(lambda x: sum(x.notnull()), axis=1)
%timeit df.drop(['Close', 'Time'], axis=1).count(axis=1)
%timeit df[list('abcde')].count(axis=1)
%timeit df[['a', 'b', 'c', 'd', 'e']].count(axis=1)
%timeit df[list('abcde')].notnull().sum(axis=1) 

1 loops, best of 3: 5.83 s per loop
100 loops, best of 3: 6.15 ms per loop
100 loops, best of 3: 6.49 ms per loop
100 loops, best of 3: 6.04 ms per loop

另外你可以尝试的是:df[list('abcde')].notnull().sum(axis=1),在我的测试中比上述任何方法都稍微快一些。 - n8yoder
1
@n8yoder 这会稍微快一些,将在更大的数据集上尝试。 - EdChum

2
df['Count'] = df[['a', 'b', 'c', 'd', 'e']].apply(lambda x: sum(x.notnull()), axis=1)

In [1254]: df
Out[1254]: 
              Close  a   b   c   d   e      Time  Count
2015-12-03  2051.25  5   4   3   1   1  05:00:00      5
2015-12-04  2088.25  5   4   3   1 NaN  06:00:00      4
2015-12-07  2081.50  5   4   3 NaN NaN  07:00:00      3
2015-12-08  2058.25  5   4 NaN NaN NaN  08:00:00      2
2015-12-09  2042.25  5 NaN NaN NaN NaN  09:00:00      1

1

包括所需的列表,或者仅删除您不想从计数中排除的两个 - 沿着axis=1(查看文档):

df['Count'] = df.drop(['Close', 'Time'], axis=1).count(axis=1)


     Close  a  b   c   d   e      Time  Count
0  2051.25  5  4   3   1   1  05:00:00      5
1  2088.25  5  4   3   1 NaN  06:00:00      4
2  2081.50  5  4   3 NaN NaN  07:00:00      3
3  2058.25  5  4   3 NaN NaN  08:00:00      3
4  2042.25  5  4 NaN NaN NaN  09:00:00      2

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