【问题标题】:Python with Itertools?Python 与 Itertools?
【发布时间】:2018-04-12 21:29:09
【问题描述】:

我有一个 .csv 包含 3 列。 PersonX、PersonY 和 PersonZ。有 7000 行名称和不同的组合。我的目标是看看哪对和三人组是最高的匹配。我无法在 excel 中找到可以实现此目标的公式。我确信 python 将能够与 itertools 组合,但我还没有那么先进。这些名字可以按任何顺序排列,只是看看这 2 或 3 个人在同一行中有多少次。任何建议都会有很大帮助,谢谢!

小数据示例

PersonX         PersonY             PersonZ
Aaron Ekblad    Keith Yandle        Vincent Trocheck
Aaron Ekblad    Denis Malgin        Mike Matheson
Aaron Ekblad    Denis Malgin        Mike Matheson
Aaron Ekblad    Jonathan Huberdeau  Keith Yandle
Aaron Ekblad    Jonathan Huberdeau  Keith Yandle
Aaron Ekblad    Jamie McGinn        Keith Yandle
Aaron Ekblad    Aleksander Barkov   Jonathan Huberdeau
Aaron Ekblad        
Adam Erne       Andrej Sustr        Vladislav Namestnikov
Adam Erne       Anthony Cirelli 
Adam Erne       
Adam Henrique   Rickard Rakell      Ryan Getzlaf
Adam Henrique   Brandon Montour     Ryan Getzlaf
Adam Henrique   Corey Perry         Brandon Montour
Adam Henrique   Corey Perry         Brandon Montour
Adam Henrique   Brian Gibbons       Andy Greene
Adam Henrique   Ryan Getzlaf    
Adam Henrique   Ondrej Kase 
Adam Henrique   Josh Manson 
Adam Henrique   Brian Gibbons   
Adam Henrique       
Adam Henrique   

启动脚本

import csv
from itertools import combinations, product

#Header = PersonX PersonY PersonZ

#Import Game
with open('1718_All_Goals_&_Assists.csv', newline='') as f:
    next(f)
    skaters = '\n'.join(' '.join(row) for row in csv.reader(f))
    print(skaters)

【问题讨论】:

  • 你的数据集的结果是什么(为什么)?另外,您的数据集中的分隔符是什么?它不能是空格,因为名称包含空格并且您的示例不包含制表符。
  • 一对或一组已经在一起的次数。比我可以按最大排序并查看最频繁的对或组

标签: excel python-3.x itertools


【解决方案1】:

您可以简单地在您的csv.reader 上使用collections.Counter

from collections import Counter

>>> cnt = Counter(frozenset(item.strip() for item in line if item.strip()) for line in csv.reader(f))

使用 Counter 的.most_common 方法可以得到排序(降序)的输出:

>>> cnt.most_common()
[(frozenset({'Aaron Ekblad', 'Denis Malgin', 'Mike Matheson'}), 2),
 (frozenset({'Aaron Ekblad', 'Jonathan Huberdeau', 'Keith Yandle'}), 2),
 (frozenset({'Adam Henrique', 'Brandon Montour', 'Corey Perry'}), 2),
 (frozenset({'Adam Henrique'}), 2),
 (frozenset({'Aaron Ekblad', 'Keith Yandle', 'Vincent Trocheck'}), 1),
 (frozenset({'Aaron Ekblad', 'Jamie McGinn', 'Keith Yandle'}), 1),
 (frozenset({'Aaron Ekblad', 'Aleksander Barkov', 'Jonathan Huberdeau'}), 1),
 (frozenset({'Aaron Ekblad'}), 1),
 (frozenset({'Adam Erne', 'Andrej Sustr', 'Vladislav Namestnikov'}), 1),
 (frozenset({'Adam Erne', 'Anthony Cirelli'}), 1),
 (frozenset({'Adam Erne'}), 1),
 (frozenset({'Adam Henrique', 'Rickard Rakell', 'Ryan Getzlaf'}), 1),
 (frozenset({'Adam Henrique', 'Brandon Montour', 'Ryan Getzlaf'}), 1),
 (frozenset({'Adam Henrique', 'Andy Greene', 'Brian Gibbons'}), 1),
 (frozenset({'Adam Henrique', 'Ryan Getzlaf'}), 1),
 (frozenset({'Adam Henrique', 'Ondrej Kase'}), 1),
 (frozenset({'Adam Henrique', 'Josh Manson'}), 1),
 (frozenset({'Adam Henrique', 'Brian Gibbons'}), 1)]

同样,您可以使用以下方法获得最常见的(如果是平局,它将只返回其中之一):

>>> cnt.most_common(1)
[(frozenset({'Aaron Ekblad', 'Denis Malgin', 'Mike Matheson'}), 2)]

如果您希望所有的都具有最大计数,您可以使用 max 的自定义方法:

>>> maximum_occurences = max(cnt.values())
>>> [group for group, occurences in cnt.items() if occurences == maximum_occurences]
[frozenset({'Aaron Ekblad', 'Denis Malgin', 'Mike Matheson'}),
 frozenset({'Aaron Ekblad', 'Jonathan Huberdeau', 'Keith Yandle'}),
 frozenset({'Adam Henrique', 'Brandon Montour', 'Corey Perry'}),
 frozenset({'Adam Henrique'})]

补充一点解释:

Counter 只计算元素,这应该正是您想要的 - 但要计算的值必须是可散列的。这很不方便,因为csv.reader 将返回列表(不方便,因为列表不可散列),因此您需要另一个数据结构。

tuples 会浮现在脑海中,但是因为您说名称可以按任何顺序排列,所以您可能想要一个无序的集合。这意味着您应该使用frozenset。但是有一个警告:它不能多次保存相同的值,所以如果你有同名的人,你就不能使用它。

我不知道您的 csv 的结构如何,因此结果可能在名称之前/之后包含空“列”或空格,这就是为什么我过滤空元素并使用以下方法剥离名称:

item.strip() for item in line if item.strip()

在传递给frozenset之前。

如果您不喜欢它strips 两次,您可以添加另一个理解或map

frozenset(item for item in (item.strip() for item in line) if item)
frozenset(item for item in map(lambda x: x.strip(), line) if item)
frozenset(item for item in map(str.strip, line) if item)  # if all items are really of type str

顺便说一句,外部理解可以被filter替换为bool

frozenset(filter(bool, map(str.strip, line)))

哦,“函数式编程”的奇迹。我什至没有用过itertools

我完全忘记了:如果您想省略单人组,您可以在访问 most_common 组之前轻松过滤 Counter

cnt = Counter({k: v for k, v in cnt.items() if len(k) > 1})

这可以在之后应用,或者您可以尝试将其放入 Counter 中的理解中,但我将把它留给感兴趣的读者作为练习。


以防万一您可能在一行中多次使用相同的名称并且您不想丢失该“信息”,您可以计算每一行中的名称,然后将该计数器转换为将要成为的元组计数。

Counter(tuple(Counter(filter(bool, map(str.strip, line))).most_common()) for line in csv.reader(f))

我希望我没有过度使用单线。

【讨论】:

  • 哇,谢谢 Mseifert!是否可以编辑我上面的脚本以使其正常工作?写出来有点麻烦。另外,是否也可以将这些结果导出到 csv 中?我会将此标记为已回答,但我只是在写出来时遇到问题。
  • 有没有办法做最常见的一对?
  • @MichaelTJohnson 在这种情况下,最常见的对是什么?生成的 csv 应该是什么样子?关于脚本:将skaters = '\n'.join(' '.join(row) for row in csv.reader(f))print(skaters) 替换为我要使用的答案中的行,例如cnt = Counter(frozenset(item.strip() for item in line if item.strip()) for line in csv.reader(f))print(cnt.most_common(1))
  • 昨晚我能够让它工作,谢谢。我希望有一种方法可以查看最常见的配对。例如,如果 john、frank、joe 在一行中,而 joe、john、tom 在一行中,则最常见的一对是 joe 和 john 2 次。如果您可以导出 csv,很高兴看到 most_common_pair(20) 以及它们在一起的次数。这可能吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-06
  • 1970-01-01
  • 1970-01-01
  • 2017-11-02
  • 2015-09-08
  • 1970-01-01
相关资源
最近更新 更多