如何在python中更快地拆分列?

时间:2017-10-23 05:36:30

标签: python bash pandas awk

我有一个这样的文件:

NODE_1_length_4014_cov_1.97676  1   1
NODE_1_length_4014_cov_1.97676  2   1
NODE_1_length_4014_cov_1.97676  3   1
NODE_1_length_4014_cov_1.97676  4   1
NODE_1_length_4014_cov_1.97676  5   1
NODE_1_length_4014_cov_1.97676  6   1
NODE_1_length_4014_cov_1.97676  7   1
NODE_1_length_4014_cov_1.97676  8   1
NODE_1_length_4014_cov_1.97676  9   1
NODE_1_length_4014_cov_1.97676  10  1

我想根据字符' _'将第一列拆分为新列,如下所示:

1 4014 1.97676  1   1
1 4014 1.97676  2   1
1 4014 1.97676  3   1
1 4014 1.97676  4   1
1 4014 1.97676  5   1
1 4014 1.97676  6   1
1 4014 1.97676  7   1
1 4014 1.97676  8   1
1 4014 1.97676  9   1
1 4014 1.97676  10  1

我知道我可以在熊猫中做到这一点:

df.columns = ['contig','loci','depth']
df['node'] =df.contig.str.split(r'_').str[1]
df['len'] =df.contig.str.split(r'_').str[3]
df['cov'] =df.contig.str.split(r'_').str[5]
df.drop(['contig'], axis=1, inplace=True)

但是我的文件太大(> 2G)并且我的计算机上需要永远做这件事。有没有其他更快的方法来做到这一点?根据我的经验,Pandas通常很慢处理大文件,虽然它很容易使用。

非常感谢。

5 个答案:

答案 0 :(得分:1)

使用awk

$ awk -F'_' '{print $2, $4,$6,$7,$8}' infile
1 4014 1.97676  1   1  
1 4014 1.97676  2   1  
1 4014 1.97676  3   1  
1 4014 1.97676  4   1  
1 4014 1.97676  5   1  
1 4014 1.97676  6   1  
1 4014 1.97676  7   1  
1 4014 1.97676  8   1  
1 4014 1.97676  9   1  
1 4014 1.97676  10  1  

答案 1 :(得分:1)

您也可以使用cut

$ cut -d_ --output-delimiter=$'\t' -f2,4,6 ip.txt
1   4014    1.97676  1   1
1   4014    1.97676  2   1
1   4014    1.97676  3   1
1   4014    1.97676  4   1
1   4014    1.97676  5   1
1   4014    1.97676  6   1
1   4014    1.97676  7   1
1   4014    1.97676  8   1
1   4014    1.97676  9   1
1   4014    1.97676  10  1
  • -d选项允许指定输入分隔符
  • --output-delimiter=$'\t'指定输出分隔符。注意使用$'\t'作为Tab字符(不确定这是否适用于所有shell,我在bash上进行了测试)


速度比较

$ cut --version | head -n1
cut (GNU coreutils) 8.25
$ awk --version | head -n1
GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)

$ perl -0777 -lne 'print $_ x 1000000' ip.txt > f1
$ du -h f1
363M    f1

$ time cut -d_ --output-delimiter=$'\t' -f2,4,6 f1 > t1

real    0m2.097s
user    0m1.660s
sys     0m0.324s
$ time cut -d_ --output-delimiter=$'\t' -f2,4,6 f1 > t1

real    0m3.267s
user    0m1.612s
sys     0m0.376s

$ time awk -F'_' -v OFS='\t' '{print $2,$4,$6}' f1 > t2

real    0m17.394s
user    0m16.200s
sys     0m0.328s
$ time awk -F'_' -v OFS='\t' '{print $2,$4,$6}' f1 > t2

real    0m16.329s
user    0m15.336s
sys     0m0.432s

$ diff -s t1 t2
Files t1 and t2 are identical

如果输入为ASCII,则awk显示出很多改进

$ time LC_ALL=C awk -F'_' -v OFS='\t' '{print $2,$4,$6}' f1 > t2

real    0m7.783s
user    0m6.832s
sys     0m0.428s

答案 2 :(得分:0)

使用:

df[['node','len','cov']] = df.contig.str.split(r'_', expand=True).iloc[:,[1,3,5]]
df.drop(['contig'], axis=1, inplace=True)
print (df)
   loci  depth node   len      cov
0     1      1    1  4014  1.97676
1     2      1    1  4014  1.97676
2     3      1    1  4014  1.97676
3     4      1    1  4014  1.97676
4     5      1    1  4014  1.97676
5     6      1    1  4014  1.97676
6     7      1    1  4014  1.97676
7     8      1    1  4014  1.97676
8     9      1    1  4014  1.97676
9    10      1    1  4014  1.97676

如果没有NaN s:

df[['node','len','cov']] = pd.DataFrame([x.split('_') for x in df['contig'].values.tolist()])
                             .iloc[:,[1,3,5]]
df.drop(['contig'], axis=1, inplace=True)
print (df)
   loci  depth node   len      cov
0     1      1    1  4014  1.97676
1     2      1    1  4014  1.97676
2     3      1    1  4014  1.97676
3     4      1    1  4014  1.97676
4     5      1    1  4014  1.97676
5     6      1    1  4014  1.97676
6     7      1    1  4014  1.97676
7     8      1    1  4014  1.97676
8     9      1    1  4014  1.97676
9    10      1    1  4014  1.97676

答案 3 :(得分:0)

使用Bash:

struct foo

答案 4 :(得分:0)

也很好。

sed -E 's/[^0-9.]+/\t/g;s/\t//' infile