我有3个列表,a
,b
和c
每个列表都包含3个数字的元组。
以下是输入示例:
a = [(1,2,4),(1,7,8),(1,5,4),(3,6,7)]
b = [(1,2,5),(1,9,3),(1,0,3),(3,6,8)]
c = [(2,6,3),(2,4,9),(2,8,5),(1,2,7)]
我正在寻找一种方法来生成一个列表,如果每个元组的两个第一项是等于的,那么就会获取这3个列表的元素,并添加第三个元素。
在我提供的数据中,只有一组元组,其中2个第一个值等于:(1,2,4)
,(1,2,5)
和(1,2,7)
。
如果我添加第三个值4+5+7 = 16
,那么对于这些数据,我最后应该[(1,2,16)]
。
每个列表中的两个第一个值是唯一的,[(1,2,7),(1,2,15)]
将不存在。
问题不是找到只有两个第一个值相等的元组,它很容易用列表理解来完成。但我仍然坚持寻找一种同时添加第三个值的pythonistic方法。
我可以做到这一点:
elem_list = []
for elem in a:
b_elem = [i for i in b if i[:-1] == elem[:-1]]
c_elem = [i for i in c if i[:-1] == elem[:-1]]
if len(b_elem) != 0 and len(c_elem) != 0:
elem_list.append((elem[0],elem[1], elem[2]+b_elem[0][2]+c_elem[0][2]))
这给了我想要的输出,但它真的很长,这就是为什么我确定这是一种愚蠢的方式来做到这一点没有麻烦,我只是想不出来。
答案 0 :(得分:3)
效率不高但会做你想做的事情:
a = [(1,2,4),(1,7,8),(1,5,4),(3,6,7)]
b = [(1,2,5),(1,9,3),(1,0,3),(3,6,8)]
c = [(2,6,3),(2,4,9),(2,8,5),(1,2,7)]
from itertools import product
print(filter(lambda x: x[0][:2] == x[1][:2] == x[2][:2] ,product(a,b,c)))
[((1, 2, 4), (1, 2, 5), (1, 2, 7))]
答案 1 :(得分:3)
这是一种方法:
from itertools import product, starmap
def solve(*tups):
key = tups[0][:2]
if all(x[:2] == key for x in tups):
return key + (sum(x[2] for x in tups), )
for p in product(a, b, c):
out = solve(*p)
if out:
print out
#(1, 2, 16)
或使用上述功能的单行:
print filter(None, starmap(solve, product(a, b, c)))
#[(1, 2, 16)]
答案 2 :(得分:1)
这是一种不考虑任何效率的方法(它循环i * j * k次,假设i,j和k是列表a,b,c的长度)。
from operator import itemgetter
f = itemgetter(0,1)
print [(x[0],x[1],x[2]+y[2]+z[2]) for x in a for y in b for z in c if f(x)==f(y)==f(z)]
<强>输出:强>
[(1, 2, 16)]
答案 3 :(得分:0)
使用三元组的前两个元素作为字典的键。添加三元组的第三个元素,并将其用作字典条目的值。
d = {}
# group the tuples and sum
for x,y,z in a+b+c:
d[(x,y)] = d.get((x,y), 0) + z
results = []
# sort the keys and convert to a list of tuples
for k in sorted(d.keys()):
x,y = k
results.append((x,y,d[(x,y)]))
print results
答案 4 :(得分:0)
为了更好的衡量,这是一个很好的无聊方式,将位置与您的自定义匹配逻辑(例如前两个元组组件),转换逻辑(例如,将第三个组件相加)分离成普通的无聊辅助函数,然后使用无聊的for-loop进行简单的递归调用(每次通过过滤掉不匹配来缩小) - 这是避免对itertools.product
或starmap
进行浪费调用的一种方法。
from functools import partial
from operator import eq, is_not, itemgetter
a = [(1,2,4),(1,7,8),(1,5,4),(3,6,7)]
b = [(1,2,5),(1,9,3),(1,0,3),(3,6,8)]
c = [(2,6,3),(2,4,9),(2,8,5),(1,2,7)]
is_not_none = partial(is_not, None)
def my_match_criterion(t1, t2):
return eq(*map(itemgetter(0,1), (t1, t2)))
def my_transformation(t1, t2):
return t1[0:2] + (t1[2] + t2[2],)
def collapse_matches_with_transformation(tups, *args):
if args == ():
return tups
else:
collapsed = collapse_matches_with_transformation(*args)
for i,c in enumerate(collapsed):
include = False
for t in tups:
if my_match_criterion(t, c):
collapsed[i], include = my_transformation(t, c), True
if not include:
collapsed[i] = None
return filter(is_not_none, collapsed)
print collapse_matches_with_transformation(a, b, c)
我可能代表脾气暴躁的反对者 - 我的观点是,这至少与任何理解事业一样是Pythonic。将“Pythonic”这个术语称为“不惜一切代价简化语法”已经变得有点过于时髦了。许多人习惯于将单行理解或内联lambda
函数视为关键参数,这使得这种情况持续存在。他们可以轻松地阅读这些东西,仅仅是熟悉的工件,云思考这种方式真正是否更具“可读性”,从封装的角度来看它是否是好的。 / p>
当然,如果你的问题只需要在一个小的实例上解决一次,比如在解释器中玩游戏,那么无论什么都有效......
但是如果您可能会重新访问此代码,如果发生这种情况的可能性很小,为什么不将不同的需求部分写入不同的分离函数中呢?
在这个问题中有几件事情在起作用:(1)可能需要处理多少个元组列表?总是只有3个? (2)匹配条件有多大可能发生变化?如果您突然需要包含一个新的数据,要使您的元组4元组与前3个元素匹配,需要更改多少代码以及需要更改多少个位置? (3)如果你需要改变转变怎么办?如果你需要乘法或加总其他元素,而不是对第3个元素进行求和?
这些注意事项必须在几乎所有实际问题中编制预算(阅读:您使用此代码的任何位置不止一次)。
在任何一种情况下,所有垃圾代码都涉及抛出诸如lambda x: x[0:2] ... blah
之类的大量内容,或者只是将逻辑x[2] + y[2] + z[2]
放入返回结果的理解中,等等,虚假的简洁,因为系统非常脆弱假设它只是3个3元组的列表,其中第3个组件只需要在前两个组件匹配的唯一匹配条件下求和。
最后,即使您知道所有这些事情都将得到解决,更好提供简洁的方法是更改数据结构。例如,如果您首先将列表的列表转换为计数器列表,并将前两个元素的子元组作为键,则执行此操作的速度非常快:
from collections import Counter
def countify(tups):
return Counter({t[0:2]:t[2] for t in tups})
a, b, c = map(countify, (a,b,c))
common_keys = set(a.keys()).intersection(b, c)
totals = a + b + c
print [k + (itemgetter(k)(totals),) for k in common_keys]
大多数人肯定会说第二种方法更像是“Pythonic” - 但实际上,如果你不介意严重承诺你正在对原始的前两个组成部分的值进行求和,这是真的。元组。代码并没有干净地推广到3个以上的元组列表,它对于转换或数据表示中的微小变化并不健全。
这就是为什么我认为这种代码高尔夫简洁应该不与“Pythonic”同义 - 这应该更加关注什么是实用和直接的。 “简单”并不一定意味着“简短”和“复杂”与“更多代码行”并不完全相关。 “可读性”非常主观,并且根据经验而变化很大。
做无聊的事!写下额外的帮手功能!写出for-loop!你以后会更加兴奋!