如何使用Python按两个列对CSV文件进行排序?

5
我有一个包含6列的csv文件。
我想按第2列,然后按第3列对其进行排序。
我的当前代码正在创建一个空文件:
import csv
with open('original.csv', mode='rt') as f, open('sorted.csv', 'w') as final:
        writer = csv.writer(final, delimiter='\t')
        reader = csv.reader(f, delimiter=',')
        _ = next(reader)
        sorted1 = sorted(reader, key=lambda row: int(row[1]))
        sorted2 = sorted(reader, key=lambda row: int(row[2]))
        for row in sorted2:
            writer.writerow(row)

我做错了什么?


一个要点:“如果csvfile是一个文件对象,在那些有区别的平台上,它必须用‘b’标志打开。” https://docs.python.org/2/library/csv.html - sancho.s ReinstateMonicaCellio
@sancho.s 对于 Python 2 来说,这当然是正确的,但在 Python 3 中,CSV 文件必须以文本模式打开。当你想要使用既能在 Python 2 上运行又能在 Python 3 上运行的代码处理 CSV 文件时,这会让人感到相当烦恼。 - PM 2Ring
@PM2Ring - 是的。原帖没有指定版本,因此需要注意。 - sancho.s ReinstateMonicaCellio
你是否检查了sorted1和sorted2的内容以确定问题出在哪里? - sancho.s ReinstateMonicaCellio
4个回答

8
您的输出文件为空的原因是因为:
sorted2 = sorted(reader, key=lambda row: int(row[2]))

您正在尝试从reader中对数据进行排序,但在先前的排序语句中已经读取了所有数据,因此读取器没有剩余数据可供读取。然而,您真正想要重新对sorted1中的数据进行排序,就像这样:
import csv

with open('original.csv', mode='rt') as f, open('sorted.csv', 'w') as final:
    writer = csv.writer(final, delimiter='\t')
    reader = csv.reader(f, delimiter=',')
    _ = next(reader)
    sorted1 = sorted(reader, key=lambda row: int(row[1]))
    sorted2 = sorted(sorted1, key=lambda row: int(row[2]))
    for row in sorted2:
        writer.writerow(row)

另一方面,没有必要进行两次排序。通过改变键函数,可以在单次遍历中完成排序。

import csv

with open('original.csv', mode='rt') as f, open('sorted.csv', 'w') as final:
    writer = csv.writer(final, delimiter='\t')
    reader = csv.reader(f, delimiter=',')
    _ = next(reader)
    sorted2 = sorted(reader, key=lambda row: (int(row[1]), int(row[2])))        
    for row in sorted2:
        writer.writerow(row)

那个关键函数首先通过它们的row [1]值比较项目,如果这些值相同,它就会通过它们的row [2]值进行比较。那样做可能无法得到您实际想要的排序方式。您可能希望颠倒这些测试的顺序:
key=lambda row: (int(row[2]), int(row[1])) 

正如Peter Wood在评论中提到的那样,Writer对象有一个writerows方法,可以一次性写入所有行。这比在for循环中逐个写入行更有效率。
顺便说一句,没有必要执行此赋值操作:
_ = next(reader)

我猜这表明你要丢弃第一行,但是你可以直接写调用而不执行赋值操作:
next(reader)

2
此外,writerows会循环并为您编写所有行。 - Peter Wood

3

使用pandas,您可以实现简单的操作。

import pandas as pd

df = pd.read_csv('original.csv', delimiter='\t')

df = df.sort_values(['col1', 'col2'], ascending=[True, True]) # parameter ascending is applied to 'col1' and 'col2' respectively.

df.to_csv('sorted.csv')

Pandas read_csv文档

Pandas sort文档


2
这很好,但是OP并没有提到Pandas,所以你为什么要发布一个Pandas的答案呢?并非每个人都安装了Pandas,而标准的'csv'模块完全能够执行这样一个简单的任务。 - PM 2Ring

0

lambda函数可以返回一个元组

sorted(reader, key=lambda row: (int(row[1]), int(row[2])))

0

试一下这个

 with open('original.csv',mode='r') as csvfile:
        reader = csv.DictReader(csvfile, delimiter=";")
        sortedlist = sorted(reader, key=lambda row:(int(row[1]), int(row[2])))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接