Python - 根据排名信息分组(Cluster/Sort)数组
Python - Group(Cluster/Sort) arrays based on ranking information
我的数据框如下所示:
A B C D
0 5 4 3 2
1 4 5 3 2
2 3 5 2 1
3 4 2 5 1
4 4 5 2 1
5 4 3 5 1
...
我将数据帧转换为二维数组,如下所示:
[[5 4 3 2]
[4 5 3 2]
[3 5 2 1]
[4 2 5 1]
[4 5 2 1]
[4 3 5 1]
...]
每一行的分数1-5
其实就是人们给项目A, B, C, D
的分数。我想确定具有相同排名的人,例如人们认为 A > B > C > D
。我想根据这样的排名信息重新组合这些数组:
2DArray1: [[5 4 3 2]]
2DArray2: [[4 5 3 2]
[3 5 2 1]
[4 5 2 1]]
2DArray3: [[4 2 5 1]
[4 3 5 1]]
例如2DArray2
表示认为B > A > C > D
的人,2DArray3
是认为C > A > B > D
的人。我在 numpy 中尝试了不同的 sort functions 但我找不到合适的。我该怎么办?
Numpy 没有 groupby
函数,因为 groupby 会 return 不同大小列表的列表;而 numpy 大多只处理“矩形”数组。
解决方法是对行进行排序,使相似的行相邻,然后生成每组开头索引的数组。
由于我懒得这样做,这里有一个没有 numpy 的解决方案:
直接按排列索引
对于每一行,我们计算 'ABCD'
的相应排列。然后,我们将该行添加到行列表的字典中,其中字典键是相应的排列。
from collections import defaultdict
a = [[5, 4, 3, 2], [4, 5, 3, 2], [3, 5, 2, 1], [4, 2, 5, 1], [4, 5, 2, 1], [4, 3, 5, 1]]
groups = defaultdict(list)
for row in a:
groups[tuple(sorted(range(len(row)), key=lambda i: row[i], reverse=True))].append(row)
print(groups)
输出:
defaultdict(<class 'list'>, {
(0, 1, 2, 3): [[5, 4, 3, 2]],
(1, 0, 2, 3): [[4, 5, 3, 2], [3, 5, 2, 1], [4, 5, 2, 1]],
(2, 0, 1, 3): [[4, 2, 5, 1], [4, 3, 5, 1]]
})
请注意,使用此解决方案,如果某些用户对两个不同的项目给出相同的分数,结果可能不是您所期望的,因为 sorted
不保持原样;相反,它按出现顺序打破联系(在这种情况下,这意味着两个项目之间的关系按字母顺序打破)。
按排列的索引索引
'ABCD'
的排列可以按字典顺序排列:'ABCD'
排在第一位,然后'ABDC'
排在第二位,然后'ACBD'
排在第三位...
事实证明,有一种算法可以计算给定排列出现在该序列中的索引!该算法在 python 模块 more_itertools
:
中实现
因此,我们可以用简单的数字键 permutation_index(row, sorted(row, reverse=True))
.
替换我们的元组键 tuple(sorted(range(len(row)), key=lambda i: row[i], reverse=True))
from collections import defaultdict
from more_itertools import permutation_index
a = [[5, 4, 3, 2], [4, 5, 3, 2], [3, 5, 2, 1], [4, 2, 5, 1], [4, 5, 2, 1], [4, 3, 5, 1]]
groups = defaultdict(list)
for row in a:
groups[permutation_index(row, sorted(row, reverse=True))].append(row)
print(groups)
输出:
defaultdict(<class 'list'>, {
0: [[5, 4, 3, 2]],
6: [[4, 5, 3, 2], [3, 5, 2, 1], [4, 5, 2, 1]],
8: [[4, 2, 5, 1], [4, 3, 5, 1]]
})
混合 permutation_index 和 pandas
由于 permutation_index 的输出是一个简单的数字,我们可以轻松地将其作为新列包含在 numpy 数组或 pandas 数据帧中:
import pandas as pd
from more_itertools import permutation_index
df = pd.DataFrame({'A': [5,4,3,4,4,4], 'B': [4,5,5,2,5,3], 'C': [3,2,2,5,2,5], 'D': [2,2,1,1,1,1]})
df['perm_idx'] = df.apply(lambda row: permutation_index(row, sorted(row, reverse=True)), axis=1)
print(df)
A B C D perm_idx
0 5 4 3 2 0
1 4 5 2 2 6
2 3 5 2 1 6
3 4 2 5 1 8
4 4 5 2 1 6
5 4 3 5 1 8
for idx, sub_df in df.groupby('perm_idx'):
print(idx)
print(sub_df)
0
A B C D perm_idx
0 5 4 3 2 0
6
A B C D perm_idx
1 4 5 2 2 6
2 3 5 2 1 6
4 4 5 2 1 6
8
A B C D perm_idx
3 4 2 5 1 8
5 4 3 5 1 8
你可以
(i) 转置 df
并将其转换为字典,
(ii) 按值对字典进行排序并获取键,
(iii) 加入每个“人”的排序键并将此字典分配给 df['ranks']
,
(iv) 汇总排名积分并将其分配给 df['pref']
,
(v) groupby(['ranks'])
并从 pref
创建列表
df = pd.DataFrame({'A': {0: 5, 1: 4, 2: 3, 3: 4, 4: 4, 5: 4},
'B': {0: 4, 1: 5, 2: 5, 3: 2, 4: 5, 5: 3},
'C': {0: 3, 1: 3, 2: 2, 3: 5, 4: 2, 5: 5},
'D': {0: 2, 1: 2, 2: 1, 3: 1, 4: 1, 5: 1}})
df['ranks'] = pd.Series({k : ''.join(list(zip(*sorted(v.items(), key=lambda d:d[1],
reverse=True)))[0])
for k,v in df.T.to_dict().items()})
df['pref'] = df.loc[:,'A':'D'].values.tolist()
out = df[['ranks','pref']].groupby('ranks').agg(list).to_dict()['pref']
输出:
{'ABCD': [[5, 4, 3, 2]],
'BACD': [[4, 5, 3, 2], [3, 5, 2, 1], [4, 5, 2, 1]],
'CABD': [[4, 2, 5, 1], [4, 3, 5, 1]]}
我的数据框如下所示:
A B C D
0 5 4 3 2
1 4 5 3 2
2 3 5 2 1
3 4 2 5 1
4 4 5 2 1
5 4 3 5 1
...
我将数据帧转换为二维数组,如下所示:
[[5 4 3 2]
[4 5 3 2]
[3 5 2 1]
[4 2 5 1]
[4 5 2 1]
[4 3 5 1]
...]
每一行的分数1-5
其实就是人们给项目A, B, C, D
的分数。我想确定具有相同排名的人,例如人们认为 A > B > C > D
。我想根据这样的排名信息重新组合这些数组:
2DArray1: [[5 4 3 2]]
2DArray2: [[4 5 3 2]
[3 5 2 1]
[4 5 2 1]]
2DArray3: [[4 2 5 1]
[4 3 5 1]]
例如2DArray2
表示认为B > A > C > D
的人,2DArray3
是认为C > A > B > D
的人。我在 numpy 中尝试了不同的 sort functions 但我找不到合适的。我该怎么办?
Numpy 没有 groupby
函数,因为 groupby 会 return 不同大小列表的列表;而 numpy 大多只处理“矩形”数组。
解决方法是对行进行排序,使相似的行相邻,然后生成每组开头索引的数组。
由于我懒得这样做,这里有一个没有 numpy 的解决方案:
直接按排列索引
对于每一行,我们计算 'ABCD'
的相应排列。然后,我们将该行添加到行列表的字典中,其中字典键是相应的排列。
from collections import defaultdict
a = [[5, 4, 3, 2], [4, 5, 3, 2], [3, 5, 2, 1], [4, 2, 5, 1], [4, 5, 2, 1], [4, 3, 5, 1]]
groups = defaultdict(list)
for row in a:
groups[tuple(sorted(range(len(row)), key=lambda i: row[i], reverse=True))].append(row)
print(groups)
输出:
defaultdict(<class 'list'>, {
(0, 1, 2, 3): [[5, 4, 3, 2]],
(1, 0, 2, 3): [[4, 5, 3, 2], [3, 5, 2, 1], [4, 5, 2, 1]],
(2, 0, 1, 3): [[4, 2, 5, 1], [4, 3, 5, 1]]
})
请注意,使用此解决方案,如果某些用户对两个不同的项目给出相同的分数,结果可能不是您所期望的,因为 sorted
不保持原样;相反,它按出现顺序打破联系(在这种情况下,这意味着两个项目之间的关系按字母顺序打破)。
按排列的索引索引
'ABCD'
的排列可以按字典顺序排列:'ABCD'
排在第一位,然后'ABDC'
排在第二位,然后'ACBD'
排在第三位...
事实证明,有一种算法可以计算给定排列出现在该序列中的索引!该算法在 python 模块 more_itertools
:
因此,我们可以用简单的数字键 permutation_index(row, sorted(row, reverse=True))
.
tuple(sorted(range(len(row)), key=lambda i: row[i], reverse=True))
from collections import defaultdict
from more_itertools import permutation_index
a = [[5, 4, 3, 2], [4, 5, 3, 2], [3, 5, 2, 1], [4, 2, 5, 1], [4, 5, 2, 1], [4, 3, 5, 1]]
groups = defaultdict(list)
for row in a:
groups[permutation_index(row, sorted(row, reverse=True))].append(row)
print(groups)
输出:
defaultdict(<class 'list'>, {
0: [[5, 4, 3, 2]],
6: [[4, 5, 3, 2], [3, 5, 2, 1], [4, 5, 2, 1]],
8: [[4, 2, 5, 1], [4, 3, 5, 1]]
})
混合 permutation_index 和 pandas
由于 permutation_index 的输出是一个简单的数字,我们可以轻松地将其作为新列包含在 numpy 数组或 pandas 数据帧中:
import pandas as pd
from more_itertools import permutation_index
df = pd.DataFrame({'A': [5,4,3,4,4,4], 'B': [4,5,5,2,5,3], 'C': [3,2,2,5,2,5], 'D': [2,2,1,1,1,1]})
df['perm_idx'] = df.apply(lambda row: permutation_index(row, sorted(row, reverse=True)), axis=1)
print(df)
A B C D perm_idx
0 5 4 3 2 0
1 4 5 2 2 6
2 3 5 2 1 6
3 4 2 5 1 8
4 4 5 2 1 6
5 4 3 5 1 8
for idx, sub_df in df.groupby('perm_idx'):
print(idx)
print(sub_df)
0
A B C D perm_idx
0 5 4 3 2 0
6
A B C D perm_idx
1 4 5 2 2 6
2 3 5 2 1 6
4 4 5 2 1 6
8
A B C D perm_idx
3 4 2 5 1 8
5 4 3 5 1 8
你可以
(i) 转置 df
并将其转换为字典,
(ii) 按值对字典进行排序并获取键,
(iii) 加入每个“人”的排序键并将此字典分配给 df['ranks']
,
(iv) 汇总排名积分并将其分配给 df['pref']
,
(v) groupby(['ranks'])
并从 pref
df = pd.DataFrame({'A': {0: 5, 1: 4, 2: 3, 3: 4, 4: 4, 5: 4},
'B': {0: 4, 1: 5, 2: 5, 3: 2, 4: 5, 5: 3},
'C': {0: 3, 1: 3, 2: 2, 3: 5, 4: 2, 5: 5},
'D': {0: 2, 1: 2, 2: 1, 3: 1, 4: 1, 5: 1}})
df['ranks'] = pd.Series({k : ''.join(list(zip(*sorted(v.items(), key=lambda d:d[1],
reverse=True)))[0])
for k,v in df.T.to_dict().items()})
df['pref'] = df.loc[:,'A':'D'].values.tolist()
out = df[['ranks','pref']].groupby('ranks').agg(list).to_dict()['pref']
输出:
{'ABCD': [[5, 4, 3, 2]],
'BACD': [[4, 5, 3, 2], [3, 5, 2, 1], [4, 5, 2, 1]],
'CABD': [[4, 2, 5, 1], [4, 3, 5, 1]]}