【问题标题】:How to extract target row, the row before, and the row after from a CSV File?如何从 CSV 文件中提取目标行、前行和后行?
【发布时间】:2018-07-13 21:26:48
【问题描述】:

我一直试图弄清楚如何使用我在 python 中提供的for 循环和enumerate 对象来完成此任务。我有一个HH:MM 格式的时间。我有一个 csv 文件,其中第一列是以相同格式遵循的时间戳。然后我在文件中搜索匹配时间,然后提取该行以稍后转换为 XML 文件。但是,我还需要提取该目标行之前的行和之后的行。我尝试了以下代码:

def findRow(timeID, filename):
    rows = []
    csvFile = csv.reader(open(filename, "rb"), delimiter=",")
    for i, row in enumerate(csvFile):
        if timeID == timeInRow:
            rows.append(i-1)
            rows.append(i)
            rows.append(i+1)
            return rows

但是,不久之后我意识到这不是正确的方法,因为我提取的是索引而不是值。我需要的是类似于 row[i-1],row[i],row[i+1] 的东西。换句话说,我需要与行匹配的 i 元素。

有没有简单的方法来做到这一点?我曾考虑过使用range(csvFile),但老实说我不知道​​最终会做什么。

【问题讨论】:

    标签: python python-2.7 csv enumerate


    【解决方案1】:

    我会使用不同的方法:

    • 将上一行存储在循环中
    • 如果匹配,则使用next获取下一行,并返回3行

    像这样(我添加了一条评论,因为 timeInRow 应该从 row 中提取,但您的代码没有显示它):

    prev_row = []  # just in case it matches at first row
    for row in csvFile:
        # something must be done to extract timeInRow from row here!
        if timeID == timeInRow:
            return [prev_row,row,next(csvFile,[])]
        prev_row = row  # save current row for next iteration
    

    next 使用默认的空列表值,以防 last 行匹配(避免 StopIteration 异常)

    这种线性方法有效,但如果行按时间排序并且您需要执行多次搜索,更好的方法(更快)可能会创建一个行列表,一个时间列表,然后使用 bisect 模块计算时间列表中的插入点,检查时间是否匹配,并使用索引返回行列表的切片。

    类似:

    list_of_rows = list(csvFile)
    list_of_times = [x[3] for x in list_of_rows] # assume that the time is the 4th column here
    i = bisect.bisect(list_of_rows,timeInRow)
    if i < len(list_of_rows) and list_of_rows[i] == timeInRow:
        return list_of_rows[max(i-1,0):min(i+2,len(list_of_rows)]
    

    如果您只需要执行 1 次搜索,这会比较慢,因为无论如何您都必须创建列表,所以 O(n) + O(log(n))。但是如果你想在同一个列表中执行多次搜索,每次搜索的成本是O(log(n))

    【讨论】:

    • timeInRow 是通过我编写的另一个函数提取的。我尝试过这种方法,但收到错误消息TypeError: list object is not an iterator
    • 哎呀修复了我的next 部分,该部分必须适用于csvFile 对象,而不是row :)
    • 抱歉这么晚才回复您,这种方法将如何解决 CSV 文件超出范围的问题?如果匹配最终成为文件中的第一件事或最后一件事,这会中断吗?
    • 不,它不会因为切片将首先停止。好吧,也许有一个极端情况,正在编辑
    • 第一种方法中没有,如果在开始时找到,它将返回上一个的空列表,如果在末尾找到,则返回下一个的空列表。
    【解决方案2】:

    您可以为此使用deque

    给定:

    $ cat /tmp/file.csv
    firstName,lastName,email,phoneNumber
    John,Doe,john@doe.com,0123456789
    Jane,Doe,jane@doe.com,9876543210
    James,Bond,james.bond@mi6.co.uk,0612345678
    

    假设您想要Jane 的行以及之前和之后的行。

    试试:

    import csv 
    from collections import deque 
    
    dq=deque([[None] for _ in range(3)],maxlen=3)
    with open(fn,'r') as f:
        for row in csv.reader(f):
            dq.append(row)
            if dq[-2][0]=='Jane': break # here you can use your custom function 
    

    然后:

     >>> dq
     deque([['John', 'Doe', 'john@doe.com', '0123456789'], ['Jane', 'Doe', 'jane@doe.com', '9876543210'], ['James', 'Bond', 'james.bond@mi6.co.uk', '0612345678']], maxlen=3)
    

    【讨论】:

    • dq.append(row) 是否会将所有内容附加到双端队列并在达到最大长度 3 后将所有内容踢出?
    • @Sailanarmo:是的。无论maxlen=3 的值是多少,都会在一端添加项目,然后在另一端移除项目以保持长度为maxlen。它就像一个滚动窗口进入更长的数据元素。你也可以没有maxlen,因此双端队列的行为更像一个列表(有一些区别,包括两端的性能推送和弹出)
    • 抱歉这么晚才回来,这个方法会不会越界?如果匹配最终成为 CSV 文件中的最后一件事或第一件事怎么办?这会破吗?或者我可以捕捉到异常吗?或者我可以用 null 填充那个条目吗?
    • 如果是第一个,你会得到[[None], [Line 1], [Line 2]] 如果你的匹配是最后一行,你可以在for 循环中添加一个else 并在循环结束时测试该条件。那么双端队列将是最后三行。
    【解决方案3】:

    上述方法的替代(功能)方法是使用zip 或其变体。比如:

    rows = list(csv.reader(f))
    for x, y, z in zip(rows, rows[1:], rows[2:]):
        # y is the middle row, x is above it, and z below it
        pass
    

    如果您想在迭代中包含前两行和最后两行

    (None, None, rows[0])
    (None, rows[0], rows[1])
    (rows[-2], rows[-1], None)
    (rows[-1], None, None)
    

    那么您必须在 rows 列表的两端添加两个 None。

    并不是说这一定比其他答案更好,但这是我会考虑编写的另一种方法。

    [编辑]

    根据 Jean-François 的建议使用 itertools.islice:

    rows = list(csv.reader(f))
    from itertools import islice
    for x, y, z in zip(rows, islice(rows, 1, None), islice(rows, 2, None)):
        # y is the middle row, x is above it, and z below it
        pass
    

    【讨论】:

    • 在这种情况下,最好使用itertools.islice 以避免创建rows 列表的2 个副本。
    • 类似上面的编辑@Jean-FrançoisFabre ?我没有使用过 islice,但对于这个用例来说似乎是合理的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多