【发布时间】:2016-06-02 23:55:01
【问题描述】:
我正在尝试对 CSV 中的记录进行重复数据删除。我不认为自己是 Python 新手或编写 ETL 脚本的新手。我已经尽职尽责并搜索了 S.O.页面,并且不要认为这个问题可以通过使用 SET 来解决(就像大多数重复数据删除问题一样)。
我的目标是:对于所有ORIG等于前一行ORIG的行,在ORIG相等的两行中,删除SEQ_TIME == 0的那一行。
正如 Python 格言所说,“应该有一种——最好只有一种——明显的方式来做到这一点。”我编写的代码我相信可以实现这一点,但任何人都会告诉你这是非常不符合 Pythonic 的。 CSV 数据看起来像这样,下面是我的单独结果 CSV。满足条件的行以黄色突出显示,便于比较。
CSV 文本格式的数据:
TRAIN#,SEQ#,ORIG,DEP,DEST,ARR,SEQ_TIME
A21,9,BPK,0.582986111,X66,0.584375,2
A21,10,X66,0.584375,CNLEMOYN,0.586805556,3.5
A21,11,CNLEMOYN,0.586805556,SMT,0.590972222,6
A21,12,SMT,0.590972222,,0.590972222,0
A21,13,SMT,0.590972222,CNCANAL,0.591666667,1
A21,14,CNCANAL,0.591666667,MEWILSPR,0.594791667,4.5
A21,15,MEWILSPR,0.594791667,,0.594791667,0
A21,16,MEWILSPR,0.594791667,MELEMONT,0.6,7.5
A21,17,MELEMONT,0.6,,0.6,6.5
A21,18,MELEMONT,0.6,MELOCKPO,0.605208333,0
A21,19,MELOCKPO,0.605208333,,0.605208333,0
A21,20,MELOCKPO,0.605208333,XUD,0.60625,2.5
A21,21,XUD,0.60625,JOL,0.607638889,2
我认为实现目标的(非 Pythonic)代码如下。
import csv
f = open("my_data.csv", "r")
reader = csv.reader(f, lineterminator="\n")
header = reader.next()
# Dict comprehension so we can refer to each column by index or name.
hdict = {value:index for index, value in enumerate(header)}
# Data is converted to a 2-D list, since I do other stuff with it later.
data = [row for row in reader]
# Main (un-pythonic) solution.
result = []
try:
i = 0
while True:
row1 = data[i]
row2 = data[i+1] # Will cause an IndexError on the last row.
if row1[hdict["ORIG"]] == row2[hdict["ORIG"]]:
if float(row1[hdict["SEQ_TIME"]]):
result.append(row1)
elif float(row2[hdict["SEQ_TIME"]]):
result.append(row2)
else:
raise AssertionError("Two sequential rows with equivalent ORIG cannot both have SEQ_TIME == 0.")
i += 1 # Force-skips to row3 in the next iteration, since row1 & row2 are handled above.
else:
result.append(row1)
i += 1 # I'm brute-forcing a loop with a manual index.
except IndexError:
result.append(data[-1]) # Handle the last row.
# Write results to some other CSV.
g = open("my_results.csv", "w")
writer = csv.writer(g, lineterminator="\n")
writer.writerow(header)
for row in result:
writer.writerow(result)
f.close()
g.close()
虽然 Python 中的 while True: break 习语很常见并且(我相信)编码草率,但 try: while True: go on forever: except IndexError 习语确实很糟糕。有没有更简单、更优雅的方式来完成这项任务,比如简单的for 循环?
我追求的一个想法是在for 循环中迭代每一行时使用可迭代对象来控制光标:
data_iterable = iter(data)
for row in data_iterable:
row1 = row[:]
row2 = data_iterable.next() # Controlling the cursor here.
if row1[hdict["ORIG"]] == row2[hdict["ORIG"]]:
if float(row1[hdict["SEQ_TIME"]]):
result.append(row1)
else:
result.append(row2)
# The AssertionError check can be omitted.
else:
result.append(row1) # If nothing unusual...
result.append(row2) # append both rows.
这里的问题是这段代码只处理偶数个重复而错过了奇数个重复。
或者,我们可以遍历数据两次,根据 SEQ# 之类的 ID 标记我们想要保留在 keep_these_rows 列表中的行。然后在第二遍时,只将那些行附加到结果中?但这对我来说似乎同样笨拙,而且必然慢 2 倍。
大家有什么更好的解决方案吗?
注意事项:
-
hdict是结合csv.reader和csv.DictReader功能的简单方法,因此您可以按名称引用行,例如row[hdict["ORIG"]]或索引,例如row[2]。 - 我阅读了@DSM 的一篇文章,其中提到了itertools.GroupBy 函数作为竞争者。这对我们有什么好处吗?
谢谢!
【问题讨论】:
-
为什么not very useful发的是图片而不是文字?
-
这个更适合codereview.stackexchange.com
-
@DSM 添加了 CSV 作为文本,并在 itertools.groupby 帖子中提到了您的姓名。谢谢。
-
@chepner 我想你是对的。我不会在 codereview.stackexchange.com 版块中重新发布此帖子,以免遭到各种成员的“双重海报”报复,但如果有人可以顶撞此帖子,请务必使用。
标签: python list csv duplicates