【问题标题】:Custom reading CSV files (Keyword accesible / custom structure)自定义读取 CSV 文件(关键字可访问/自定义结构)
【发布时间】:2015-08-20 13:45:02
【问题描述】:

我正在尝试执行以下操作: 我下载了一个 csv 文件,其中包含我过去 180 天的银行交易。 我想读入这个 csv 文件,然后用数据做一些图。 为此,我设置了一个程序来读取 csv 文件并通过关键字使数据可用。 例如在 csv 文件中有一列“Buchungstag”。 我用日期关键字等替换它。

import numpy as np
import matplotlib.pylab as mpl
import csv

class finanz():

    def __init__(self):

        path = "/home/***/"
        self.dataFileName = path + "test.csv"
        self.data_read = open(self.dataFileName, 'r')
        self._columns = {}
        self._columns[0] = ["date", "Buchungstag", "", "S15"]
        self._columns[1] = ["value", "Umsatz", "Euro", "f8"]
        self._ident = {"Buchungstag":"date", "Umsatz in {0}":"value"}
        self.base = 1205.30
        self._readData()

    def _readData(self):

        r = csv.DictReader(self.data_read, delimiter=';')
        dtype = map(lambda x: (self._columns[x][0],self._columns[x][3]),range(len(self._columns)))
        self.data = np.recarray((2), dtype=dtype)
        desiredKeys = map(lambda x:x, self._ident.iterkeys())

        for i, x in enumerate(r):
            for k in desiredKeys:
                if k == "Umsatz in {0}":
                    v = np.float(x[k].replace(",", "."))+self.base           
                else:               
                    v = x[k]
                self.data[self._ident[k]][i] = v 

    def getAllData(self):
        return self.data.copy()   

a = finanz()
b = a.getAllData()
print type(b)
print type(b['value']),type(b['date'])

样本数据

"Buchungstag";"Wertstellung (Valuta)";"Vorgang";"Buchungstext";"Umsatz in {0}";
"02.06.2015";"02.06.2015";"Lastschrift/Belast.";"Auftraggeber: abc";"-3,75";

我现在的问题是为什么 type(b['date']) 是一个类 'numpy.core.records.recarray' 而 type(b['value']) 是一个类型 'numpy.ndarray' ??

我的第二个问题是如何以我可以与 matplotlib 一起使用的格式“保存”日期?

第三个也是最后一个问题是如何检查 csv 文件的行数(用于创建空 self.data 数组)

谢谢!

【问题讨论】:

  • np.loadtxtnp.genfromtxtcsv 数据收集在一个列表中(通过列表追加),并在最后将其转换为数组。他们不需要事先知道文件中的行数。他们还可以轻松处理创建结构化数组的细节。
  • 是的,这是真的。但我不想有这种格式。我希望能够使用关键字访问数据,并且我也不想阅读仅提供选定部分的所有信息。我想将它扩展为包含一定范围的日期等。我有点困惑为什么日期是一个类和值是一个类型。

标签: python csv numpy matplotlib


【解决方案1】:

无需额外代码即可重复生成数组:

In [230]: dt=np.dtype([('date', 'S15'), ('value', '<f8')])
In [231]: data=np.recarray((2,),dtype=dt)

In [232]: type(data['date'])
Out[232]: numpy.core.records.recarray

In [233]: type(data['value'])
Out[233]: numpy.ndarray

一个字段返回为ndarray,而另一个字段返回为recarray 这一事实并不重要。这就是 recarray 类的设置方式。

现在我们主要使用“结构化数组”,例如使用

data1=np.empty((2,),dtype=dt)

或用“0”填充:

data1=np.zeros((2,),dtype=dt)

# array([('', 0.0), ('', 0.0)], 
  dtype=[('date', 'S15'), ('value', '<f8')])

这样,data1['date']['value'] 都是 ndarrayrecarray 是旧版本,仍然兼容,但结构化数组的语法和行为更加一致。有很多关于结构化数组的 SO 问题,其中许多是由 np.genfromtxt 产生的,适用于像你这样的 csv 文件。

我可以结合这个想法,加上我的评论(关于列表追加):

def _readData(self):
    r = csv.DictReader(self.data_read, delimiter=';')
    if self._columns[0][1].endswith('tag'):
        self._columns[0][2] = 'datetime64[D]'
    dtype = map(lambda x: (self._columns[x][0],self._columns[x][3]),range(len(self._columns)))
    desiredKeys = map(lambda x:x, self._ident.iterkeys())
    data = []
    for x in r:
        aline = np.zeros((1,), dtype=dtype)
        for k in desiredKeys:
            if k == "Umsatz in {0}":
                v = np.float(x[k].replace(",", "."))+self.base
            else:
                v = x[k]
                v1 = v.split('.')
                if len(v1)==3:  # convert date to yyyyy-mm-dd format
                     v = '%s-%s-%s'%(v1[2],v1[1],v1[0])
            aline[self._ident[k]] = v
        data.append(aline)
    self.data = np.concatenate(data)

产生b 喜欢:

array([(datetime.date(2015, 6, 2), 1201.55),
       (datetime.date(2015, 6, 2), 1201.55),
       (datetime.date(2015, 6, 2), 1201.55)], 
       dtype=[('date', '<M8[D]'), ('value', '<f8')])

我相信genfromtxt 将每一行收集为一个元组,并在最后创建数组。结构化数组的文档表明它们可以从

np.array([(item1, item2), (item3, item4),...], dtype=dtype)

我选择为每一行构造一个数组,并在最后将它们连接起来,因为这需要对您的代码进行更少的更改。

我还更改了该函数,以便将“标记”列转换为np.datetime64 dtype。关于使用该 dtype 有许多 SO 问题。我相信它可以在matplotlib 中使用,虽然我没有这方面的经验。

【讨论】:

  • 非常感谢,我将更改代码以使用结构化数组。最让我困惑的是,一个是类,一个是类型。我尽量避免追加,主要是因为它减慢了代码的速度(也许我在这里错了,但这就是我上次在 python 中编码时所记得的)。当然,对于这种情况,这并不重要,但我只是好奇是否可以在没有附加的情况下做到这一点。我让 matplotlib 与 ax.set_xticklabels(self.data['date'],rotation=90) 和 ax.xaxis.set_ticks(np.arange(0, len(b['date']), 1)) 一起工作就像一个魅力。
  • recarrayndtype 都是类。追加到一个数组很慢,追加到一个列表没有那么多。如果您知道有多少数据行,则数组的预分配有效。但是对于文本文件,您只能通过读取整个文件来知道行数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-31
  • 1970-01-01
相关资源
最近更新 更多