如何从CSV文件中提取目标行,前一行和后一行?

时间:2018-07-13 21:26:48

标签: python python-2.7 csv enumerate

我一直在尝试找出如何使用for循环和我在python中获得的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]之类的东西。换句话说,我需要与该行匹配的元素。

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

3 个答案:

答案 0 :(得分:2)

我会使用其他方法:

  • 将上一行存储在循环中
  • 如果匹配,则使用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例外)

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

类似的东西:

bisect

如果您只需要执行1个搜索,则速度会较慢,因为您仍然必须创建列表,因此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)] 。但是,如果您想在同一列表中执行几次时间搜索,则每次搜索的费用为O(n) + O(log(n))

答案 1 :(得分:1)

您可以为此使用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)

答案 2 :(得分:1)

上述替代方法(功能性方法)是使用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列表的前面和后面分别添加两个无。

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

[编辑]

根据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