【问题标题】:Pandas : balancing data熊猫:平衡数据
【发布时间】:2021-12-14 02:38:06
【问题描述】:

注意:这个问题和这里的答案不一样:“Pandas: sample each group after groupby”

试图弄清楚如何使用pandas.DataFrame.sample 或任何其他函数来平衡这些数据:

df[class].value_counts()

c1    9170
c2    5266
c3    4523
c4    2193
c5    1956
c6    1896
c7    1580
c8    1407
c9    1324

我需要获取每个类 (c1, c2, .. c9) 的随机样本,其中样本大小等于具有最少实例数的类的大小。在此示例中,样本大小应为 c9 = 1324 类的大小。

有什么简单的方法可以用 Pandas 做到这一点?

更新

为了澄清我的问题,在上表中:

c1    9170
c2    5266
c3    4523
...

数字是 c1,c2,c3,... 类实例的计数,因此实际数据如下所示:

c1 'foo'
c2 'bar'
c1 'foo-2'
c1 'foo-145'
c1 'xxx-07'
c2 'zzz'
...

等等

更新 2

澄清更多:

d = {'class':['c1','c2','c1','c1','c2','c1','c1','c2','c3','c3'],
     'val': [1,2,1,1,2,1,1,2,3,3]
    }

df = pd.DataFrame(d)

    class   val
0   c1  1
1   c2  2
2   c1  1
3   c1  1
4   c2  2
5   c1  1
6   c1  1
7   c2  2
8   c3  3
9   c3  3

df['class'].value_counts()

c1    5
c2    3
c3    2
Name: class, dtype: int64

g = df.groupby('class')
g.apply(lambda x: x.sample(g.size().min()))

        class   val
class           
c1  6   c1  1
    5   c1  1
c2  4   c2  2  
    1   c2  2
c3  9   c3  3
    8   c3  3

看起来这行得通。主要问题:

g.apply(lambda x: x.sample(g.size().min())) 是如何工作的?我知道“lambda”是什么,但是:

  • 在这种情况下,x 中的lambda 传递了什么?
  • g.size() 中的g 是什么?
  • 为什么输出包含 6、5、4、1、8、9 个数字?他们做什么 什么意思?

【问题讨论】:

    标签: python pandas


    【解决方案1】:
    g = df.groupby('class')
    g.apply(lambda x: x.sample(g.size().min()).reset_index(drop=True))
    
      class  val
    0    c1    1
    1    c1    1
    2    c2    2
    3    c2    2
    4    c3    3
    5    c3    3
    

    回答您的后续问题

    1. lambda 中的x 最终成为一个数据帧,它是组所代表的df 的子集。这些数据帧中的每一个,每个组一个,都通过这个lambda 传递。
    2. ggroupby 对象。我将它放在一个命名变量中,因为我计划使用它两次。 df.groupby('class').size() 是做df['class'].value_counts() 的另一种方法,但因为无论如何我要去groupby,我不妨重复使用相同的groupby,使用size 来获取值计数......节省时间。
    3. 这些数字是来自df 的与采样相关的索引值。我添加了reset_index(drop=True) 以摆脱它。

    【讨论】:

    • 应该reset_index lambda 之外发布结果(即apply(...).reset_index(...)?我得到一个MultiIndex 与发布的代码。
    【解决方案2】:

    上面的答案是正确的,但我想指出上面的 g 不是用户最可能想要的 Pandas DataFrame 对象。它是一个 pandas.core.groupby.groupby.DataFrameGroupBy 对象。 Pandas apply 不会就地修改数据框,而是返回一个数据框。要看到这一点,请尝试在 g 上调用head,结果将如下所示。

    import pandas as pd
    d = {'class':['c1','c2','c1','c1','c2','c1','c1','c2','c3','c3'],
         'val': [1,2,1,1,2,1,1,2,3,3]
        }
    
    d = pd.DataFrame(d)
    g = d.groupby('class')
    g.apply(lambda x: x.sample(g.size().min()).reset_index(drop=True))
    g.head()
    >>> class val
    0    c1    1
    1    c2    2
    2    c1    1
    3    c1    1
    4    c2    2
    5    c1    1
    6    c1    1
    7    c2    2
    8    c3    3
    9    c3    3
    

    要解决此问题,您可以创建一个新变量 org 分配给 apply 的结果,如下所示,以便您获得 Pandas DataFrame

    g = d.groupby('class')
    g = pd.DataFrame(g.apply(lambda x: x.sample(g.size().min()).reset_index(drop=True)))
    

    现在调用头部会产生:

    g.head()
    
    >>>class val
    0   c1   1
    1   c2   2
    2   c1   1
    3   c1   1
    4   c2   2
    

    这很可能是用户想要的。

    【讨论】:

    • g.apply 的结果是DataFrame,不需要这个转换。自己检查:type(g.apply(lambda x: x.sample(g.size().min()).reset_index(drop=True)))。您在示例中看到此行为的原因是因为 apply 没有就地修改对象 -- 它返回 DataFrame
    • 这是错误的,正如@ntjess 指出的那样,不需要转换。不要传播错误信息。令人担忧的是,这个答案被赞成了。 g.apply(...)g 的结果是两个不同的对象。第一个是DataFrame,它是聚合g 所指的DataFrameGroupBy 对象的结果。
    • 如果类的数量很大,这也有可能在计算上变得不必要地复杂。建议存储最小值并在采样期间重复使用该值,即min_num_samps = g.size().min(); d = g.apply(lambda x: x.sample(min_num_samps).reset_index(drop=True))
    【解决方案3】:

    该方法随机获取每个类的k个元素。

    def sampling_k_elements(group, k=3):
        if len(group) < k:
            return group
        return group.sample(k)
    
    balanced = df.groupby('class').apply(sampling_k_elements).reset_index(drop=True)
    

    【讨论】:

      【解决方案4】:

      “以下代码适用于不平衡类的欠采样,但对此非常抱歉。试试吧!它也适用于上采样问题!祝你好运!”

      导入所需的采样库

      from sklearn.utils import resample
      

      定义多数和少数类

       df_minority9 = df[df['class']=='c9']
          df_majority1 = df[df['class']=='c1']
          df_majority2 = df[df['class']=='c2']
          df_majority3 = df[df['class']=='c3']
          df_majority4 = df[df['class']=='c4']
          df_majority5 = df[df['class']=='c5']
          df_majority6 = df[df['class']=='c6']
          df_majority7 = df[df['class']=='c7']
          df_majority8 = df[df['class']=='c8']
      

      欠采样多数类

       maj_class1 = resample(df_majority1, 
                                       replace=True,     
                                       n_samples=1324,    
                                       random_state=123) 
          maj_class2 = resample(df_majority2, 
                                       replace=True,     
                                       n_samples=1324,    
                                       random_state=123) 
          maj_class3 = resample(df_majority3, 
                                       replace=True,     
                                       n_samples=1324,    
                                       random_state=123) 
          maj_class4 = resample(df_majority4, 
                                       replace=True,     
                                       n_samples=1324,    
                                       random_state=123) 
          maj_class5 = resample(df_majority5, 
                                       replace=True,     
                                       n_samples=1324,    
                                       random_state=123) 
          maj_class6 = resample(df_majority6, 
                                       replace=True,     
                                       n_samples=1324,    
                                       random_state=123) 
          maj_class7 = resample(df_majority7, 
                                       replace=True,     
                                       n_samples=1324,    
                                       random_state=123) 
          maj_class8 = resample(df_majority8, 
                                       replace=True,     
                                       n_samples=1324,    
                                       random_state=123) 
      

      将少数类与欠采样的多数类结合

      df=pd.concat([df_minority9,maj_class1,maj_class2,maj_class3,maj_class4, maj_class5,dmaj_class6,maj_class7,maj_class8])
      

      显示新的平衡类计数

       df['class'].value_counts()
      

      【讨论】:

      • 这可以通过 for 循环或 lambda 函数自动完成。
      • 这个答案写得太糟糕了。我很想投反对票。但是,如果我投反对票,我将失去一分。但是,这写得真的很糟糕(特别是考虑到有人已经评论过使用循环)
      【解决方案5】:

      我知道这个问题很老,但我偶然发现了它,并且对这里和其他线程中的解决方案并不满意。我使用对我有用的列表理解做了一个快速的解决方案。也许它对其他人有用:

      df_for_training_grouped = df_for_training.groupby("sentiment")
      df_for_training_grouped.groups.values()
      frames_of_groups = [x.sample(df_for_training_grouped.size().min()) for y, x in df_for_training_grouped]
      new_df = pd.concat(frames_of_groups)
      

      结果是一个数据框,其中每个组包含相同数量的条目。条目数量设置为最小组的大小。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-23
        • 2021-12-12
        • 2021-06-13
        • 1970-01-01
        • 2018-05-15
        相关资源
        最近更新 更多