【问题标题】:Change information in a CSV file using info from the first one in python使用 python 中第一个文件中的信息更改 CSV 文件中的信息
【发布时间】:2016-02-08 17:17:16
【问题描述】:

我正在尝试使用第一个文件中的信息编辑 CSV 文件。这对我来说似乎并不简单,因为我应该过滤多个东西。让我们解释一下我的问题。

我有两个 CSV 文件,比如说 patch.csv 和 origin.csv。输出 csv 文件应与 origin.csv 具有相同的模式,但具有更正的值。

如果origin.csv 行中的direction_id 字段为0,我想使用patch.csv 中的forward_line_name 列替换origin.csv 中的trip_headsign 列字段,或者如果direction_id 为1,则使用backward_line_name。

只有当 patch.csv 中“:”和“:”符号之间的 line_id 值部分与 origin.csv 中“:”符号之前的 route_id 值部分相同时,我才想这样做。

我知道如何替换整行,但不仅是某些部分,尤其是有时我只需要查看值的一部分。

这是 origin.csv 的示例:

route_id,service_id,trip_id,trip_headsign,direction_id,block_id

210210109:001,2913,70405957139549,70405957,0,
210210109:001,2916,70405961139553,70405961,1,

还有一个 patch.csv 样本:

line_id,line_code,line_name,forward_line_name,forward_direction,backward_line_name,backward_direction,line_color,line_sort,network_id,commercial_mode_id,contributor_id,geometry_id,line_opening_time,line_closing_time

OIF:100110010:10OIF439,10,Boulogne Pont de Saint-Cloud - Gare d'Austerlitz,BOULOGNE / PONT DE ST CLOUD - GARE D'AUSTERLITZ,OIF:SA:8754700,GARE D'AUSTERLITZ - BOULOGNE / PONT DE ST CLOUD,OIF:SA:59400,DFB039,91,OIF:439,metro,OIF,geometry:line:100110010:10,05:30:00,25:47:00
OIF:210210109:001OIF30,001,FFOURCHES LONGUEVILLE PROVINS,Place Mérot - GARE DE LONGUEVILLE,,GARE DE LONGUEVILLE - Place Mérot,OIF:SA:63:49,000000   1,OIF:30,bus,OIF,,05:39:00,19:50:00

每个文件都有数百行我需要以这种方式解析和编辑。

根据 mhopeng 的回答,我得到了那个代码:

#!/usr/bin/env python2
from __future__ import print_function
import fileinput
import sys

# first get the route info from patch.csv
f = open(sys.argv[1])
d = open(sys.argv[2])
# ignore header line
#line1 = f.readline()
#line2 = d.readline()
# get line of data
for line1 in f.readline():
    line1 = f.readline().split(',')
    route_id = line1[0].split(':')[1] # '210210109'
    route_forward = line1[3]
    route_backward = line1[5]
    line_code = line1[1]

# process origin.csv and replace lines in-place
    for line in fileinput.input(sys.argv[2], inplace=1):
        line2 = d.readline().split(',')
        num_route = line2[0].split(':')[0]
# prevent lines with same route_id but different code to be considered as the same line 
        if line.startswith(route_id) and (num_route == line_code):
        if line.startswith(route_id):
            newline = line.split(',')
            if newline[4] == 0:
                newline[3] = route_backward
            else:
                newline[3] = route_forward
            print('\t'.join(newline),end="")
        else:
            print(line,end="")

但不幸的是,这不会在trip_headsign(总是向前)中推动正确的向前或向后的线名,并最终在完成文件解析之前触发该错误:

Traceback(最近一次调用最后一次): 文件“./GTFS_enhancer_headsigns.py”,第 28 行,在 如果换行[4] == 0: IndexError: 列表索引超出范围

感谢您对此的帮助。

【问题讨论】:

  • 如果您编辑问题以包含两个输入 csv 文件的示例以及输出 csv 文件的外观,将会有所帮助。
  • 刚刚使用来自两个 csv 文件的样本进行了编辑,这里我要比较的 line_id 部分是 100110010,我想将它与 210210109 route_id 部分进行比较。如果两者相同,我想执行更改。
  • 你确定标题行和标题后的空白行没有引起问题吗?如果您正在读取空行,则换行符[4] 将不存在。
  • 原始示例文件 sn-ps 也是制表符分隔的(并且在标题后没有此空白行)。所以别忘了把所有的'\t'改成','
  • 您的代码看起来像是将一个文件的每一行与另一个文件中的一行(具有相同行号的行)进行比较。这是你的意图吗?如果是这样,这在问题陈述中并不明显。或者patch.csv中的任何一行都可以用来修改origin.csv中的任何一行吗?

标签: python csv


【解决方案1】:

pandas 便于处理 csv 文件。我会使用这样的东西:

import pandas as pd

origin = pd.read_csv('origin.csv',index_col=None)
patch  = pd.read_csv('patch.csv', index_col=None)

# Create match_keys for matching origin.csv from patch.line_id

patch['match_key'] = [x.split(':')[1] for x in patch.line_id.values]
origin['match_key'] = [x.split(':')[0] for x in origin.route_id.values]

for i,key in enumerate(origin.match_key.values):
    p = patch[patch.match_key == key]
    if len(p) == 1:
        if (origin.direction_id[i] == 0):
            origin.trip_headsign[i] = p.forward_line_name.values[0]
        elif (origin.direction_id[i] == 1):
            origin.trip_headsign[i] = p.backward_line_name.values[0]

origin.to_csv('new_origin.csv',index=False)

【讨论】:

    【解决方案2】:

    您可以使用标准库中的fileinput 模块对文件进行就地编辑。像这样的:

    from __future__ import print_function
    import fileinput
    
    # first get the route info from patch.csv
    f = open('patch.csv')
    # discard header line
    line = f.readline()
    # get line of data
    line = f.readline().split('\t')
    route_id = line[0].split(':')[1] # '210210109'
    route_forward = line[3]
    route_backward = line[5]
    f.close()
    
    # process origin.csv and replace lines in-place
    for line in fileinput.input('origin.csv', inplace=1):
        if line.startswith(route_id):
            newline = line.split('\t')
            if newline[4] == 0:
                newline[3] = route_backward
            else:
                newline[3] = route_forward
            print('\t'.join(newline),end="")
        else:
            print(line,end="")
    

    【讨论】:

    • mhopeng,感谢您的帮助。运行此脚本时出现此错误: Traceback (last recent call last): File "./GTFS_enhancer_headsigns.py", line 13, in route_forward = line[3] IndexError: list index out of range
    • 好的,我找到了问题所在并修复了它。现在,我需要解析和替换 origin.csv 中的所有行与 patch.csv 中的所有行。我该怎么做?
    【解决方案3】:

    我将littletable 模块编写为一个占用空间小的 Python 模块,它可以轻松处理表格数据,而无需使用 pandas 之类的重量级工具。 littletable表格可以导入/导出CSV数据,以及各种表格方法。

    首先将数据加载到表格中:

    import littletable as lt
    
    # Actual script would import from CSV files - for example, just use given sample data
    # origin = lt.Table().csv_import("origin.csv")
    # patch = lt.Table().csv_import("patch.csv")
    
    origin = lt.Table().csv_import("""\
    route_id,service_id,trip_id,trip_headsign,direction_id,block_id
    210210109:001,2913,70405957139549,70405957,0,
    210210109:001,2916,70405961139553,70405961,1,
    310210109:001,2913,70405957139549,70405957,0,
    """)
    
    patch = lt.Table().csv_import("""\
    line_id,line_code,line_name,forward_line_name,forward_direction,backward_line_name,backward_direction,line_color,line_sort,network_id,commercial_mode_id,contributor_id,geometry_id,line_opening_time,line_closing_time
    OIF:100110010:10OIF439,10,Boulogne Pont de Saint-Cloud - Gare d'Austerlitz,BOULOGNE / PONT DE ST CLOUD - GARE D'AUSTERLITZ,OIF:SA:8754700,GARE D'AUSTERLITZ - BOULOGNE / PONT DE ST CLOUD,OIF:SA:59400,DFB039,91,OIF:439,metro,OIF,geometry:line:100110010:10,05:30:00,25:47:00
    OIF:210210109:001OIF30,001,FFOURCHES LONGUEVILLE PROVINS,Place Mérot - GARE DE LONGUEVILLE,,GARE DE LONGUEVILLE - Place Mérot,OIF:SA:63:49,000000   1,OIF:30,bus,OIF,,05:39:00,19:50:00
    """)
    

    添加字段以通过 route_key 进行查找,并为其添加唯一索引

    patch.add_field("route_key", lambda rec: rec.line_id.split(":")[1])
    patch.create_index("route_key", unique=True)
    

    显示“之前”原始表(使用 rich 模块)

    origin("initial data").present()
    

    看起来像这样:

                                          initial data
    
      Route_Id        Service_Id   Trip_Id          Trip_Headsign   Direction_Id   Block_Id 
     ───────────────────────────────────────────────────────────────────────────────────────
      210210109:001   2913         70405957139549   70405957        0
      210210109:001   2916         70405961139553   70405961        1
      310210109:001   2913         70405957139549   70405957        0
    

    现在遍历 origin,从 route_id 中提取路由键,并从补丁表中获取数据,以 route_key 为键:

    for line_no, rec in enumerate(origin, start=1):
        route_key = rec.route_id.split(":")[0]
        if route_key not in patch.by.route_key:
            print("no such route key {!r} in patch.csv (origin.csv/{})".format(route_key, line_no))
            continue
    
        patch_rec = patch.by.route_key[route_key]
        if rec.direction_id == "0":
            rec.trip_headsign = patch_rec.forward_line_name
        else:
            rec.trip_headsign = patch_rec.backward_line_name
    

    我在 origin 中添加了一条带有无效 route_key 的行,我们看到了这条消息:

    no such route key '310210109' in patch.csv (origin.csv/3)
    

    显示更新的表格:

    origin("updated data").present()
    

    现在看起来像:

                                                    updated data
    
      Route_Id        Service_Id   Trip_Id          Trip_Headsign                       Direction_Id   Block_Id 
     ───────────────────────────────────────────────────────────────────────────────────────────────────────────
      210210109:001   2913         70405957139549   Place Mérot - GARE DE LONGUEVILLE   0
      210210109:001   2916         70405961139553   GARE DE LONGUEVILLE - Place Mérot   1
      310210109:001   2913         70405957139549   70405957                            0
    

    现在将修改后的原始记录保存到新的 CSV 文件(或直接覆盖 origin.csv)

    origin.csv_export("updated_origin.csv")
    

    或者在测试期间使用 StringIO 捕获为字符串

    import io
    example = io.StringIO()
    origin.csv_export(example)
    print(example.getvalue())
    

    给予:

    route_id,service_id,trip_id,trip_headsign,direction_id,block_id
    210210109:001,2913,70405957139549,Place Mérot - GARE DE LONGUEVILLE,0,
    210210109:001,2916,70405961139553,GARE DE LONGUEVILLE - Place Mérot,1,
    310210109:001,2913,70405957139549,70405957,0,
    

    【讨论】:

      猜你喜欢
      • 2021-10-19
      • 2022-10-14
      • 1970-01-01
      • 2019-07-15
      • 1970-01-01
      • 1970-01-01
      • 2022-10-25
      • 1970-01-01
      • 2016-12-29
      相关资源
      最近更新 更多