如何计算第一和第三四分位数?

91

我有一个DataFrame:

    time_diff   avg_trips
0   0.450000    1.0
1   0.483333    1.0
2   0.500000    1.0
3   0.516667    1.0
4   0.533333    2.0

我想获取列time_diff的第一四分位数、第三四分位数和中位数。要获取中位数,我使用np.median(df["time_diff"].values)

我该如何计算四分位数?


1
希望以下链接能够帮到您:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.quantile.html - BENY
1
你也在寻找IQR吗?你应该使用pd.qcut - cs95
什么是df?我遇到了这种错误:AttributeError: 'Series' object has no attribute 'time_diff' - Charlie Parker
15个回答

99

通过使用pandas

df.time_diff.quantile([0.25,0.5,0.75])


Out[793]: 
0.25    0.483333
0.50    0.500000
0.75    0.516667
Name: time_diff, dtype: float64

9
请记住,有15种不同的方法来计算四分位数。因此,在查看不同的函数时要注意细节,因为它们可能会给出稍微不同的结果(如pandas与numpy与scipy)。http://jse.amstat.org/v14n3/langford.html - ahmedhosny
1
是的,我曾经使用过 df.quantile(q=[0.25, 0.75], axis=0, numeric_only=True, interpolation='midpoint') -- 这会为数据框计算Q1和Q3(每个序列分别)。 - RAM237
@ahmedhosny 谢谢分享这篇论文。我已经弄清楚它如何映射到Python上了。对于pandas插值numpy方法,需要指定百分位数。Scipy统计只有IQR函数。 - Simone
什么是df?导入语句?这是什么? - Charlie Parker
错误:AttributeError: 'DataFrame'对象没有'time_diff'属性 - Charlie Parker
@CharlieParker 数据显示在问题中 - BENY

89
您可以使用np.percentile来计算四分位数(包括中位数):
>>> np.percentile(df.time_diff, 25)  # Q1
0.48333300000000001

>>> np.percentile(df.time_diff, 50)  # median
0.5

>>> np.percentile(df.time_diff, 75)  # Q3
0.51666699999999999

或者一次性完成:

>>> np.percentile(df.time_diff, [25, 50, 75])
array([ 0.483333,  0.5     ,  0.516667])

1
或者 np.nanpercentile,如果您的数据集中有 NaN。 - blupp

28

恰巧,这个信息可以使用describe方法进行捕获:

df.time_diff.describe()

count    5.000000
mean     0.496667
std      0.032059
min      0.450000
25%      0.483333
50%      0.500000
75%      0.516667
max      0.533333
Name: time_diff, dtype: float64

如何将25%和平均值分别命名为变量my_meanmy_firstquartile - 3kstc
1
my_mean = df.time_diff.describe()[1] my_firstquartile = df.time_diff.describe()[4] - MSalty
什么是df?我收到了“错误:'DataFrame'对象没有'time_diff'属性”的错误信息。 - Charlie Parker

27

np.percentile 不计算 Q1、中位数和Q3的值。考虑以下排序后的列表:

samples = [1, 1, 8, 12, 13, 13, 14, 16, 19, 22, 27, 28, 31]

运行 np.percentile(samples, [25, 50, 75]) 返回列表中的实际值:

Out[1]: array([12., 14., 22.])

然而,四分位数为Q1=10.0,中位数=14,Q3=24.5(您也可以使用此链接在线查找四分位数和中位数)。 可以使用以下代码计算排序列表的四分位数和中位数(由于排序,此方法需要O(nlogn)计算,其中n是项数)。 此外,使用中位数选择算法顺序统计)可以进行O(n)计算来查找四分位数和中位数。

samples = sorted([28, 12, 8, 27, 16, 31, 14, 13, 19, 1, 1, 22, 13])

def find_median(sorted_list):
    indices = []

    list_size = len(sorted_list)
    median = 0

    if list_size % 2 == 0:
        indices.append(int(list_size / 2) - 1)  # -1 because index starts from 0
        indices.append(int(list_size / 2))

        median = (sorted_list[indices[0]] + sorted_list[indices[1]]) / 2
        pass
    else:
        indices.append(int(list_size / 2))

        median = sorted_list[indices[0]]
        pass

    return median, indices
    pass

median, median_indices = find_median(samples)
Q1, Q1_indices = find_median(samples[:median_indices[0]])
Q3, Q3_indices = find_median(samples[median_indices[-1] + 1:])

quartiles = [Q1, median, Q3]

print("(Q1, median, Q3): {}".format(quartiles))

1
这是正确的答案。我至少花了一个小时试图理解为什么 describe 没有输出精确的四分位值,直到我想到第25个百分位数并不等于Q1。干得好! - Wladston Ferreira Filho
@Wladston 如果样本中存在重复的值,将会导致不同的结果。 - BENY
没有一个百分位数的单一定义,因此几乎每个人都得到了正确的答案。 - PatrickT
1
np.percentile 做的就是它该做的... 如果你习惯于使用中点的“定义”... 只需检查 numpy 文档的链接以查看正确的签名即可。 - cards

15

基于或者说是在Babak所说的基础上做出一点修正....

np.percentile 非常明确地 计算Q1,中位数和Q3的值。请考虑以下排序后的列表:

s1=[18,45,66,70,76,83,88,90,90,95,95,98]

运行np.percentile(s1, [25, 50, 75])会返回列表中的实际值:

[69.  85.5  91.25]

然而,四分位数为Q1=68.0、中位数=85.5、Q3=92.5,这是需要说的正确事情。

我们在这里缺少的是np.percentile和相关函数的插值参数。默认情况下,此参数的值为线性。这个可选参数指定了当所需分位数位于两个数据点i<j之间时要使用的插值方法:
线性:i + (j - i) * fraction,其中fraction是被i和j包围的索引的小数部分。
lower:i。
higher:j。
nearest:最近的i或j。
midpoint:(i + j)/2。

因此,使用np.percentile(s1, [25, 50, 75], interpolation='midpoint')运行将返回列表的实际结果:

[68. 85.5 92.5]

3
这不适用于Cyrus使用的相同数值列表,'midpoint'对于他的列表而言与'linear'结果相同。你的解决方案有效,因为你有偶数个数值。Cyrus有奇数个数值,如果再添加一个附加值,这仍然会给出您预期的结果吗? - dshanahan
numpy >= 1.22 开始,interpolation 已被弃用,请使用 method 替代。详情请参见我的回答。 - cards

7

q25和q75分别是前半部分和后半部分的中位数,如果我想要前半部分和后半部分的平均值呢? - user7739833

5
如果你想使用原始的Python而不是NumPy或Pandas,你可以使用Python的stats模块来找到列表上半部分和下半部分的中位数。
    >>> import statistics as stat
    >>> def quartile(data):
            data.sort()               
            half_list = int(len(data)//2)
            upper_quartile = stat.median(data[-half_list:])
            lower_quartile = stat.median(data[:half_list])
            print("Lower Quartile: "+str(lower_quartile))
            print("Upper Quartile: "+str(upper_quartile))
            print("Interquartile Range: "+str(upper_quartile-lower_quartile)
    
    >>> quartile(df.time_diff)

第一行:导入statistics模块并将其别名为"stat"
第二行:定义四分位数函数
第三行:将数据按升序排序
第四行:获取列表长度的一半
第五行:获取列表下半部分的中位数
第六行:获取列表上半部分的中位数
第七行:打印下四分位数
第八行:打印上四分位数
第九行:打印四分位距
第十行:对DataFrame的time_diff列运行四分位数函数

感谢您发布了一个没有使用外部库的解决方案。只是想提醒一下,您有一个拼写错误 upper_quartile = stat.median(data[-half_list:])(需要冒号来定义切片)。 - zvxr
感谢您发布了一个没有使用外部库的解决方案。只是想提醒一下,您有一个拼写错误 upper_quartile = stat.median(data[-half_list:])(需要冒号来定义切片)。 - undefined

4

您可以使用

df.describe()

这将显示信息。

df.describe()


2

在学习统计学和面向对象编程的过程中,我做了这个东西,也许你会觉得它有用:

最初的回答:

samplesCourse = [9, 10, 10, 11, 13, 15, 16, 19, 19, 21, 23, 28, 30, 33, 34, 36, 44, 45, 47, 60]

class sampleSet:
    def __init__(self, sampleList):
        self.sampleList = sampleList
        self.interList = list(sampleList) # interList is sampleList alias; alias used to maintain integrity of original sampleList

    def find_median(self):
        self.median = 0

        if len(self.sampleList) % 2 == 0:
            # find median for even-numbered sample list length
            self.medL = self.interList[int(len(self.interList)/2)-1]
            self.medU = self.interList[int(len(self.interList)/2)]
            self.median = (self.medL + self.medU)/2

        else:
            # find median for odd-numbered sample list length
            self.median = self.interList[int((len(self.interList)-1)/2)]
        return self.median

    def find_1stQuartile(self, median):
        self.lower50List = []
        self.Q1 = 0

        # break out lower 50 percentile from sampleList
        if len(self.interList) % 2 == 0:
            self.lower50List = self.interList[:int(len(self.interList)/2)]
        else:
            # drop median to make list ready to divide into 50 percentiles
            self.interList.pop(interList.index(self.median))
            self.lower50List = self.interList[:int(len(self.interList)/2)]

        # find 1st quartile (median of lower 50 percentiles)
        if len(self.lower50List) % 2 == 0:
            self.Q1L = self.lower50List[int(len(self.lower50List)/2)-1]
            self.Q1U = self.lower50List[int(len(self.lower50List)/2)]
            self.Q1 = (self.Q1L + self.Q1U)/2

        else:
            self.Q1 = self.lower50List[int((len(self.lower50List)-1)/2)]

        return self.Q1

    def find_3rdQuartile(self, median):
        self.upper50List = []
        self.Q3 = 0

        # break out upper 50 percentile from sampleList
        if len(self.sampleList) % 2 == 0:
            self.upper50List = self.interList[int(len(self.interList)/2):]
        else:
            self.interList.pop(interList.index(self.median))
            self.upper50List = self.interList[int(len(self.interList)/2):]

        # find 3rd quartile (median of upper 50 percentiles)
        if len(self.upper50List) % 2 == 0:
            self.Q3L = self.upper50List[int(len(self.upper50List)/2)-1]
            self.Q3U = self.upper50List[int(len(self.upper50List)/2)]
            self.Q3 = (self.Q3L + self.Q3U)/2

        else:
            self.Q3 = self.upper50List[int((len(self.upper50List)-1)/2)]

        return self.Q3

    def find_InterQuartileRange(self, Q1, Q3):
        self.IQR = self.Q3 - self.Q1
        return self.IQR

    def find_UpperFence(self, Q3, IQR):
        self.fence = self.Q3 + 1.5 * self.IQR
        return self.fence

samples = sampleSet(samplesCourse)
median = samples.find_median()
firstQ = samples.find_1stQuartile(median)
thirdQ = samples.find_3rdQuartile(median)
iqr = samples.find_InterQuartileRange(firstQ, thirdQ)
fence = samples.find_UpperFence(thirdQ, iqr)

print("Median is: ", median)
print("1st quartile is: ", firstQ)
print("3rd quartile is: ", thirdQ)
print("IQR is: ", iqr)
print("Upper fence is: ", fence)

1

使用Python统计模块可以轻松完成此操作。 https://docs.python.org/3/library/statistics.html

import statistics

time_diff = [0.45,0.483333,0.5,0.516667,0.5333333]
statistics.quantiles(time_diff, method='inclusive')

[0.483333, 0.5, 0.516667]

以上默认为4组数据(n=4),有3个分割点(第一四分位数、中位数、第三四分位数),并将方法设置为包含,使用列表中的所有数据。 输出是第一四分位数、中位数和第三四分位数的列表。


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