熊猫系列的分位数函数的倒数是啥?
Posted
技术标签:
【中文标题】熊猫系列的分位数函数的倒数是啥?【英文标题】:what's the inverse of the quantile function on a pandas Series?熊猫系列的分位数函数的倒数是什么? 【发布时间】:2014-12-16 19:43:11 【问题描述】:分位数函数为我们提供给定熊猫系列的分位数s,
例如
s.quantile(0.9) 是 4.2
是否有逆函数(即累积分布)找到 x 的值使得
s.quantile(x)=4
谢谢
【问题讨论】:
【参考方案1】:我和你有同样的问题!我找到了一种使用 scipy 获得分位数倒数的简单方法。
#libs required
from scipy import stats
import pandas as pd
import numpy as np
#generate ramdom data with same seed (to be reproducible)
np.random.seed(seed=1)
df = pd.DataFrame(np.random.uniform(0,1,(10)), columns=['a'])
#quantile function
x = df.quantile(0.5)[0]
#inverse of quantile
stats.percentileofscore(df['a'],x)
【讨论】:
值得注意的是,如果您的系列中有 NaN 值,则分数函数的分位数和百分位数似乎不会以相同的方式对待它们,即这些函数彼此并不完全相反。 请注意,当分位数与某个值不精确对齐时,pandas 插值会导致结果不一致;例如,尝试quantile(0.51)
,反之亦然。
只做 y = stats.percentileofscore(df['a'].dropna(), x) 得到与 df['a].quantile(y) == x 匹配的逆【参考方案2】:
排序可能很昂贵,如果您寻找单个值,我猜您最好使用以下方法计算它:
s = pd.Series(np.random.uniform(size=1000))
( s < 0.7 ).astype(int).mean() # =0.7ish
可能有一种方法可以避免 int(bool) 恶作剧。
【讨论】:
这很聪明。 (s 如何获取多个值,类似于分位数方法?您可以使用 q 参数将 quantile 传递给要计算的分位数列表。【参考方案3】:从数学上讲,您试图找到CDF 或返回s
小于或等于q
的值或分位数的概率:
F(q) = Pr[s <= q]
大家可以使用 numpy 试试这个一行代码:
np.mean(s.to_numpy() <= q)
【讨论】:
Imo 使用 mean(x) = sum(x)/len(x) 一个优雅的解决方案需要一点解释:它计算 (sum(x)) 有多少值小于或等于 q并使其成为相对频率 (/len(x))。这是 ECDF 的定义(经验分布函数en.wikipedia.org/wiki/Empirical_distribution_function)。从技术上讲,'s.to_numpy()' 用于将 's' 带入正确的格式(例如,如果 's' 是像 '[1,2,2,3,3]' 这样的列表) - 如果's' 已经是一个熊猫系列 (´s = pd.Series([1,2,2, 3, 3])´),如 OP 中所述。 @Qaswed,我认为“mean(x) = sum(x)/len(x) 一个优雅的解决方案需要一点解释是不正确的:它很重要 (sum(x))有多少值小于或等于 q 并使其成为相对频率 (/len(x))。” sum(x) 只是将 x 中的所有元素相加。分布函数将涉及一个指标函数的总和(x 的指标 【参考方案4】:据我所知,没有 1-liner,但您可以使用 scipy 实现这一点:
import pandas as pd
import numpy as np
from scipy.interpolate import interp1d
# set up a sample dataframe
df = pd.DataFrame(np.random.uniform(0,1,(11)), columns=['a'])
# sort it by the desired series and caculate the percentile
sdf = df.sort('a').reset_index()
sdf['b'] = sdf.index / float(len(sdf) - 1)
# setup the interpolator using the value as the index
interp = interp1d(sdf['a'], sdf['b'])
# a is the value, b is the percentile
>>> sdf
index a b
0 10 0.030469 0.0
1 3 0.144445 0.1
2 4 0.304763 0.2
3 1 0.359589 0.3
4 7 0.385524 0.4
5 5 0.538959 0.5
6 8 0.642845 0.6
7 6 0.667710 0.7
8 9 0.733504 0.8
9 2 0.905646 0.9
10 0 0.961936 1.0
现在我们可以看到这两个函数是互逆的。
>>> df['a'].quantile(0.57)
0.61167933268395969
>>> interp(0.61167933268395969)
array(0.57)
>>> interp(df['a'].quantile(0.43))
array(0.43)
interp 还可以接收列表、numpy 数组或 pandas 数据系列,真的是任何迭代器!
【讨论】:
【参考方案5】:刚刚遇到同样的问题。这是我的两分钱。
def inverse_percentile(arr, num):
arr = sorted(arr)
i_arr = [i for i, x in enumerate(arr) if x > num]
return i_arr[0] / len(arr) if len(i_arr) > 0 else 1
【讨论】:
【参考方案6】:s
中小于 x
的记录的百分比
# Find the percentile of `x` in `s`
(s<x).mean() # i.e., (s<x).sum()/len(s)
或者,当s
被排序时:
s.searchsorted(x)/len(s)
另请参阅:pandas.Series.searchsorted
【讨论】:
【参考方案7】:我使用np.searchsorted 函数“查找应插入元素以保持顺序的索引”:
np.random.seed(seed=1)
#we want to find the 5th 10-tile of a series of 20 elements
S = 20
N = 10
n = 5
df = pd.DataFrame(np.random.uniform(0,1,S), columns=['a'])
#quantile N function
q = df['a'].quantile(np.arange(0,N+1)/(N))
print(q)
#retrieve the ntile
x = q.iloc[n]
print('-'*30)
print(f"the nth N-tile of the series is: x")
#inverse
print('-'*30)
print(f"x is in the np.searchsorted(q,x)th N-tile of the series")
#and it works also with a value not present in the series
x=x+random.uniform(-.2,.2)
print('-'*30)
print(f"x is in the np.searchsorted(q,x)th N-tile of the series")
输出:
0.0 0.000114
0.1 0.085843
0.2 0.145482
0.3 0.194549
0.4 0.263180
0.5 0.371164
0.6 0.417135
0.7 0.455081
0.8 0.581045
0.9 0.688730
1.0 0.878117
Name: a, dtype: float64
------------------------------
the 5th 10-tile of the series is: 0.37116410063685884
------------------------------
0.37116410063685884 is in the 5th 10-tile of the series
------------------------------
0.27693796519907005 is in the 5th 10-tile of the series
【讨论】:
【参考方案8】:您可以使用 statsmodels 中的 ECDF 函数。 ECDF 代表经验分布函数,“经验的”指的是它创建的函数是基于在您的数据中观察到的事实。
假设你有一个系列s
:
import pandas as pd
s = pd.Series(np.random.uniform(size=1000))
您可以在 0.282 处评估 CDF:
(s <= 0.282).mean()
或者您可以使用 statsmodels 函数创建 ECDF:
from statsmodels.distributions.empirical_distribution import ECDF
ecdf_s = ECDF(s)
ecdf_s
[ecdf_s(k) for k in [0.282, 0.544, 0.775]]
并检查它是否是分位数的倒数:
s.quantile([0.25, 0.50, 0.75])
【讨论】:
以上是关于熊猫系列的分位数函数的倒数是啥?的主要内容,如果未能解决你的问题,请参考以下文章