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

Python 基于两列中的值合并其他列中的值,python,merge,pandas,Python,Merge,Pandas,我有一个以制表符分隔的文件,有四列。我需要为'col1'和'col2'中的每个唯一值对组合'col3'和'col4'。示例和输出如下图所示 我想到的一种方法是使用嵌套循环:外循环按顺序读取行,内循环从一开始读取所有行并查找映射。然而,这个过程似乎是计算密集型的 有没有其他方法可以做到这一点 col1 col2 col3 col4 a c 1,2 physical a c 2,3 genetic b c 22 physical b d 33,4

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

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

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

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”组合起来。

我将创建一个键字典,这些键是保存column1和column2数据的元组。这些值将是一个包含第3列和第4列数据的列表

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)
现在,使用“,”对连接column3和column4列表的输出进行排序和写入,使用set使其唯一,并正确排序

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}\t{1}\t{2}\t{3}\n'.format(col1,col2,col3_str,','.join(col4_data)))

我将创建一个键字典,键是保存column1和column2数据的元组。这些值将是一个包含第3列和第4列数据的列表

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)
现在,使用“,”对连接column3和column4列表的输出进行排序和写入,使用set使其唯一,并正确排序

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}\t{1}\t{2}\t{3}\n'.format(col1,col2,col3_str,','.join(col4_data)))

@mgilson给出了一个很好的无需额外零件的解决方案+1。我看到熊猫也有标签,所以为了完整起见,我将给出一个与熊猫相当的标签:

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对前两列进行排序,使ec变成ce,2按col和col 2对术语进行分组,然后通过逗号连接已排序的扁平术语集来聚合agg col3和col4


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

@mgilson给出了一个很好的无需额外部件的解决方案+1。我看到熊猫也有标签,所以为了完整起见,我将给出一个与熊猫相当的标签:

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对前两列进行排序,使ec变成ce,2按col和col 2对术语进行分组,然后通过逗号连接已排序的扁平术语集来聚合agg col3和col4


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

谢谢@mgilson,但我在回溯最近的调用last:File,第6行,在ValueError:需要超过0个值才能解包时出错。这是因为代码第一部分的第7行。@好奇-我在测试代码之前也知道了。看我的修订。具体来说,它应该是defaultdictlambda:[],[]而不是defaultdictlambda:[],[]谢谢。工作完美如期。有没有一种方法可以忽略col1和col2中的顺序。我在问题中编辑了这个。@好奇-您可以在第一部分中使用col3\u数据,col4\u数据=数据[frozensetcol1,col2]。这使得在第二部分中对键进行排序变得毫无价值。@好奇-请参阅我在这两部分中添加的注释。谢谢@mgilson,但我在回溯最近的调用last:File,第6行,在ValueError:需要超过0个值才能解包时出错。这是因为代码第一部分的第7行。@好奇-我在测试代码之前也知道了。看我的修订。具体来说,它应该是defaultdictlambda:[],[]而不是defaultdictlambda:[],[]谢谢。工作完美如期。有没有一种方法可以忽略col1和col2中的顺序。我在问题中编辑了这个。@好奇-您可以在第一部分中使用col3\u数据,col4\u数据=数据[frozensetcol1,col2]。这使得在第二部分中对键进行排序变得毫无价值。@questival-请参阅我在这两部分中添加的注释。