用正则表达式或常规Python替换字符串?

时间:2018-01-27 13:58:10

标签: python regex python-3.x pandas csv

我有一个字符串列表,如下所示

orig = ["a1  2.3  ABC  4  DEFG  567  b890",
        "a2  3.0  HI  4  5  JKL  67  c65",
        "b1  1.2  MNOP  3  45  67  89  QR  987  d64  e112"]

这里的上下文是这是一个CSV文件,省略了某些列。我不认为大熊猫csv阅读器可以处理这些情况。现在的想法是为缺失值注入na,因此输出变为

corr = ["a1  2.3  ABC  4  na  na  na  DEFG  567  b890",
        "a2  3.0  HI  4  5  na  na  JKL  67  c65",
        "b1  1.2  MNOP  3  45  67  89  QR  987  d64  e112"]
当pandas导入时,

稍后将第二列与大写单词对齐。

结构如下:列之间的分隔符是两个或更多个空格,两个大写列之间的分隔符必须是四个值。在原始文件中,始终只有两个大写列,它们之间至少有一个和最多四个数字,这些大写单词之间只有数字值。
我可以在本机Python中编写没有问题的脚本,所以请不要这样做。但我想,这可能是正则表达式的一个例子。作为一个正则表达式初学者,我只设法用

提取两个大写列之间的字符串
for line in orig:
    a = re.findall("([A-Z]+[\s\d]+[A-Z]+)", line))
    print(a)
>>>'ABC  4  DEFG' #etc pp

现在有一种简单的方法可以在正则表达式中确定大写单词和插入单词之间有多少个数字。值之间总是有四个值?或者我应该在原生Python中做到这一点?

当然,如果有办法用pandas csv阅读器做到这一点,那就更好了。但我研究pandas csv_reader docs并且没有发现任何有用的东西。

2 个答案:

答案 0 :(得分:2)

基于完整的大熊猫方法,分裂和连接可能会有所帮助,即

ndf = pd.Series(orig).str.split(expand=True)

#   0    1     2  3     4    5     6     7     8     9     10
#0  a1  2.3   ABC  4  DEFG  567  b890  None  None  None  None
#1  a2  3.0    HI  4     5  JKL    67   c65  None  None  None
#2  b1  1.2  MNOP  3    45   67    89    QR   987   d64  e112

df = pd.concat([ndf.iloc[:,:4], ndf.iloc[:,4:].apply(sorted,key=pd.notnull,axis=1)],1)

df.astype(str).apply(' '.join,axis=1).tolist()

['a1 2.3 ABC 4 None None None None DEFG 567 b890',
 'a2 3.0 HI 4 None None None 5 JKL 67 c65',
 'b1 1.2 MNOP 3 45 67 89 QR 987 d64 e112']

答案 1 :(得分:0)

虽然共识似乎是正则表达式不是这种动态字符串替换的最佳工具,但我发现re模块在​​这种情况下使用起来相当舒服。捕获模式基于Jon Clements的评论。

import re

orig = ["a1  2.3  ABC  4  DEFG  567  b890",
        "a2  3.0  HI  4  5  JKL  67  c65",
        "b1  1.2  MNOP  3  45  67  89  QR  987  d64  e112"]

corr = []

for item in orig:
    #capture group starting with first capitalised word and stopping before the second
    col_betw = re.search("\s{2,}([A-Z]+.*)\s{2,}[A-Z]+\s{2,}", item).group(1)
    #determine, how many elements we have in this segment
    nr_col_betw = len(re.split(r"\s{2,}", col_betw))
    #substitute, if not enough numbers
    if nr_col_betw <= 4:
        #fill with NA, which is interpreted by pandas csv reader as NaN
        subst = col_betw + "   NA" * (5 - nr_col_betw) 
        item = item.replace(col_betw, subst, 1)
    corr.append(item)