基于两列中的值合并其他列中的值

Posted

技术标签:

【中文标题】基于两列中的值合并其他列中的值【英文标题】:Based on the values in two columns merge values in other columns 【发布时间】:2012-10-13 12:37:41 【问题描述】:

我有一个包含四列的制表符分隔文件。我需要为“col1”和“col2”中的每个唯一值对组合“col3”和“col4”。示例和输出如下所示。

我正在考虑的一种方法是使用嵌套循环:外循环按顺序读取行,内循环从头开始读取所有行并查找 map。然而,这个过程似乎是计算密集型的。

有没有其他方法可以做到这一点。

col1    col2    col3    col4
a   c   1,2 physical
a   c   2,3 genetic
b   c   22  physical 
b   d   33,44   genetic
c   e   1,2 genetic
c   e   2   physical
c   f   33,44   physical
c   f   3   genetic
a   a   4   genetic
e   c   1,2 xxxxx


col1    col2    col3    col4
a   c   1,2,3   genetic,physical
a   a   4   genetic
b   c   22  physical 
b   d   33,44   genetic
c   e   1,2 genetic,physical,xxxxx
c   f   3,33,44 genetic,physical

如果 'col1' 和 'col2' 像上面最后一行那样切换为值 'xxxxx',它会合并值

【问题讨论】:

【参考方案1】:

我会创建一个键字典,这些键是包含 column1 和 column2 数据的元组。这些值将是一个包含 column3 和 column4 数据的列表...

from collections import defaultdict
with open('test.dat') as f:
    data = defaultdict( lambda:([],[]))
    header = f.readline()
    for line in f:
        col1,col2,col3,col4 = line.split()
        col3_data,col4_data = data[(col1,col2)]  #data[frozenset((col1,col2))] if order doesn't matter
        col3_data.append(col3)
        col4_data.append(col4)

现在对输出进行排序和写入(使用',' 连接第 3 列和第 4 列列表,使用 setsorted 使其唯一以正确排序)

with open('outfile.dat','w') as f:
   f.write(header)
   #If you used a frozenset in the first part, you might want to do something like:
   #for k in sorted(map(sorted,data.keys())):
   for k in sorted(data.keys()):
       col1,col2 = k
       col3_data,col4_data = data[k]
       col3_data = ','.join(col3_data) #join the list
       col3_data = set(int(x) for x in col3_data.split(',')) #make unique integers
       col3_str = ','.join(map(str,sorted(col3_data)))       #sort, convert to strings and join with ','
       col4_data = ','.join(col4_data)  #join the list
       col4_data = sorted(set(col4_data.split(',')))  #make unique and sort
       f.write('0\t1\t2\t3\n'.format(col1,col2,col3_str,','.join(col4_data)))

【讨论】:

感谢@mgilson,但我在“Traceback(最近一次调用最后一次)中出现错误:文件“”,第 6 行,在 ValueError:需要超过 0 个值解包”。这是因为第一部分代码的第 7 行。 @Curious -- 在我测试代码之前我也明白了。请参阅我的修订。 (具体来说,应该是defaultdict(lambda:([],[])) 而不是defaultdict(lambda:[],[]) 谢谢。根据需要完美地工作。有没有办法忽略 col1 和 col2 中的排序。我在问题中编辑了这个。 @Curious -- 你可以在第一部分使用col3_data,col4_data = data[frozenset((col1,col2))]。 . .这使得在第二部分中对键进行排序毫无价值。 @Curious -- 查看我添加的 cmets(两个部分)。【参考方案2】:

@mgilson 提供了一个很好的不需要额外零件的解决方案 (+1)。我看到 pandas 也被标记了,所以为了完整起见,我将给出一个 pandas 等效项:

import pandas as pd

df = pd.read_csv("merge.csv",delimiter=r"\s*")

key_cols = ["col1", "col2"]
df[key_cols] = df[key_cols].apply(sorted, axis=1)

def join_strings(seq, key):
    vals = [term for entry in seq for term in entry.split(',')]
    return ','.join(sorted(set(vals), key=key))

new_df = df.groupby(key_cols).agg("col3": lambda x: join_strings(x, int),
                                   "col4": lambda x: join_strings(x, str))
new_df.to_csv("postmerged.csv")

产生

In [173]: !cat postmerged.csv
col1,col2,col3,col4
a,a,4,genetic
a,c,"1,2,3","genetic,physical"
b,c,22,physical
b,d,"33,44",genetic
c,e,"1,2","genetic,physical,xxxxx"
c,f,"3,33,44","genetic,physical"

所有这些都是 (1) 对前两列进行排序,使 e c 变为 c e,(2) 按 colcol 2 对术语进行分组,然后聚合 (agg) @987654330 @ 和 col4 通过逗号连接扁平术语的排序集。

groupby 对于这样的事情真的很方便。 join_strings 函数的内置替代品可能也潜伏在某处,但我不确定。

【讨论】:

以上是关于基于两列中的值合并其他列中的值的主要内容,如果未能解决你的问题,请参考以下文章

基于可互换出现在两列中的值聚合数据?

然后匹配两列中的值,然后基于R中返回的新值

从 SQLITE 中的两列中选择不同的值

使用pandas创建稀疏矩阵,并使用来自.dat文件的其他两列的索引[x,y]的.dat文件的一列中的值填充它

如何选择在两列中具有相同值集的行,从而连接第三列中的值?

比较两列中的值