【问题标题】:Descriptive stats from frequency table in pandas来自 Pandas 频率表的描述性统计
【发布时间】:2016-09-18 14:52:49
【问题描述】:

我有一个考试成绩频率表:

score    count
-----    -----
  77      1105
  78       940
  79      1222
  80      4339
etc

我想显示频率表汇总的样本的基本统计数据和箱线图。 (比如上面例子的平均值是79.16,中位数是80。)

有没有办法在 Pandas 中做到这一点?我见过的所有例子都假设一个单独的案例表。

我想我可以生成一个个人分数列表,像这样 --

In [2]: s = pd.Series([77] * 1105 + [78] * 940 + [79] * 1222 + [80] * 4339)
In [3]: s.describe()
Out[3]: 
count    7606.000000
mean       79.156324
std         1.118439
min        77.000000
25%        78.000000
50%        80.000000
75%        80.000000
max        80.000000
dtype: float64

-- 但我希望避免这种情况;真实非玩具数据集中的总频率高达数十亿。

任何帮助表示赞赏。

(我认为这是与Using describe() with weighted data 不同的问题,它是关于将权重应用于个别案例。)

【问题讨论】:

  • 我认为它与我链接到的问题相同:您想要score 列的加权描述性统计数据,权重由count 列给出。唉,我不认为这个问题有一个令人满意的答案。
  • 我同意他们问的问题非常相似,但我不知道 SAS proc 是如何工作的,所以我将在此处发布我的答案,因为它可能无法满足这些要求。

标签: python pandas


【解决方案1】:

这是一个计算频率分布的描述性统计的小函数:

# from __future__ import division (for Python 2)
def descriptives_from_agg(values, freqs):
    values = np.array(values)
    freqs = np.array(freqs)
    arg_sorted = np.argsort(values)
    values = values[arg_sorted]
    freqs = freqs[arg_sorted]
    count = freqs.sum()
    fx = values * freqs
    mean = fx.sum() / count
    variance = ((freqs * values**2).sum() / count) - mean**2
    variance = count / (count - 1) * variance  # dof correction for sample variance
    std = np.sqrt(variance)
    minimum = np.min(values)
    maximum = np.max(values)
    cumcount = np.cumsum(freqs)
    Q1 = values[np.searchsorted(cumcount, 0.25*count)]
    Q2 = values[np.searchsorted(cumcount, 0.50*count)]
    Q3 = values[np.searchsorted(cumcount, 0.75*count)]
    idx = ['count', 'mean', 'std', 'min', '25%', '50%', '75%', 'max']
    result = pd.Series([count, mean, std, minimum, Q1, Q2, Q3, maximum], index=idx)
    return result

一个演示:

np.random.seed(0)

val = np.random.normal(100, 5, 1000).astype(int)

pd.Series(val).describe()
Out: 
count    1000.000000
mean       99.274000
std         4.945845
min        84.000000
25%        96.000000
50%        99.000000
75%       103.000000
max       113.000000
dtype: float64

vc = pd.value_counts(val)
descriptives_from_agg(vc.index, vc.values)

Out: 
count    1000.000000
mean       99.274000
std         4.945845
min        84.000000
25%        96.000000
50%        99.000000
75%       103.000000
max       113.000000
dtype: float64

请注意,这不处理 NaN 并且未正确测试。

【讨论】:

  • 谢谢!您的快速响应又为我节省了几个小时尝试找到内置方法的时间。
【解决方案2】:

在我最初的问题中,我说我不想从频率表中重建原始值,但只要它适合内存,我现在认为我会走这条路,特别是因为我的实际用例涉及更多列。

如果有人感兴趣,这是我将频率表转换为案例的函数。

In [5]: def freqs2cases(df, freq_col, cases_cols):
   ...:     def itcases():
   ...:         for i, row in df.iterrows():
   ...:             for j in range(int(row[freq_col])):
   ...:                 yield row[cases_cols]
   ...:     return pd.DataFrame(itcases())
   ...: 

In [8]: freq_df
Out[8]: 
  course  score  freq
0   math     75     3
1   math     81     4
2   chem     92     2
3   chem     66     3

In [9]: freqs2cases(freq_df, 'freq', ['course', 'score'])
Out[9]: 
  course  score
0   math     75
0   math     75
0   math     75
1   math     81
1   math     81
1   math     81
1   math     81
2   chem     92
2   chem     92
3   chem     66
3   chem     66
3   chem     66

【讨论】:

    【解决方案3】:

    你可以这样做:

    1. 使用 groupby,您可以划分“分数”列。
    2. 您可以添加 [['score'] 次计数]
    3. sum(add) 是列表的列表。所以使用 itertools.chain,你可以把它变成扁平列表。
    4. 使用 pd.Series(),你可以使用 .describe()
        import itertools
        sum_add = []
        for idx,grp in df.groupby('score'):
            sum_add.append((list(grp['score']) * grp['count'].iloc[0]) )
        pd.Series(list(itertools.chain.from_iterable(sum_add))).describe()
    
    

    【讨论】:

    • 嗨,欢迎来到 Stack Overflow。在回答已经有很多答案的问题时,请务必添加一些额外的见解,说明为什么您提供的回复是实质性的,而不是简单地呼应原始发帖人已经审查过的内容。这在您提供的“纯代码”答案中尤其重要。
    • 感谢您的建议。我英语不流利。但我会努力的。
    猜你喜欢
    • 1970-01-01
    • 2021-12-21
    • 2019-05-03
    • 1970-01-01
    • 1970-01-01
    • 2019-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多