【问题标题】:String Compression in PythonPython 中的字符串压缩
【发布时间】:2023-02-01 15:43:50
【问题描述】:

我有以下输入:

 my_list = ["x d1","y d1","z d2","t d2"]

并想将其转换为:

Expected_result = ["d1(x,y)","d2(z,t)"]

我不得不使用蛮力,而且还不得不调用 pandas 来拯救我,因为我没有找到任何在普通/香草 python 中做到这一点的方法。您还有其他方法可以解决这个问题吗?

import pandas as pd 

my_list = ["x d1","y d1","z d2","t d2"]

df = pd.DataFrame(my_list,columns=["col1"])

df2 = df["col1"].str.split(" ",expand = True)
df2.columns = ["col1","col2"]
grp = df2.groupby(["col2"])

result = []
for grp_name, data in grp:
  res =  grp_name +"(" + ",".join(list(data["col1"])) + ")"
  result.append(res)
print(result)

【问题讨论】:

  • 您的要求在问题中没有明确说明。您在下面的 cmets 中提到您想要处理像 ["x d1", "y d1", "z d1"] 这样的情况。 ["x d1", "y d1", "t d2", "z d1", "u d2"] 呢?还有哪些案件需要处理?
  • ["x d1", "y d1", "t d2", "z d1", "u d2"] 不是边缘条件。它属于一般情况,结果将是 ['d1(x,y,z)', 'd2(t,u)']

标签: python pandas string substring


【解决方案1】:
  1. 代码定义了一个空字典。
  2. 然后它遍历列表中的每个项目并使用split() 方法将项目拆分为keyvalue
  3. 然后使用setdefault()方法将keyvalue添加到空字典中。如果 value 已经作为 key 存在于字典中,它会将 key 附加到该值的现有键列表中。如果 value 在字典中不作为键存在,它会创建一个新的键值对,以值作为键,键作为新列表中的第一个元素。
  4. 最后,列表理解遍历字典中的项目,并使用join() 方法为每个键值对创建一个字符串,以将值列表中的键连接成一个字符串。
    result = {}
    
    for item in my_list:
        key, value = item.split()
        result.setdefault(value, []).append(key)
        
    output = [f"{k}({', '.join(v)})" for k, v in result.items()]
    print(output)
    

    ['d1(x, y)', 'd2(z, t)']
    

【讨论】:

  • 您可以用单个 result.setdefault(value, []).append(key) 替换 if 和它后面的两行(另外,反转变量名称可能更有意义);)
  • 我同意变量的名称应该像这样更改:for item in my_list: value, key = item.split() result.setdefault(key, []).append(value) 另外,你介意解释一下吗关于这条线是如何工作的? result.setdefault(key, []).append(value) 因为这是算法的关键
  • 我在答案中添加了一些解释,您可以参考。我希望对@ShckTchamna 有所帮助
【解决方案2】:

如果您的值已经按键排序(d1, d2),可以使用itertools.groupby

from itertools import groupby

out = [f"{k}({','.join(x[0] for x in g)})"
       for k, g in groupby(map(str.split, my_list), lambda x: x[1])]

输出:

['d1(x,y)', 'd2(z,t)']

否则你应该使用字典as shown by @Jamiu

你的熊猫解决方案的一个变体:

out = (df['col1'].str.split(n=1, expand=True)
       .groupby(1)[0]
       .apply(lambda g: f"{g.name}({','.join(g)})")
       .tolist()
      )

【讨论】:

【解决方案3】:
my_list = ["x d1","y d1","z d2","t d2"]
res = []
 
for item in my_list:

    a, b, *_ = item.split()
 
    if len(res) and b in res[-1]:
            res[-1] = res[-1].replace(')', f',{a})')
    else:
        res.append(f'{b}({a})')

print(res)
['d1(x,y)', 'd2(z,t)']

设 N 是 d 之后的数字,此代码适用于 dN 中的任意数量的元素,只要 N 是有序的,即 d1 在 d2 之前,d2 在 d3 之前,...适用于 N 的任何值,您可以在 d 链接中使用任何字母,只要它具有 dN 和 dN 中的任何值,保持该顺序,“val_in_dN dN”

如果你需要一些即使 dN 不按顺序也能工作的东西,只需说出这个词,但它会花费更多

【讨论】:

    【解决方案4】:

    另一种可能的解决方案,基于pandas

    (pd.DataFrame(np.array([str.split(x, ' ') for x in my_list]), columns=['b', 'a'])
     .groupby('a')['b'].apply(lambda x: f'({x.values[0]}, {x.values[1]})')
     .reset_index().sum(axis=1).tolist())
    

    输出:

    ['d1(x, y)', 'd2(z, t)']
    

    编辑

    OP,@ShckTchamna,希望看到修改上述解决方案,以便更通用:此编辑的原因是提供一个解决方案,该解决方案适用于 OP 在他下面的评论中给出的示例。

    my_list = ["x d1","y d1","z d2","t d2","kk d2","m d3", "n d3", "s d4"] 
    
    (pd.DataFrame(np.array([str.split(x, ' ') for x in my_list]), columns=['b', 'a'])
     .groupby('a')['b'].apply(lambda x: f'({",".join(x.values)})')
     .reset_index().sum(axis=1).tolist())
    

    输出:

    ['d1(x,y)', 'd2(z,t,kk)', 'd3(m,n)', 'd4(s)']
    

    【讨论】:

    • 谢谢您的意见。我赞成它。但是,似乎应该改进您的解决方案以使其更加通用。例如:尝试以下操作: 输入:my_list = ["x d1","y d1","z d2","t d2","kk d2","m d3", "n d3", "s d4 "] 预期输出 = ['d1(x,y)', 'd2(z,t,kk)', 'd3(m,n)', 'd4(s)']
    • 感谢@ShckTchamna 对我的解决方案发表评论并进行投票。我会很快添加一个方法来处理你指出的情况(目前很忙)。
    【解决方案5】:
    import pandas as pd
    
    df = pd.DataFrame(data=[e.split(' ') for e in ["x d1","y d1","z d2","t d2"]])
    r = (df.groupby(1)
           .apply(lambda r:"{0}({1},{2})".format(r.iloc[0,1], r.iloc[0,0], r.iloc[1,0]))
           .reset_index()
           .rename({1:"points", 0:"coordinates"}, axis=1)
             )
    
    print(r.coordinates.tolist())
    # ['d1(x,y)', 'd2(z,t)']
    
    print(r)
    #   points coordinates
    # 0    d1     d1(x,y)
    # 1    d2     d2(z,t)
    

    代替我以前的(也有效):

    import itertools as it
    
    my_list = [e.split(' ') for e in ["x d1","y d1","z d2","t d2"]]
    
    r=[]
    for key, group in it.groupby(my_list, lambda x: x[1]):
        l=[e[0] for e in list(group)]
        r.append("{0}({1},{2})".format(key, l[0], l[1]))
    
    print(r)
    Output :
    
    ['d1(x,y)', 'd2(z,t)']
    

    【讨论】:

    • 谢谢您的意见。我赞成它。但是,似乎应该改进您的解决方案以使其更加通用。例如:尝试以下操作: 输入:my_list = ["x d1","y d1","z d2","t d2","kk d2","m d3", "n d3", "s d4 "] 预期输出 = ['d1(x,y)', 'd2(z,t,kk)', 'd3(m,n)', 'd4(s)']
    • 感谢您的评论。我正在做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-20
    相关资源
    最近更新 更多