带有NaN的Pandas分位数计算失败

8
我是一名有用的助手,可以为您翻译文本。
我在计算四分位数范围时遇到了一个有趣的情况。假设我们有一个数据框如下:
import pandas as pd
index=pd.date_range('2014 01 01',periods=10,freq='D')
data=pd.np.random.randint(0,100,(10,5))
data = pd.DataFrame(index=index,data=data)

data
Out[90]: 
             0   1   2   3   4
2014-01-01  33  31  82   3  26
2014-01-02  46  59   0  34  48
2014-01-03  71   2  56  67  54
2014-01-04  90  18  71  12   2
2014-01-05  71  53   5  56  65
2014-01-06  42  78  34  54  40
2014-01-07  80   5  76  12  90
2014-01-08  60  90  84  55  78
2014-01-09  33  11  66  90   8
2014-01-10  40   8  35  36  98

# test for q1 values (this works)
data.quantile(0.25)
Out[111]: 
0    40.50
1     8.75
2    34.25
3    17.50
4    29.50

# break it by inserting row of nans
data.iloc[-1] = pd.np.NaN

data.quantile(0.25)
Out[115]: 
0    42
1    11
2    34
3    12
4    26

首位四分位数可以通过计算数据框中低于总体中位数的值的中位数来计算,因此我们可以看到数据.quantile(0.25) 应该得出什么结果。例如:

med = data.median()
q1  = data[data<med].median()
q1
Out[119]: 
0    37.5
1     8.0
2    19.5
3    12.0
4    17.0

似乎分位数无法提供适当的q1等表示,因为它不能很好地处理NaN值(即它可以在没有NaN的情况下工作,但有NaN却不行)。 我认为这可能不是一个“NaN”问题,而是分位数无法处理偶数数据集(即需要将中位数计算为两个中心数字的平均值时)。 然而,在测试具有奇数和偶数行数的数据帧时,我发现分位数正确处理了这些情况。 问题似乎仅在数据框中存在NaN值时出现。 我想使用分位数计算我的数据框中的滚动q1 / q3值,但这在存在NaN时将无法工作。 有人能提供解决此问题的方法吗?

你试过 df.dropna() 吗? - acushner
我已经应用了dropna(how='all'),这使得我的示例具有误导性。然而,我不想应用dropna(how='any'),因为我不想仅仅因为NaN在下一列中出现就丢失有效数据。你的建议很好。话虽如此,quantile似乎仍然存在根本性问题(至少我是这么认为的!)。 - tnknepp
抱歉伙计,祝你好运! - acushner
4
你确定结果应该是 data[data<med].median() 而不是 data[data<=med].median() 吗? - alko
1
这个(@alko的上面评论)确实与Pandas的quantile给出了相同的结果。 - joris
1个回答

4

在内部,quantile使用numpy.percentile来处理非空值。当你将data的最后一行更改为NaNs时,实际上留下的是一个数组array([ 33., 46., 71., 90., 71., 42., 80., 60., 33.]) 的第一列。

计算 np.percentile(array([ 33., 46., 71., 90., 71., 42., 80., 60., 33.]) 的结果为 42。

根据文档字符串:

给定长度为N的向量V,V的第q个分位数是有序副本中排名第q次的值。 如果标准化排序与q不完全匹配,则使用两个最近邻居的加权平均值。 当q=50时,与中位数相同;当q=0时,与最小值相同;当q=100时,与最大值相同。


另外需要补充的是,如果您直接使用np.percentile(包括NaN),例如使用df2.apply(np.percentile, args=[25]),将会得到其他值,因为在计算分位数时NaN并未被移除,而Pandas则会移除。 - joris
@TomAugspurger 代码 np.percentile(np.array([33., 46., 71., 90., 71., 42., 80., 60., 33.], 0.25) 返回的是32.999,而不是42。我认为使用np.quantile()可以返回42,代码np.quantile(np.array([33., 46., 71., 90., 71., 42., 80., 60., 33.], 0.25)返回42。 - Chong Onn Keat

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