【问题标题】:How to create a boxplot from data with weights?如何从具有权重的数据创建箱线图?
【发布时间】:2020-01-23 00:02:17
【问题描述】:

我有以下数据:Name 名称出现的次数 (Count),以及每个名称的 Score。我想创建一个Score 的箱须图,通过Count 对每个名称的Score 进行加权。

结果应该与我拥有原始(而非频率)形式的数据相同。但我不想将数据实际转换为这种形式,因为它会很快膨胀。

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

data = {
    "Name":['Sara', 'John', 'Mark', 'Peter', 'Kate'],
    "Count":[20, 10, 5, 2, 5], 
    "Score": [2, 4, 7, 8, 7]
}
df = pd.DataFrame(data)
print(df)
   Count   Name  Score
0     20   Sara      2
1     10   John      4
2      5   Mark      7
3      2  Peter      8
4      5   Kate      7

我不确定如何在 Python 中解决这个问题。任何帮助表示赞赏!

【问题讨论】:

    标签: python pandas dataframe data-visualization


    【解决方案1】:

    这个问题迟到了,但万一它对遇到它的人有用--

    当您的权重是整数时,您可以使用 reindex 按计数进行扩展,然后直接使用 boxplot 调用。我已经能够在几千个变成几十万的数据帧上执行此操作而没有内存挑战,特别是如果实际重新索引的数据帧被包装到第二个函数中,该函数没有将其分配到内存中。

    import pandas as pd
    import seaborn as sns
    
    data = {
        "Name": ['Sara', 'John', 'Mark', 'Peter', 'Kate'],
        "Count": [20, 10, 5, 2, 5],
        "Score": [2, 4, 7, 8, 7]
    }
    df = pd.DataFrame(data)
    
    def reindex_df(df, weight_col):
        """expand the dataframe to prepare for resampling
        result is 1 row per count per sample"""
        df = df.reindex(df.index.repeat(df[weight_col]))
        df.reset_index(drop=True, inplace=True)
        return(df)
    
    df = reindex_df(df, weight_col = 'Count')
    
    sns.boxplot(x='Name', y='Score', data=df)
    

    或者如果您担心内存

    def weighted_boxplot(df, weight_col):
        sns.boxplot(x='Name', 
                    y='Score', 
                    data=reindex_df(df, weight_col = weight_col))
        
    weighted_boxplot(df, 'Count')
    

    【讨论】:

      【解决方案2】:

      这里有两种提问方式。您可能会期待第一个,但是在计算confidence intervals of the median 时它不是一个好的解决方案,它具有使用示例数据的以下代码,参考matplotlib/cbook/__init__.py。因此,Second 比其他任何代码都要好,因为它经过了很好的测试,可以与任何其他自定义代码进行比较。

      def boxplot_stats(X, whis=1.5, bootstrap=None, labels=None,
                        autorange=False):
          def _bootstrap_median(data, N=5000):
              # determine 95% confidence intervals of the median
              M = len(data)
              percentiles = [2.5, 97.5]
      
              bs_index = np.random.randint(M, size=(N, M))
              bsData = data[bs_index]
              estimate = np.median(bsData, axis=1, overwrite_input=True)
      

      第一:

      import pandas as pd
      import matplotlib.pyplot as plt
      import numpy as np
      
      data = {
          "Name": ['Sara', 'John', 'Mark', 'Peter', 'Kate'],
          "Count": [20, 10, 5, 2, 5],
          "Score": [2, 4, 7, 8, 7]
      }
      
      df = pd.DataFrame(data)
      print(df)
      
      
      def boxplot(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)
      
          print([std, variance])
          Q1 = values[np.searchsorted(cumcount, 0.25 * count)]
          Q2 = values[np.searchsorted(cumcount, 0.50 * count)]
          Q3 = values[np.searchsorted(cumcount, 0.75 * count)]
      
          '''
          interquartile range (IQR), also called the midspread or middle 50%, or technically
          H-spread, is a measure of statistical dispersion, being equal to the difference
          between 75th and 25th percentiles, or between upper and lower quartiles,[1][2]
          IQR = Q3 −  Q1. In other words, the IQR is the first quartile subtracted from
          the third quartile; these quartiles can be clearly seen on a box plot on the data.
          It is a trimmed estimator, defined as the 25% trimmed range, and is a commonly used
          robust measure of scale.
          '''
      
          IQR = Q3 - Q1
      
          '''
          The whiskers add 1.5 times the IQR to the 75 percentile (aka Q3) and subtract
          1.5 times the IQR from the 25 percentile (aka Q1).  The whiskers should include
          99.3% of the data if from a normal distribution.  So the 6 foot tall man from
          the example would be inside the whisker but my 6 foot 2 inch girlfriend would
          be at the top whisker or pass it.
          '''
          whishi = Q3 + 1.5 * IQR
          whislo = Q1 - 1.5 * IQR
      
          stats = [{
              'label': 'Scores',  # tick label for the boxplot
              'mean': mean,  # arithmetic mean value
              'iqr': Q3 - Q1,  # 5.0,
      #         'cilo': 2.0,  # lower notch around the median
      #         'cihi': 4.0,  # upper notch around the median
              'whishi': maximum,  # end of the upper whisker
              'whislo': minimum,  # end of the lower whisker
              'fliers': [],  # '\array([], dtype=int64)',  # outliers
              'q1': Q1,  # first quartile (25th percentile)
              'med': Q2,  # 50th percentile
              'q3': Q3  # third quartile (75th percentile)
          }]
      
          fs = 10  # fontsize
          _, axes = plt.subplots(nrows=1, ncols=1, figsize=(6, 6), sharey=True)
          axes.bxp(stats)
          axes.set_title('Default', fontsize=fs)
          plt.show()
      
      
      boxplot(df['Score'], df['Count'])
      
      

      第二:

      import pandas as pd
      import seaborn as sns
      import matplotlib.pyplot as plt
      
      
      data = {
          "Name": ['Sara', 'John', 'Mark', 'Peter', 'Kate'],
          "Count": [20, 10, 5, 2, 5],
          "Score": [2, 4, 7, 8, 7]
      }
      
      df = pd.DataFrame(data)
      print(df)
      
      labels = ['Scores']
      
      data = df['Score'].repeat(df['Count']).tolist()
      
      # compute the boxplot stats
      stats = cbook.boxplot_stats(data, labels=labels, bootstrap=10000)
      
      print(['stats :', stats])
      
      fs = 10  # fontsize
      
      fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(6, 6), sharey=True)
      axes.bxp(stats)
      axes.set_title('Boxplot', fontsize=fs)
      
      plt.show()
      

      参考资料:

      【讨论】:

      • 有趣。我实际上是使用权重的新手。这基本上就是传递一组权重的作用吗?
      • @thewhitetie 在sns.boxplot(... 后面添加了print(df),以帮助您了解数据框。
      • @thewhitetie DataFrame 将数据保存在 python 字典中。如果你查看pandas/core/frame.py中的源代码class DataFrame(NDFrame):,你会得到它。
      • 编辑:我的意思是我想要一个箱线图,而不是在名称级别,而是在 AGGREGATE 级别 - 显示平均值、中位数、Q25 等的箱线图。换句话说,我想总结一下整个数据。这表明了不同的事情。
      • 例如,这将获得所需的均值。仍然不确定如何从中创建箱线图:desired_mean = sum((df['Count'] * df['Score'])) / sum(df['Count'])
      猜你喜欢
      • 2021-05-09
      • 1970-01-01
      • 1970-01-01
      • 2021-08-03
      • 1970-01-01
      • 1970-01-01
      • 2020-05-13
      • 1970-01-01
      • 2021-08-12
      相关资源
      最近更新 更多