在Python中读取并排序CSV文件

0 投票
5 回答
5459 浏览
提问于 2025-04-16 15:37

我正在尝试读取一个CSV文件,内容大致是这样的:

ruby,2,100
diamond,1,400
emerald,3,250
amethyst,2,50
opal,1,300
sapphire,2,500
malachite,1,60

这是我正在尝试的一些代码。

class jewel:
    def __init__(gem, name, carat, value):
            gem.name = name
            gem.carot = carat
            gem.value = value
    def __repr__(gem):
            return repr((gem.name, gem.carat, gem.value))

jewel_objects = [jewel('diamond', '1', 400),
                 jewel('ruby', '2', 200),
                 jewel('opal', '1', 600),
                ]

aList = [sorted(jewel_objects, key=lambda jewel: (jewel.value))]
print aList

我想把这些值读取进来,并把它们分别赋值给名字、克拉和价值,但我不太确定该怎么做。然后,一旦我把它们读取进来,我想根据每克拉的价值进行排序,也就是价值除以克拉。我搜索了很多资料,但还是没有找到答案。非常感谢你们的帮助!

5 个回答

0

你可以使用numpy的结构化数组,配合csv模块,然后用numpy.sort()来对数据进行排序。下面的代码应该可以正常运行。假设你的csv文件叫做geminfo.csv

import numpy as np
import csv

fileobj = open('geminfo.csv','rb')
csvreader = csv.reader(fileobj)

# Convert data to a list of lists
importeddata = list(csvreader)

# Calculate Value/Carat and add it to the imported data
# and convert each entry to a tuple
importeddata = [tuple(entry + [float(entry[2])/entry[1]]) for entry in importeddata]

一种排序这些数据的方法是使用numpy,下面是具体的做法。

# create an empty array
data = np.zeros(len(importeddata), dtype = [('Stone Name','a20'),
                            ('Carats', 'f4'),
                            ('Value', 'f4'), 
                            ('valuepercarat', 'f4')]
                        )
data[:] = importeddata[:]
datasortedbyvaluepercarat = np.sort(data, order='valuepercarat')
0

如果你想处理现实中的CSV(逗号分隔值)数据,建议使用Python自带的CSV模块,这在最近的Python版本中都有。

CSV其实是一种约定,而不是标准。你给出的示例数据看起来简单且规则,但CSV通常会有一些麻烦的情况,比如某个字段里可能会有嵌入的逗号,这就会让解析变得复杂。

下面是一个非常简单的程序,基于你的代码,采用了比较基础的方式来解析数据(先按行分割,再按逗号分割每一行)。这个程序不能处理那些分割后字段数量不对的数据,也不能正确解析那些数字字段(比如整数和浮点数)。换句话说,这个程序没有进行错误检查和异常处理。

不过,我故意把它写得简单,这样你可以很容易地和你的草稿进行对比。另外,我在类定义中使用了Python的常规约定,关于“self”的引用。通常只有在做“元类”编程时,才会用到“self”以外的名字,也就是在动态创建其他类的时候。其他情况下,使用不同的名字可能会让有经验的Python程序员感到困惑。

#!/usr/bin/env python
class Jewel:
    def __init__(self, name, carat, value):
        self.name = name
        self.carat = int(carat)
        self.value = float(value)
        assert self.carat != 0      # Division by zero would result from this
    def __repr__(self):
        return repr((self.name, self.carat, self.value))

if __name__ == '__main__':
    sample='''ruby,2,100
diamond,1,400
emerald,3,250
amethyst,2,50
opal,1,300
sapphire,2,500
malachite,1,60'''

    these_jewels = list()
    for each_line in sample.split('\n'):
        gem_type, carat, value = each_line.split(',')
        these_jewels.append(Jewel(gem_type, carat, value))
        # Equivalently: 
        # these_jewels.append(Jewel(*each_line.split(',')))

    decorated = [(x.value/x.carat, x) for x in these_jewels]
    results = [x[1] for x in sorted(decorated)]
    print '\n'.join([str(x) for x in results])

这里的解析是通过字符串的.split()方法简单实现的,数据通过Python的“元组解包”语法提取到变量中(如果输入的某一行字段数量不对,这个方法就会失败)。

另外一种语法是使用Python的“apply”语法。参数前面的*符号会将它解包成单独的参数,然后传递给Jewel()类的实例化。

这段代码还使用了广泛推荐的DSU(装饰、排序、去装饰)模式来对数据的某个字段进行排序。我通过创建一系列元组(计算值,对象引用)来“装饰”数据,然后以一种我希望你能理解的方式“去装饰”排序后的数据。(对于任何有经验的Python程序员来说,这一点是非常清楚的)。

是的,整个DSU过程可以简化成一行代码;我这里分开写是为了让你更容易理解和学习。

再次强调,这段示例代码只是为了帮助你学习。处理任何现实数据时,你应该使用CSV模块;同时在解析过程中或在Jewel.__init__处理时引入异常处理,以便将数字数据转换为正确的Python类型。(另外,建议你考虑使用Python的Decimal模块来表示货币值,而不是使用float(),或者至少把值存储为分或厘,然后用自己的函数将这些值表示为美元和分)。

2

这里你需要做两件事,第一件是把数据加载到对象里。我建议你看看Python标准库里的'csv'模块。这个模块功能很强大,可以读取每一行数据,并且让你很方便地访问这些数据。

CSV文档:http://docs.python.org/library/csv.html

我会创建一个对象的列表,然后在你的对象里实现一个cmp函数,或者(如果你用的是旧版本的Python)可以给sorted()函数传一个定义比较方式的函数。关于排序的更多信息,你可以在Python的维基上找到。

维基文档:http://wiki.python.org/moin/HowTo/Sorting

你可以在你的类里这样实现cmp函数(虽然可以更高效,但我这里是为了让你更容易理解)。

def __cmp__(gem, other):
    if (gem.value / gem.carot) < (other.value / other.carot):
        return -1
    elif (gem.value / gem.carot) > (other.value / other.carot): 
        return 1
    else:
        return 0

撰写回答