【问题标题】:python pandas - dynamic cleaning of 4000 csvs with varying column/rowspython pandas - 动态清理具有不同列/行的 4000 个 csv
【发布时间】:2020-08-17 17:39:20
【问题描述】:

我正在寻找一种方法来清理 4000 个类似格式但具有不同数量的行/列的 csv,然后将它们组合成一个表(可能是 SQLite 超过 400 万条记录)。数据是相关的始发地/目的地 (O/D) 调查 - 每个 csv 是包含多个停靠站到停靠站计数的特定路线和票证类型(例如路线 101 成人、路线 101 孩子 wkend 等)。每个 csv 都采用类似的阶梯式格式,其中 csv 具有与列相同的行数(如果不包括第一行(路线信息)):

route info
stop1, stop1
stop2, value, stop2
stop3, value, value, stop3
stop4, value, value, value, stop4

route info
stop11, stop11
stop32, value, stop32
....
stop150, value, value, value, ......., stop150

Sample raw data

但是,每个 csv 可以有不同/更多/更少的 O/D 组合。数据没有标题,因此很难进入我建议的“中间步骤”。

Intermediary cleaning step - not required if can go directly to final output

Required cleaned output

我才刚刚开始寻找解决方案,但在将数据加载到 Pandas DataFrame 时遇到了问题:

  • CParserError:错误标记数据。 C 错误:第 3 行中应有 2 个字段,看到 3。 (使用 sep = "\t" 更正)
  • 在单列中看到的所有数据(通过首先使用 csv.reader 打开数据以获取列数并为每个列分配一个数字来更正)
    for dirty_csv in csvs_to_be_cleaned:
        print (dirty_csv)
        # open csv to get number of columns so that Pandas can read data
        with open(dirty_csv, 'r') as csvfile:
            reader = csv.reader(csvfile)
            # subtract 1 from length to get actual number of columns
            # first row contains route/ticket info (which will be populated in 2 new fields)
            col_range = len(list(reader)) - 1
        default_cols = [str(i) for i in range(col_range)] # create some col names
        df = pd.read_csv(dirty_csv, sep = "\t", delimiter=",", names = default_cols, header = None)
        print(df) 

问题

  • 是否有更优雅的解决方案让 Pandas 查看 csv 中的所有数据,其中数据/标题以阶梯方式呈现
  • Pandas 可以使用列中的第一个字符串条目作为列标题吗?

我想知道是否有类似的解决方案或有人愿意提供帮助。

python 模块/进程:

  1. glob 清理所有 csvs
  2. 使用 csv/pandas 处理每个 csv 个体(完整的解决方案未写入 atm)
  3. 将清理后的 csv 输出到新文件夹
  4. 全部合并到单个 SQLite 表中

【问题讨论】:

    标签: python pandas csv


    【解决方案1】:

    这是重新格式化源/目标文件的一种方法。首先,创建一个样本数据集。

    from io import StringIO
    import pandas as pd
    
    data = '''
    route r1
    dest 1, Origin 1
    dest 2,        a, Origin 2
    dest 3,        b,        d, Origin 3
    dest 4,        c,        e,        f, Origin 4
    '''
    

    其次,解析文件。

    with StringIO(data) as handle:
        # get the route (first non-blank line)
        while True:
            line = next(handle).rstrip('\n')
            if line:
                break
        route = line
        
        origins = list()
        bus_trips = list()
        
        for line in handle:
            fields = line.rstrip().split(',')
            fields = [f.strip() for f in fields]
            
            destination = fields[0]
            origins.append(fields[-1])
            
            for origin, count in zip(origins[:-1], fields[1:]):
                t = (route, origin, destination, count)
                bus_trips.append(t)
                
    

    bus_trips 是一个元组列表。您可以将其转换为 pandas 数据框,并使用 pandas .to_sql() 方法写入数据库。

    [('route r1', 'Origin 1', 'dest 2', 'a'),
     ('route r1', 'Origin 1', 'dest 3', 'b'),
     ('route r1', 'Origin 2', 'dest 3', 'd'),
     ('route r1', 'Origin 1', 'dest 4', 'c'),
     ('route r1', 'Origin 2', 'dest 4', 'e'),
     ('route r1', 'Origin 3', 'dest 4', 'f')]
    

    现在,创建数据框很简单,因为我们将“阶梯”格式更改为矩形格式。

    col_names = ['route', 'origin', 'destination', 'ride_count']
    df = pd.DataFrame.from_records(bus_trips, columns=col_names)
    print(df)
    
          route    origin destination ride_count
    0  route r1  Origin 1      dest 2          a
    1  route r1  Origin 1      dest 3          b
    2  route r1  Origin 2      dest 3          d
    3  route r1  Origin 1      dest 4          c
    4  route r1  Origin 2      dest 4          e
    5  route r1  Origin 3      dest 4          f
    

    【讨论】:

    • 非常感谢@jsmart。一个非常好的解决方案 - 我急于将数据放入 DF 并在那里做魔术,但事后看来是不需要的。 fields[0]fields[-1] 的使用很棒。我打算生成一个函数来获取不是 NaN 的最后一行索引 - if x.last_valid_index() is None: return np.nan else: return x[x.last_valid_index()].strip()
    • 太棒了!我的方法也是尽可能快地到达 pandas,但有时我们需要预处理。仅供参考:json_normalize() 一个很好的 pandas 函数,用于嵌套 JSON pandas.pydata.org/pandas-docs/stable/reference/api/…
    猜你喜欢
    • 2019-04-07
    • 1970-01-01
    • 2021-10-04
    • 2021-07-14
    • 1970-01-01
    • 2017-02-02
    • 2021-09-29
    • 2019-10-29
    • 2015-06-08
    相关资源
    最近更新 更多