【问题标题】:Count of all possible combinations between dataframe columns数据框列之间所有可能组合的计数
【发布时间】:2019-11-18 09:49:21
【问题描述】:

我正在尝试获取数据框八列之间每个可能组合的计数(其中所有行值为 1)。基本上我需要了解存在多少次不同的重叠。

我尝试使用itertools.product 来获取所有组合,但它似乎不起作用。

import pandas as pd
import numpy as np
import itertools

df = pd.read_excel('filename.xlsx')

df.head(15)

    a   b   c   d   e   f   g   h
0   1   0   0   0   0   1   0   0
1   1   0   0   0   0   0   0   0
2   1   0   1   1   1   1   1   1
3   1   0   1   1   0   1   1   1
4   1   0   0   0   0   0   0   0
5   0   1   0   0   1   1   1   1
6   1   1   0   0   1   1   1   1
7   1   1   1   1   1   1   1   1
8   1   1   0   0   1   1   0   0
9   1   1   1   0   1   0   1   0
10  1   1   1   0   1   1   0   0
11  1   0   0   0   0   1   0   0
12  1   1   1   1   1   1   1   1
13  1   1   1   1   1   1   1   1
14  0   1   1   1   1   1   1   0


print(list(itertools.product(new_df.columns)))

预期的输出将是一个数据帧,其中包含每个有效组合的行数 (n)(其中行中的值全为 1)。

例如:

    a   b
0   1   0   
1   1   0   
2   1   0   
3   1   0   
4   1   0   
5   0   1   
6   1   1   
7   1   1   
8   1   1   
9   1   1   
10  1   1   
11  1   0   
12  1   1   
13  1   1   
14  0   1

愿意

combination   count

a              12
a_b             7
b               9

请注意,输出需要包含ah 之间可能的所有组合,而不仅仅是成对的

【问题讨论】:

    标签: python python-3.x pandas numpy itertools


    【解决方案1】:

    因为你恰好有 8 列,np.packbits 连同 np.bincount这里比较方便:

    import numpy as np
    import pandas as pd
    
    # make large example
    ncol, nrow = 8, 1_000_000
    df = pd.DataFrame(np.random.randint(0,2,(nrow,ncol)), columns=list("abcdefgh"))
    
    from time import time
    T = [time()]
    # encode as binary numbers and count
    counts = np.bincount(np.packbits(df.values.astype(np.uint8)),None,256)
    
    # find sets in other sets
    rng = np.arange(256, dtype=np.uint8)
    contained = (rng & rng[:, None]) == rng[:, None]
    
    # and sum
    ccounts = (counts * contained).sum(1)
    
    # if there are empty bins, remove them
    nz = np.where(ccounts)[0].astype(np.uint8)
    
    # helper to build bin labels 
    a2h = np.array(list("abcdefgh"))
    
    # put labels to counts
    result = pd.Series(ccounts[nz], index = ["_".join((*a2h[np.unpackbits(i).view(bool)],)) for i in nz])
    
    
    
    from itertools import chain, combinations
    
    def powerset(iterable):
        "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
        s = list(iterable)
        return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
    
    T.append(time())
    s = pd.Series({
        '_'.join(c): df[c].min(axis=1).sum() 
        for c in map(list, filter(None, powerset(df)))
    })
    T.append(time())
    
    print("packbits {:.3f} powerset {:.3f}".format(*np.diff(T)))
    print("results equal", (result.sort_index()[1:]==s.sort_index()).all())
    

    这给出了与 powerset 方法相同的结果,但实际上快了 1000 倍:

    packbits 0.016 powerset 21.974
    results equal True
    

    【讨论】:

      【解决方案2】:

      你只需要同现矩阵:

      我们先构造一个例子:

      import numpy as np
      import pandas as pd
      
      mat = np.zeros((5,5))
      mat[0,0] = 1
      mat[0,1] = 1
      mat[1,0] = 1
      mat[2,1] = 1
      mat[3,3] = 1
      mat[3,4] = 1
      mat[2,4] = 1
      cols = ['a','b','c','d','e']
      df = pd.DataFrame(mat,columns=cols)
      print(df)
      
           a    b    c    d    e
      0  1.0  1.0  0.0  0.0  0.0
      1  1.0  0.0  0.0  0.0  0.0
      2  0.0  1.0  0.0  0.0  1.0
      3  0.0  0.0  0.0  1.0  1.0
      4  0.0  0.0  0.0  0.0  0.0
      
      

      现在我们构造同现矩阵:

      # construct the cooccurence matrix:
      co_df = df.T.dot(df)
      print(co_df)
      
           a    b    c    d    e
      a  2.0  1.0  0.0  0.0  0.0
      b  1.0  2.0  0.0  0.0  1.0
      c  0.0  0.0  0.0  0.0  0.0
      d  0.0  0.0  0.0  1.0  1.0
      e  0.0  1.0  0.0  1.0  2.0
      

      终于得到了你需要的结果:

      result = {}
      
      for c1 in cols:
          for c2 in cols:
              if c1 == c2:
                  if c1 not in result:
                      result[c1] = co_df[c1][c2]
              else:
      
                  if '_'.join([c1,c2]) not in result:
                      result['_'.join([c1,c2])] = co_df[c1][c2]
      
      
      print(result)
      
      
      
      {'a': 2.0, 'a_b': 1.0, 'a_c': 0.0, 'a_d': 0.0, 'a_e': 0.0, 'b_a': 1.0, 'b': 2.0, 'b_c': 0.0, 'b_d': 0.0, 'b_e': 1.0, 'c_a': 0.0, 'c_b': 0.0, 'c': 0.0, 'c_d': 0.0, 'c_e': 0.0, 'd_a': 0.0, 'd_b': 0.0, 'd_c': 0.0, 'd': 1.0, 'd_e': 1.0, 'e_a': 0.0, 'e_b': 1.0, 'e_c': 0.0, 'e_d': 1.0, 'e': 2.0}
      

      【讨论】:

        【解决方案3】:

        Powerset 组合

        使用powerset 配方,

        s = pd.Series({
            '_'.join(c): df[c].min(axis=1).sum() 
            for c in map(list, filter(None, powerset(df)))
        })
        
        a                  13
        b                   9
        c                   8
        d                   6
        e                  10
        f                  12
        g                   9
        h                   7
        a_b                 7
        ...
        

        成对组合

        这是一种特殊情况,可以向量化。

        from itertools import combinations
        
        u = df.T.dot(df)   
        pd.DataFrame({
            'combination': [*map('_'.join, combinations(df, 2))], 
            # pandas < 0.24
            # 'count': u.values[np.triu_indices_from(u, k=1)]
            # pandas >= 0.24
            'count': u.to_numpy()[np.triu_indices_from(u, k=1)]
        })
        

        可以使用dot,然后提取上三角矩阵值:

          combination  count
        0         a_b      7
        1         a_c      7
        2         a_d      5
        3         a_e      8
        4         a_f     10
        5         a_g      7
        6         a_h      6
        7         b_c      6
        8         b_d      4
        9         b_e      9
        

        【讨论】:

        • 谢谢 - 虽然我想要所有组合,而不仅仅是成对组合,例如 a_b_c_d_e_f_g_h 或 a_c_e_g
        • 您可以为此增加组合数。目前,即 2,您可以在 combinations(df, 2) 中看到,如果您想要从 a 到 h,请将其更改为 8。顺便说一句,答案很好!
        • @amanb 谢谢,但这只能解决获取索引的问题,而不是相应的计数。后者是一个更难的问题,OP 的帖子中没有明确说明,他们的预期输出(嗯,他们的初始修订版)也具有误导性(它只显示 a_b 的预期输出,导致人们相信他们只想要成对计数) .
        • @Dan 检查编辑,以后请更清楚您的要求。
        • oOo 没想到使用.min。不错的一个
        【解决方案4】:

        如果你只有 1 和 0 的值,你可以这样做:

        df= pd.DataFrame({
        'a': [1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1],
        'b': [1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0],
        'c': [1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1],
        'd': [1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1],
        })
        
        (df.a * df.b).sum()
        

        结果为 4。

        要获得所有组合,您可以使用来自itertoolscombinations

        from itertools import combinations
        
        analyze=[(col,) for col in df.columns]
        analyze.extend(combinations(df.columns, 2))
        for cols in analyze:
            num_ser= None
            for col in cols:
                if num_ser is None:
                    num_ser= df[col]
                else:
                    num_ser*= df[col]
            num= num_ser.sum()
            print(f'{cols} contains {num}')
        

        这会导致:

        ('a',) contains 4
        ('b',) contains 7
        ('c',) contains 11
        ('d',) contains 23
        ('a', 'b') contains 4
        ('a', 'c') contains 4
        ('a', 'd') contains 4
        ('b', 'c') contains 7
        ('b', 'd') contains 7
        ('c', 'd') contains 11
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-06-06
          • 1970-01-01
          • 2017-09-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多