【问题标题】:Compare lists of strings regarding the suffixes of their respective elements比较有关其各自元素后缀的字符串列表
【发布时间】:2015-09-15 05:28:25
【问题描述】:

假设我有一个这样的列表

myList = ['A_x','B_x','C_x','D_x']

和这样的列表列表

myListOfList = [['A_x','B_y','C_x','D_z'],
                ['A_y','B_y','C_y','D_y'],
                ['A_u','B_y','C_y','D_y'],
                ['A_y','C_y','B_y','D_y', 'E_l'],
                ['A_y','P_y','T_y','D_y'],
                ['A_y','B_y','C_y'],
                ['A_y','C_y','D_y','B_y'],
                ['A_z','C_z','D_z','B_z']]

现在我想确定myListOfList 中与myList 相同的所有列表,除了这些列表中各个元素的后缀,myListOfList 子列表中元素的后缀必须相同.所以在上面的例子中,我想找到:

#all elements as in myList but with suffix y instead of x
['A_y','B_y','C_y','D_y']
#all elements as in myList but with suffix y instead of x but in different order
['A_y','C_y','D_y','B_y']
#all elements as in myList but with suffix z instead of x but in different order
['A_z','C_z','D_z','B_z']

但我不想找到其他元素,因为它们的元素后缀不同,长度与 myList's 长度不同,或者元素的第一部分与 myList 中的元素不同。

我是这样实现的:

myList = ['A_x','B_x','C_x','D_x']

myListOfList = [['A_x','B_y','C_x','D_z'],
                ['A_y','B_y','C_y','D_y'],
                ['A_u','B_y','C_y','D_y'],
                ['A_y','C_y','B_y','D_y', 'E_l'],
                ['A_y','P_y','T_y','D_y'],
                ['A_y','B_y','C_y'],
                ['A_y','C_y','D_y','B_y'],
                ['A_z','C_z','D_z','B_z']]

listOfInd=[]                
for ind, sl in enumerate(myListOfList):
    if len(sl) == len(myList) and [x.endswith(sl[0][-2:]) for x in sl].count(False) == 0:
        tempList = zip(myList, sorted(sl))
        allTrue = True
        for el in tempList:
            #print el[0]
            if el[0][:-2] != el[1][:-2]:
                allTrue = False

        if(allTrue):
            listOfInd.append(ind)

这确实给了我正确的输出:listOfInd = [1, 6, 7]

解释这段代码的两个元素:

这会检查列表 sl 中的所有元素是否具有相同的后缀:

[x.endswith(sl[0][-2:]) for x in sl].count(False) == 0

这会检查字符串 el 除了后缀(字符串的最后两个元素)是否相同:

if el[0][:-2] != el[1][:-2]

所以我要做的是遍历myListOfList中的每个列表,如果此列表中的元素数量与myList中的相同并且此列表中的所有元素具有相同的后缀,我zip这个以myList 列出。然后我遍历这个压缩列表中的每个元素并比较字符串直到后缀。

代码可以工作,但是,它看起来效率很低,我想知道是否有一种更“pythonic”的方式可以在没有这么多循环的情况下执行此操作。谁能想到一种更有效的方法来实现这一点?

【问题讨论】:

  • ['A_y','B_y','C_z','D_z']['A_x','D_k','B_x','C_k'] 是否被视为相等?
  • @mescalinum:不,列表中的后缀必须相同。

标签: python performance list string-comparison


【解决方案1】:

如果前缀设置,检查前缀集是否是子列表中每个元素前缀的子集,如果是则检查所有后缀是否相同。

st = {s[0] for s in myList}
l = []
for ind, sub in enumerate(myListOfList):
    k = sub[0][-1]
    if st.issubset(ch[0] for ch in sub) and all((ch[-1] == k for ch in sub)):
        l.append(ind)
print(l)
[1, 6, 7]

如果您愿意,可以将其放入列表组合中:

inds = [ind for ind, sub in enumerate(myListOfList) if st.issubset(ch[0] for ch in sub)
        and all(ch[-1] == sub[0][-1] for ch in sub)]

print(inds)
[1, 6, 7]

您还可以进行一些优化,如果任何 sub 的长度不等于 myList 的长度,我们就不能匹配:,我们可以使用 all 来查看每个 sub 中的每个 element[0] 是否是在集合中,因为这可能会快一点。

l = []
ln_m = len(myList)
for ind, sub in enumerate(myListOfList):
    k = sub[0][-1]
    if len(sub) == ln_m and all(ch[0] in st for ch in sub) and all(ch[-1] == k for ch in sub):
        l.append(ind)

【讨论】:

  • 看起来棒极了!而且似乎比我的实现要快得多。我将针对其他数据集对其进行测试。我现在投赞成票,以后可能会接受!
  • @Cleb,别担心,我会再改进一下。
【解决方案2】:

这是我使用套装的尝试。请注意,我使用的是字符串子索引,而不是拆分 '_' 或正则表达式,因为我假设格式非常严格。

myList = ['A_x','B_x','C_x','D_x']

myListOfList = [['A_x','B_y','C_x','D_z'],
                ['A_y','B_y','C_y','D_y'],
                ['A_u','B_y','C_y','D_y'],
                ['A_y','C_y','B_y','D_y', 'E_l'],
                ['A_y','P_y','T_y','D_y'],
                ['A_y','B_y','C_y'],
                ['A_y','C_y','D_y','B_y'],
                ['A_z','C_z','D_z','B_z']]

myList_prefixes = set((x[0] for x in myList))

listOfInd = []
for idx, sublist in enumerate(myListOfList):
    if len(sublist) != len(myList):
        continue
    sublist_suffixes = set((x[-1] for x in sublist))
    # ensure that you only have one kind of suffix, like only x or only y
    if len(sublist_suffixes) != 1:
        continue
    sublist_prefixes = set((x[0] for x in sublist))
    # make sure the prefixes match
    if sublist_prefixes != myList_prefixes:
        continue
    listOfInd.append(idx)
print listOfInd

【讨论】:

  • 感谢您的选择!对于这个例子,速度几乎没有区别;我将在更大的数据集上对其进行测试。我赞成您的回答,因为它是一种可行的解决方案,也是一种不错的替代方式。
【解决方案3】:
def compare(l1,l2):
    def normalize(l):
        r = defaultdict(set)
        for a,b in map(lambda x: x.split('_'), l): r[b].add(a)
        return set(tuple(sorted(v)) for v in r.values())
    n1,n2 = normalize(l1),normalize(l2)
    return len(n1)==1 and len(n2)==1 and n1==n2

测试:

>>> [i for i,l in enumerate(myListOfList) if compare(myList,l)]
[1, 6, 7]

关于 compare() 函数内部的 normalize() 函数的一点说明:它将为每个后缀生成一组排序的前缀:

>>> normalize(myList)
{('A', 'B', 'C', 'D')}

>>> normalize(['A_x','B_x','C_x','D_y'])
{('A', 'B', 'C'), ('D',)}

>>> normalize(['A_x','B_y','C_z','D_x'])
{('A', 'D'), ('B',), ('C',)}

实际上,如果您删除 len(n1)==1 and len(n2)==1 条件,您可以像我对您的问题的评论中那样比较列表。

【讨论】:

  • 效果也很好,但似乎比 Padraic Cunningham 的解决方案慢。无论如何我都赞成:)
猜你喜欢
  • 2015-01-23
  • 1970-01-01
  • 2017-02-02
  • 1970-01-01
  • 2014-05-08
  • 1970-01-01
  • 2021-10-17
  • 1970-01-01
  • 2012-12-19
相关资源
最近更新 更多