当使用pandas的read_csv函数时,如何限制分隔符仅为某些制表符?

5
我正在使用read_csv将一些制表符分隔的数据读入pandas Dataframe,但是我在列数据中遇到了制表符,这意味着我不能仅使用"\t"作为分隔符。具体来说,每行的最后一个条目都是一组制表符分隔的可选标签,与[A-Za-z][A-Za-z0-9]:[A-Za-z]:.+匹配。无法保证会有多少个标签或哪些标签存在,并且不同的标签集可能出现在不同的行上。示例数据如下(我的数据中所有空格都是制表符):
C42TMACXX:5:2316:15161:76101    163 1   @<@DFFADDDF:DD  NH:i:1  HI:i:1  AS:i:200    nM:i:0
C42TMACXX:5:2316:15161:76101    83  1   CCCCCACDDDCB@B  NH:i:1  HI:i:1  nM:i:1
C42TMACXX:5:1305:26011:74469    163 1   CCCFFFFFHHHHGJ  NH:i:1  HI:i:1  AS:i:200    nM:i:0

我建议将标签作为单列读取,并通过传递一个排除标签上下文中出现的制表符的正则表达式来实现此目的。根据http://www.rexegg.com/regex-best-trick.html,我编写了以下正则表达式:[A-Za-z][A-Za-z0-9]:[A-Za-z]:[^\t]+\t..:|(\t)。我在在线正则表达式测试器上测试了它,似乎只匹配了我想要的制表符分隔符。但是当我运行时,...
df = pd.read_csv(myfile.txt, sep=r"[A-Za-z][A-Za-z0-9]:[A-Za-z]:[^\t]+\t..:|(\t)", 
                 header=None, engine="python")
print(df)

我得到了如下数据输出结果:
                          0       1    2   3   4   5               6   7   8 \
0  C42TMACXX:5:2316:15161:76101  \t  163  \t   1  \t  @<@DFFADDDF:DD  \t NaN   
1  C42TMACXX:5:2316:15161:76101  \t   83  \t   1  \t  CCCCCACDDDCB@B  \t NaN   
2  C42TMACXX:5:1305:26011:74469  \t  163  \t   1  \t  CCCFFFFFHHHHGJ  \t NaN   

   9    10  11      12  13    14  
0 NaN  i:1  \t     NaN NaN   i:0  
1 NaN  i:1  \t  nM:i:1 NaN  None  
2 NaN  i:1  \t     NaN NaN   i:0  

What I was expecting / want is:

                          0        1  2               3                      4
0  C42TMACXX:5:2316:15161:76101  163  1  @<@DFFADDDF:DD  NH:i:1 HI:i:1 AS:i:200 nM:i:0   
1  C42TMACXX:5:2316:15161:76101  83   1  CCCCCACDDDCB@B  NH:i:1 HI:i:1 nM:i:1   
2  C42TMACXX:5:1305:26011:74469  163  1  CCCFFFFFHHHHGJ  NH:i:1 HI:i:1 AS:i:200 nM:i:0

如何实现这个?

如果相关的话,我正在使用 pandas 0.17.1,并且我的真实数据文件有超过一亿行。


你为什么不能使用read_fwf()函数? - chrisaycock
有一些变宽的列 - 我已经删除了它们以简化示例 - 所以我认为我不能使用read_fwf()。 - kmt
1个回答

1
我快速浏览了pandas文档,似乎用作分隔符的正则表达式不能使用组。
C42TMACXX:5:2316:15161:76101    163 1   @<@DFFADDDF:DD  NH:i:1  HI:i:1  AS:i:200    nM:i:0
C42TMACXX:5:2316:15161:76101    83  1   CCCCCACDDDCB@B  NH:i:1  HI:i:1  nM:i:1
C42TMACXX:5:1305:26011:74469    163 1   CCCFFFFFHHHHGJ  NH:i:1  HI:i:1  AS:i:200    nM:i:0
                              ^    ^  ^                ^           

你只需要匹配前4个制表符,但是如果不使用分组是无法做到的。

一种解决方法是通过使用正向预查和反向预查来隔离所需的\t

以下是应该可行的正则表达式:

(?<=\d)\t(?=\d)|\t(?=[A-Z@<:]{14})|(?<=[A-Z@<:]{14})\t

说明

(?<=\d)\t(?=\d) : 一个由数字 (?<=...) 前导并由数字 (?=...) 后跟的制表符

=> 匹配第1个和第2个制表符

| 或者

\t(?=[A-Z@<:]{14}) : 一个制表符,后跟14个连续字符,这些字符在LETTER,@,<或:集合中出现

=> 匹配第3个制表符

| 或者

(?<=[A-Z@<:]{14})\t:在相同的14个字符集之前出现的制表符

=> 匹配第4个制表符

演示

注意

如果您需要在连续的14个字符模式中允许更多字符,只需将它们添加到集合中即可。


我需要在pandas中进行检查,因为你生成的正则表达式确实将数据拆分出来,但这并不是pandas实际需要的。我会尽快检查它。 - kmt
@smiley 我没有完全理解你的问题。我已经修改了我的答案。希望能有所帮助。 - Stephane Janicaud
太棒了,它完成了任务。谢谢你 - 还有为什么我所做的事情不起作用的解释。 - kmt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接