【问题标题】:Populating a “count matrix” with combinations of pandas DataFrame rows使用 pandas DataFrame 行的组合填充“计数矩阵”
【发布时间】:2018-08-12 18:20:27
【问题描述】:

假设我在 Python3.x 中有以下 pandas DataFrame

import pandas as pd

dict1 = {'name':['dog', 'dog', 'cat', 'cat', 'cat', 'bird', 'bird', 'bird', 'bird'], 'number':[42, 42, 42, 42, 42, 42, 42, 42, 42], 'count':[1, 2, 4, 5, 7, 1, 2, 5, 8]} 
df = pd.DataFrame(dict1)

print(df)
##    name  number  count
## 0   dog      42      1
## 1   dog      42      2
## 2   cat      42      4
## 3   cat      42      5
## 4   cat      42      7
## 5  bird      42      1
## 6  bird      42      2
## 7  bird      42      5
## 8  bird      42      8

counts 列包含从 1 到 8 的整数。我的目标是在给定列 name 中的唯一类别的情况下,使用每个组合“对”的计数填充一个 8 x 8 零矩阵。

因此,dogcatbird 的组合对是:

dog: (1, 2)
cat: (4, 5), (4, 7), (5, 7)
bird: (1, 2), (1, 5), (1, 8), (2, 5), (2, 8), (5, 8)

对于每一对,我将+1 添加到零矩阵的相应条目中。

这个矩阵是对称的,即(n, m) = (m, n)。给定df 的矩阵将是:

   1 2 3 4 5 6 7 8
1: 0 2 0 0 1 0 0 1
2: 2 0 0 0 1 0 0 1
3: 0 0 0 0 0 0 0 0
4: 0 0 0 0 1 0 1 0
5: 1 1 0 1 0 0 1 1
6: 0 0 0 0 0 0 0 0
7: 0 0 0 1 1 0 0 0
8: 1 1 0 0 1 0 0 0

请注意,(1,2)=(2,1) 的计数为 2,来自 dog 组合和 bird 组合。

(1) 为了做到这一点,我认为最好在给定 pandas DataFrame 的情况下创建一个“组合元组”列表。

也就是说,类似

list_combos = [(1, 2), (2, 1), (4, 5), (4, 7), (5, 7), (5, 4), (7, 4), (7, 5),
    (1, 2), (1, 5), (1, 8), (2, 5), (2, 8), (5, 8), (2, 1), (5, 1),
    (8, 1), (5, 2), (8, 2), (8, 5)]

鉴于矩阵是对称的,也许使用起来会更好:

list_combos2 = [(1, 2), (4, 5), (4, 7), (5, 7), (1, 2), (1, 5), (1, 8), (2, 5), (2, 8), (5, 8)]

在给定“names”中的分类值的情况下,如何计算 pandas DataFrame 中整体的排列?

(2) 给定元组列表,填充该矩阵的算法效率最高(即 RAM)是什么?

我应该能够将一个元组列表输入到一个 numpy 数组中,但是如何填充零?

【问题讨论】:

  • 对我来说没关系。只是想知道他们是否有关系。反正我已经回答你了。
  • 你没有解释为什么你需要一个 8x8 矩阵而不是元组列表(或者可能更快,一个 mx2 数组)。 您以后实际使用 8x8 矩阵做什么?

标签: python pandas dataframe matrix count


【解决方案1】:

您可以使用 groupby、迭代组合并像这样构建矩阵:

import numpy as np
from itertools import combinations

mat = np.zeros((df['count'].max(), ) * 2)
idx = []
for _, g in df.groupby('name'):
    idx.extend(combinations(g['count'] - 1, r=2))

np.add.at(mat, list(zip(*idx)), 1)
mat += mat.T

array([[0., 2., 0., 0., 1., 0., 0., 1.],
       [2., 0., 0., 0., 1., 0., 0., 1.],
       [0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 1., 0.],
       [1., 1., 0., 1., 0., 0., 1., 1.],
       [0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 1., 0., 0., 0.],
       [1., 1., 0., 0., 1., 0., 0., 0.]])

可能有一个更快的解决方案,但这是我能想到的最干净的解决方案。

【讨论】:

  • 啊,itertools.combinations 就是我要找的东西!我正在尝试使用itertools.product。谢谢!
  • 打败了我 :) 我还建议存储完整的矩阵是毫无价值的,因为它是对称的并且没有对角线。相反,如果内存/性能很重要,只存储顶部三角形?
  • @AlexanderMcFarlane 是的,特别是对于大型矩阵,因为就像你提到的那样,这是对称的。好点子!
  • @AlexanderMcFarlane 我同意存储整个矩阵毫无价值。你会如何修改这个? idx 真的是所有信息
  • 您可以保留一个平面的一维列表 (idx)。然后在必要时使用一些逻辑重新索引。
【解决方案2】:

使用 Numpy 的 bincount

from itertools import combinations, chain
from collections import defaultdict

d = defaultdict(list)
for tup in df.itertuples():
    d[tup.name].append(tup.count)

i, j = zip(*chain(*(combinations(v, 2) for v in d.values())))
i, j = np.array(i + j) - 1, np.array(j + i) - 1

np.bincount(i * 8 + j, minlength=64).reshape(8, 8)

array([[0, 2, 0, 0, 1, 0, 0, 1],
       [2, 0, 0, 0, 1, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 0, 1, 0],
       [1, 1, 0, 1, 0, 0, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 1, 1, 0, 0, 0],
       [1, 1, 0, 0, 1, 0, 0, 0]])

【讨论】:

    猜你喜欢
    • 2018-05-05
    • 2022-12-22
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 2014-09-25
    • 1970-01-01
    • 2017-08-18
    • 1970-01-01
    相关资源
    最近更新 更多