使用python和pandas将CSV文件中的三列合并为一列

使用python和pandas将CSV文件中的三列合并为一列,python,pandas,csv,dataframe,Python,Pandas,Csv,Dataframe,嗨,我正在尝试将几个现有列合并成一个新列,然后删除CSV文件中的三个原始列。我一直试图用熊猫来做这件事,但是运气不太好。我对python相当陌生 我的代码首先将几个CSV文件合并到同一个目录中,然后尝试操作列。第一次合并有效,我得到一个带有合并数据的output.csv,但是列的合并无效 import glob import pandas as pd interesting_files = glob.glob("*.csv") header_saved = False with open('

嗨,我正在尝试将几个现有列合并成一个新列,然后删除CSV文件中的三个原始列。我一直试图用熊猫来做这件事,但是运气不太好。我对python相当陌生

我的代码首先将几个CSV文件合并到同一个目录中,然后尝试操作列。第一次合并有效,我得到一个带有合并数据的output.csv,但是列的合并无效

import glob
import pandas as pd

interesting_files = glob.glob("*.csv")

header_saved = False
with open('output.csv','wb') as fout:
    for filename in interesting_files:
        with open(filename) as fin:
            header = next(fin)
            if not header_saved:
                fout.write(header)
                header_saved = True
            for line in fin:
                fout.write(line)

df = pd.read_csv("output.csv")
df['HostAffected']=df['Host'] + "/" + df['Protocol'] + "/" + df['Port']
df.to_csv("newoutput.csv")
有效地扭转这一局面:

Host,Protocol,Port
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,49707
10.0.0.10,tcp,49672
10.0.0.10,tcp,49670
变成这样:

HostsAffected
10.0.0.10/tcp/445
10.0.0.10/tcp/445
10.0.0.10/tcp/445
10.0.0.10/tcp/445
10.0.0.10/tcp/445
10.0.0.10/tcp/445
10.0.0.11/tcp/445
10.0.0.11/tcp/49707
10.0.0.11/tcp/49672
10.0.0.11/tcp/49670
10.0.0.11/tcp/49668
10.0.0.11/tcp/49667
但是,csv中还有其他列


我不是编码员,我只是想解决一个问题,非常感谢您的帮助。

这就是您可以做到的:

    dt = """Host,Protocol,Port
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,49707
10.0.0.10,tcp,49672
10.0.0.10,tcp,49670"""

tdf = pd.read_csv(pd.compat.StringIO(dt))
tdf['HostsAffected'] = tdf.apply(lambda x: '{}/{}/{}'.format(x['Host'] , x['Protocol'] , x['Port']), axis=1)
tdf = tdf[['HostsAffected']]
tdf.to_csv(<path-to-save-csv-file>)
如果要从文件中读取CSV,请按如下方式编辑读取CSV行:

tdf = pd.read_csv(<path-to-the-file>)
tdf=pd.read\u csv()

有两种方法可以做到这一点:要么使用矢量化函数组合系列,要么使用
lambda
函数与
pd.series.apply

矢量化解决方案

df['HostsAffected'] = df.apply(lambda x: '/'.join(list(map(str, x))), axis=1)
不要忘记将非数字类型转换为
str

df['HostAffected'] = df['Host'] + '/' + df['Protocol'] + '/' + df['Port'].map(str)
业绩说明:

应用
lambda
功能

df['HostsAffected'] = df.apply(lambda x: '/'.join(list(map(str, x))), axis=1)
对于这两种解决方案,您只需按此列筛选即可删除所有其他解决方案:

df = df[['HostsAffected']]
完整示例

from io import StringIO
import pandas as pd

mystr = StringIO("""Host,Protocol,Port
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,445
10.0.0.10,tcp,49707
10.0.0.10,tcp,49672
10.0.0.10,tcp,49670""")

# replace mystr with 'file.csv'
df = pd.read_csv(mystr)

# combine columns
df['HostsAffected'] = df['Host'] + '/' + df['Protocol'] + '/' + df['Port'].map(str)

# include only new columns
df = df[['HostsAffected']]
结果:

print(df)

         HostsAffected
0    10.0.0.10/tcp/445
1    10.0.0.10/tcp/445
2    10.0.0.10/tcp/445
3    10.0.0.10/tcp/445
4    10.0.0.10/tcp/445
5    10.0.0.10/tcp/445
6    10.0.0.10/tcp/445
7  10.0.0.10/tcp/49707
8  10.0.0.10/tcp/49672
9  10.0.0.10/tcp/49670

在我看来,我们有三种选择:

%timeit df['Host'] + "/" + df['Protocol'] + "/" + df['Port'].map(str)
%timeit ['/'.join(i) for i in zip(df['Host'],df['Protocol'],df['Port'].map(str))]
%timeit ['/'.join(i) for i in df[['Host','Protocol','Port']].astype(str).values]
计时

10 loops, best of 3: 39.7 ms per loop  
10 loops, best of 3: 35.9 ms per loop  
10 loops, best of 3: 162 ms per loop
然而,我认为这是你最容易理解的方法:

import pandas as pd

data = '''\
ID,Host,Protocol,Port
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,49707
1,10.0.0.10,tcp,49672
1,10.0.0.10,tcp,49670'''

df = pd.read_csv(pd.compat.StringIO(data)) # Recreates a sample dataframe

cols = ['Host','Protocol','Port']
newcol = ['/'.join(i) for i in df[cols].astype(str).values]
df = df.assign(HostAffected=newcol).drop(cols, 1)
print(df)
返回:

   ID         HostAffected
0   1    10.0.0.10/tcp/445
1   1    10.0.0.10/tcp/445
2   1    10.0.0.10/tcp/445
3   1    10.0.0.10/tcp/445
4   1    10.0.0.10/tcp/445
5   1    10.0.0.10/tcp/445
6   1    10.0.0.10/tcp/445
7   1  10.0.0.10/tcp/49707
8   1  10.0.0.10/tcp/49672
9   1  10.0.0.10/tcp/49670

很抱歉,问题出在哪里?可能是复制品,但解决速度很慢,最好是原创的-看你是对的。我听到很多人抱怨
apply
@jezrael不应该这样:
df['hostimpacted']=['/')。在zip中加入(I)for I(df['Host']、df['Protocol']、df['Port'].astype(str))
是最快的吗?@AntonvBR-最好的测试,不确定。@AntonvBR nice one antov.谢谢你在这方面的帮助。将.map(str)添加到合并列的末尾修复了该问题@jpp好的。现在已经证实了
zip()
应该是最快的解决方案。是的,我同意。谢谢你的更新。更接近,但现在我们有了很好的基准。我也尝试过这种方法,效果很好。谢谢。@Anton vBR如果这些字段中有一个为空,脚本就会停止-有办法解决吗?例如,如果tcp在任何给定的行中丢失,它将在哪里停止,因为它应该跳过并合并ip和端口。@Abob在
'
中为空?