【问题标题】:Pandas read csv with regex separatorPandas 使用正则表达式分隔符读取 csv
【发布时间】:2020-04-10 23:19:30
【问题描述】:

我一直在尝试读取这样的自定义 csv 文件:

6 Rotterdam NLD Zuid-Holland 593321 
19 Zaanstad NLD Noord-Holland 135621 
214 Porto Alegre BRA Rio Grande do Sul 1314032 
397 Lauro de Freitas BRA Bahia 109236 
547 Dobric BGR Varna 100399 
552 Bujumbura BDI Bujumbura 300000 
554 Santiago de Chile CHL Santiago 4703954 
626 al-Minya EGY al-Minya 201360 
646 Santa Ana SLV Santa Ana 139389 
762 Bahir Dar ETH Amhara 96140 
123 Chicago 10000 
222 New York 200000  

我在https://regex101.com/ 中尝试了正则表达式 以下代码有效:

这行得通

# https://regex101.com/
s = "6 Rotterdam NLD Zuid-Holland 593321 "
pat = r'(\d+)\s+([\D]+)\s(\d+)\s+'

m = re.match(pat,s)
m.groups() # ('6', 'Rotterdam NLD Zuid-Holland', '593321')

我得到了正确答案,但是当我将代码应用到 pandas read_csv 时,不知何故它无法工作。

我的尝试

import numpy as np
import pandas as pd
from io import StringIO

s = """6 Rotterdam NLD Zuid-Holland 593321 
19 Zaanstad NLD Noord-Holland 135621 
214 Porto Alegre BRA Rio Grande do Sul 1314032 
397 Lauro de Freitas BRA Bahia 109236 
547 Dobric BGR Varna 100399 
552 Bujumbura BDI Bujumbura 300000 
554 Santiago de Chile CHL Santiago 4703954 
626 al-Minya EGY al-Minya 201360 
646 Santa Ana SLV Santa Ana 139389 
762 Bahir Dar ETH Amhara 96140 
123 Chicago 10000 
222 New York 200000  """;

sep = r'(\d+)\s+|([\D]+)\s+|(\d+)\s+'
df = pd.read_csv(StringIO(s), sep=sep,engine='python')
df

我得到了很多 Nans,如何只得到 3 列?

Column names are: ID CITY POPULATION

类似问题

【问题讨论】:

  • 您使用了模式来匹配(提取)文本,但在 pandas 方法中,您与模式拆分。每行的开头可以有多少位数字?
  • @WiktorStribiżew 我试过sep = r'(\d+)\s+([\D]+)\s+(\d+)\s+' 它只给出一列。
  • 如果每行只能有 1、2 或 3 位数字,试试sep = r'(?:(?<=^\d)|(?<=^\d{2})|(?<=^\d{3}))\s+|\s+(?=\S+\s*$)'
  • @WiktorStribiżew 这个问题只是我数据集的一部分。现在,它适用于我的完整数据集,如果您作为答案发布,我将很乐意接受。

标签: python regex pandas


【解决方案1】:

您使用该模式来匹配(提取)文本,但在 pandas 方法中,您是 拆分该模式。

如果每行的开头只能有 1、2 或 3 位数字,请使用

sep = r'(?:(?<=^\d)|(?<=^\d{2})|(?<=^\d{3}))\s+|\s+(?=\S+\s*$)'

请参阅regex demo。您可以通过在第一个非捕获组中添加更多后视来扩展它。

详情

  • (?:(?&lt;=^\d)|(?&lt;=^\d{2})|(?&lt;=^\d{3}))\s+ - 1+ 个空格 (\s+),前面有 1 个数字 (\d),或 2 个数字 (\d{2}),或 3 个数字 (\d{3}),位于字符串 (^) 的开头)
  • | - 或
  • \s+(?=\S+\s*$) - 1+ 个空格后跟 1+ 个非空白字符,然后是字符串结尾之前的任何尾随 0+ 个空格。

【讨论】:

    【解决方案2】:

    只是为了提供一个不使用正则表达式的替代解决方案:

    您也可以在纯 Python 中解析文本文件。在某些情况下,这可能比相当复杂的正则表达式更容易维护。

    对于这种特定格式,我们知道每行中的第一个和最后一个数字具有特殊含义。所以我会使用splitrsplit 来挑选它们。

    import pandas as pd
    from io import StringIO
    
    s = """6 Rotterdam NLD Zuid-Holland 593321 
    19 Zaanstad NLD Noord-Holland 135621 
    214 Porto Alegre BRA Rio Grande do Sul 1314032 
    397 Lauro de Freitas BRA Bahia 109236 
    547 Dobric BGR Varna 100399 
    552 Bujumbura BDI Bujumbura 300000 
    554 Santiago de Chile CHL Santiago 4703954 
    626 al-Minya EGY al-Minya 201360 
    646 Santa Ana SLV Santa Ana 139389 
    762 Bahir Dar ETH Amhara 96140 
    123 Chicago 10000 
    222 New York 200000  """
    
    data = []
    for line in StringIO(s):
        line = line.strip()
        if not line:
            continue
        id_value, line = line.split(" ", 1)
        city, population = line.rsplit(" ", 1)
    
        data.append((id_value, city, population))
    
    df = pd.DataFrame(data, columns=["id", "city", "population"])
    df["id"] = pd.to_numeric(df["id"])
    df["population"] = pd.to_numeric(df["population"])
    print(df)
    

    我没有进行任何速度测量。然而,根据文件大小,速度可能根本不是问题,无论如何。但即使是这样:我会使用此脚本首先(并且仅一次)预处理数据,以便能够使用常规的旧 pd.read_csv 而无需额外的参数。

    【讨论】:

      猜你喜欢
      • 2022-10-14
      • 1970-01-01
      • 2017-05-17
      • 2015-06-30
      • 1970-01-01
      • 2019-07-16
      • 2020-09-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多