【问题标题】:A GroupBy with combinations of the categorical variables具有分类变量组合的 GroupBy
【发布时间】:2016-02-28 04:36:02
【问题描述】:

假设我有数据:

pd.DataFrame({'index': ['a','b','c','a','b','c'], 'column': [1,2,3,4,1,2]}).set_index(['index'])

给出:

       column
index
a           1
b           2
c           3
a           4
b           1
c           2

然后要获得每个子组的平均值:

df.groupby(df.index).mean()

       column
index
a         2.5
b         1.5
c         2.5

但是,在不不断循环和切片数据的情况下,我一直在努力实现的目标是如何获得子组对的平均值?

例如,a & b 的平均值是 2?好像他们的价值观是结合在一起的。

输出类似于:

       column
index
a & a     2.5
a & b     2.0
a & c     2.5
b & b     1.5
b & c     2.0
c & c     2.5

最好这将涉及操纵“groupby”中的参数,但事实上,我不得不求助于循环和切片。能够在某个时候构建子组的所有组合。

【问题讨论】:

    标签: python pandas group-by dataframe grouping


    【解决方案1】:

    我目前的实现是:

     import pandas as pd
     import itertools
     import numpy as np
    
        # get all pair of categories here
    def all_pairs(df, ix):
        hash = {
            ix: [],
            'p': []
        }
        for subset in itertools.combinations(np.unique(np.array(df.index)), 2):
            hash[ix].append(subset)
            hash['p'].append(df.loc[pd.IndexSlice[subset], :]).mean)
    
        return pd.DataFrame(hash).set_index(ix)
    

    获取组合,然后将它们添加到具有然后构建备份到数据帧中。不过这很 hacky :(

    【讨论】:

    • 如果没有得到足够的答案,我会留在这里
    【解决方案2】:

    这是一个使用 MultiIndex 和外连接来处理交叉连接的实现。

    import pandas as pd
    from pandas import DataFrame, Series
    import numpy as np
    
    df = pd.DataFrame({'index': ['a','b','c','a','b','c'], 'column': [1,2,3,4,1,2]}).set_index(['index'])
    
    groupedDF = df.groupby(df.index).mean()
    # Create new MultiIndex using from_product which gives a paring of the elements in each iterable
    p = pd.MultiIndex.from_product([groupedDF.index, groupedDF.index])
    # Add column for cross join
    groupedDF[0] = 0
    # Outer Join
    groupedDF = pd.merge(groupedDF, groupedDF, how='outer', on=0).set_index(p)
    # get mean for every row (which is the average for each pair)
    # unstack to get matrix for deduplication
    crossJoinMeans = groupedDF[['column_x', 'column_y']].mean(axis=1).unstack()
    # Create Identity matrix because each pair of itself will be needed
    b = np.identity(3, dtype='bool')
    # set the first column to True because it contains the rest of the unique means (the identity portion covers the first part)
    b[:,0] = True
    # invert for proper use of DataFrame Mask
    b = np.invert(b)
    finalDF = crossJoinMeans.mask(b).stack()
    

    我猜这可以清理并更简洁。

    【讨论】:

      【解决方案3】:

      我在 3 年后重新审视了这个问题,并给出了一个通用的解决方案。

      它正在这个开源库中使用,这就是为什么我现在能够做到这一点 here 并且它适用于任意数量的索引并使用 numpy 矩阵广播在它们上创建组合

      首先,那不是一个有效的数据框。索引不是唯一的。让我们为该对象添加另一个索引并使其成为一个系列:

      df = pd.DataFrame({
          'unique': [1, 2, 3, 4, 5, 6], 
          'index': ['a','b','c','a','b','c'], 
          'column': [1,2,3,4,1,2]
      }).set_index(['unique','index'])
      s = df['column']
      

      让我们解开那个索引:

      >>> idxs = ['index'] # set as variable to be used later on
      >>> unstacked = s.unstack(idxs)
             column
      index       a    b    c
      unique
      1         1.0  NaN  NaN
      2         NaN  2.0  NaN
      3         NaN  NaN  3.0
      4         4.0  NaN  NaN
      5         NaN  1.0  NaN
      6         NaN  NaN  2.0
      >>> vals = unstacked.values
      array([[  1.,  nan,  nan],
             [ nan,   2.,  nan],
             [ nan,  nan,   3.],
             [  4.,  nan,  nan],
             [ nan,   1.,  nan],
             [ nan,  nan,   2.]])
      
      >>> sum = np.nansum(vals, axis=0)
      >>> count = (~np.isnan(vals)).sum(axis=0)
      >>> mean = (sum + sum[:, np.newaxis]) / (count + count[:, np.newaxis])
      array([[ 2.5,  2. ,  2.5],
             [ 2. ,  1.5,  2. ],
             [ 2.5,  2. ,  2.5]])
      

      现在重新创建输出数据框:

      >>> new_df = pd.DataFrame(mean, unstacked.columns, unstacked.columns.copy())
      index_    a    b    c
      index
      a       2.5  2.0  2.5
      b       2.0  1.5  2.0
      c       2.5  2.0  2.5
      >>> idxs_ = [ x+'_' for x in idxs ]
      >>> new_df.columns.names = idxs_
      >>> new_df.stack(idxs_, dropna=False)
      index  index_
      a      a         2.5
             b         2.0
             c         2.5
      b      a         2.0
             b         1.5
             c         2.0
      c      a         2.5
             b         2.0
             c         2.5
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-11-01
        • 2016-12-26
        • 1970-01-01
        • 2020-02-13
        • 2013-09-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多