【问题标题】:How to keep repeated values in a tuple of tuples如何在元组的元组中保留重复值
【发布时间】:2022-11-10 01:36:04
【问题描述】:

假设我得到了类似((1, 2), (2, 3), (2, 3)) 的东西。如您所见,2 在元组中的所有元组中重复。我想要一些东西返回(2)

我正在处理的真实数据集是:

(
  (2, 3, 5, 7, 9),
  (2, 3, 4, 5, 7, 8, 10),
  (2, 3, 4, 5, 6, 7, 9, 10),
  (2, 3, 4, 5, 6, 7, 8, 9),
  (2, 3, 4, 5, 6, 7, 8, 9, 10),
  (2, 3, 4, 5, 6, 7, 8, 9, 10),
  (2, 3, 4, 5, 6, 7, 8, 9, 10),
  (2, 3, 4, 5, 6, 7, 8, 9, 10),
  (2, 3, 4, 5, 6, 7, 8, 9, 10)
)

我将它排除在返回(2, 3, 5, 7) 之外。

我已经尝试过以下方法,但由于某种原因它返回一个空元组。

a = ((1, 2), (2, 3), (2, 3))
print(tuple(filter(lambda x: all(x in i for i in a), a)))

对我来说,有三件事很重要。

  1. 不使用可变数据和语句
  2. 尽可能少for,而不是mapfilter
  3. 应该全部放在一行中,所以我可以将其转换为 lambda 函数左右...

    所以基本上,我想在功能上做到这一点。

【问题讨论】:

  • “它应该全部放在一行中,所以我可以将它转换为 lambda 函数左右......” 为什么这很重要?为什么必须你使用 lambda 表达式来定义你的函数?您可以只使用常规功能定义语句。使用 lambda 表达式并不能使其更具功能性

标签: python tuples


【解决方案1】:

您正在获取一组元组并返回一个不同的元组。所以filter 并不是你真正想要的。如果您正在考虑函数式编程,这对于reduce 来说确实是个问题

这可能看起来像:

from functools import reduce

t = ((2, 3, 5, 7, 9), (2, 3, 4, 5, 7, 8, 10), (2, 3, 4, 5, 6, 7, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9), (2, 3, 4, 5, 6, 7, 8, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9, 10))

reduce(lambda res, curr: tuple(n for n in res if n in curr), t)
# (2, 3, 5, 7)

在这里,您只需在每次迭代时创建一个元组 res,其中包含所有列表看到的值。

【讨论】:

  • 加一个:)我和reduce(set.intersection, t[1:], set(t[0]))一起去,但你的回答感觉更干净。
  • 是的,感谢@AndrejKesely,集合似乎是一个自然的答案——不可变数据结构的要求让我停下来。
  • @Mark 是的,更不用说这效率很低了
  • 我是说你能够使用frozenset 但这是一个愚蠢的要求。如果你想练习函数式编程,你可以不使用 mutator 方法而不是避免任何在 Python 中可变的类型
【解决方案2】:

在这里,使用functools.reduce,您可以执行以下操作:

reduce(frozenset.intersection, map(frozenset, data))

【讨论】:

  • 是的,这似乎是在不牺牲效率的情况下解决非可变需求的最佳方式。
  • 这个方法会创建多少次 freezeset ?
  • @DeepakTripathi len(data) 次。如果没有不可变的限制,我永远不会这样做
  • 我认为它不仅仅是 len(data) 因为首先你在 map 中创建它,然后在使用 reduce 时它总是为每个 reduce 步骤创建一个新的 forzenset ?
  • 当然,然后2*len(data)
【解决方案3】:

稍微不同的方法: 鉴于:

from functools import reduce
a = ((1, 2), (2, 3), (2, 3))
def combo(a, b)
    return a&b
reduce(combo, [set(x) for x in a])

产量 {2}

【讨论】:

  • 如果提供的答案之一对您有用,请将其标记为答案(帖子左侧的复选标记)。发帖人因其选择的答案而获得声誉积分。
【解决方案4】:

使用Counter

from itertools import chain
from collections import Counter
a = ((2, 3, 5, 7, 9), (2, 3, 4, 5, 7, 8, 10), (2, 3, 4, 5, 6, 7, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9), (2, 3, 4, 5, 6, 7, 8, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9, 10), (2, 3, 4, 5, 6, 7, 8, 9, 10))
print([ele[0] for ele in Counter(chain(*a)).items() if ele[1]==len(a)])

【讨论】:

  • 运行此打印[(2, 9), (3, 9), (5, 9), (7, 9)]。这似乎不是 OP 想要的结果。
  • 这是为了清楚地说明这些重复了多少次,以防 OP 只需要密钥,那么简单的 ele[0] 将起作用。
  • 好的,但是它在某些边缘情况下会产生不正确的结果,例如当最常出现的元素没有出现在每个元组中时。例如:a = ((1, 2), (2, 3), (3, 4), (2, 6)) 打印 [(2, 3)]2 不是在每个元组中。同样,如果元组有重复的值,这可能会返回错误的内容。
  • @Mark 是的,你是对的,我已经完成了所需的修改。
【解决方案5】:
(*set.intersection(*map(set, t)),)

p.s.看起来我必须添加一些词以避免将答案标记为低质量.但是这个想法似乎很明显:从元组创建集合并获得它们的交集。

更新

似乎我们可以通过创建一次集合或冻结集合并提供可迭代的对其intersection 方法:

frozenset(t[0]).intersection(*t[1:])

【讨论】:

  • (*<whatever>,) 应该只是 tuple(whatever)
  • @juanpa.arrivillaga 在大多数情况下,这是品味或公司规则的问题。在这种情况下,这可能是合理的,以避免在末尾遗漏逗号。但我认为你的话会挽救局面。因此,保持原样将是非常有用的。谢谢!
猜你喜欢
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-04
相关资源
最近更新 更多