更有效的方法是将csv中的列跳过到namedtuple函数?

时间:2013-12-15 13:06:42

标签: python python-3.x

我正在从Bloomberg的Open Symbology下载主数据表。 csv有我不感兴趣的列。

问题

是否有高效/ Pythonic方法从csv文件中找到的列子集生成namedtuple实例?

我尝试了什么

我目前的流程(下面的Python 3.3代码)如下:

  1. 创建一个包含csv中所有列的TempRecord namedtuple。
  2. 为csv文件中的每条记录创建一个TempRecord实例。
  3. 从给定的TempRecord创建BSYMRecord(包含更少和重命名的属性)。
  4. 收益率BSYMRecord。
  5. 这闻起来效率很低。

    from csv import reader
    from collections import namedtuple
    from datetime import date
    from io import BytesIO
    from urllib.request import urlopen
    from urllib.error import HTTPError
    from zipfile import ZipFile
    
    
    def bsym_records(sector, security_type, file_date):
        """Yield BSYMRecord for given sector and security type."""
        template = 'http://bdn-ak.bloomberg.com/precanned/{s}_{t}_{d}.txt.zip'
        url = template.format(s=sector, t=security_type, d=file_date)
        response = urlopen(url)
        zipfile = ZipFile(BytesIO(response.read()))
        for filename in zipfile.namelist():
            with zipfile.open(filename) as f:
                line = f.readline().decode('utf-8')
                headers = line.strip().replace(' ', '_').split('|')
                TempRecord = namedtuple('BSYMRecord', headers)
                while True:
                    line = f.readline().decode('utf-8')
                    if line[0] == '#':
                        break
                    t = TempRecord._make(line.strip().split('|'))
                    yield reduce_bsym_record(t)
    
    
    BSYMRecord = namedtuple('BSYMRecord', ['name',
                                           'ticker',
                                           'pricing_source',
                                           'security_type',
                                           'market_sector',
                                           'BBGID',
                                           'BBGID_composite',
                                           'BSID',
                                           'unique_id'])
    
    
    def reduce_bsym_record(record):
        """Eliminate non-essential fields."""
        return BSYMRecord._make((record.NAME,
                                 record.ID_BB_SEC_NUM_DES,
                                 record.FEED_SOURCE,
                                 record.SECURITY_TYP,
                                 record.MARKET_SECTOR_DES,
                                 record.ID_BB_GLOBAL,
                                 record.COMPOSITE_ID_BB_GLOBAL,
                                 record.ID_BB_SEC_NUM_SRC,
                                 record.ID_BB_UNIQUE))
    

2 个答案:

答案 0 :(得分:3)

您当前正在导入csv模块但未使用它。如果 使用它,则可以使用csv.DictReader类为文件中的每一行创建字典而不是列表。您可以使用关键字参数构造namedtuple,但它不会忽略虚假参数。所以你仍然需要手动过滤它们 - 但是你现在可以用dict理解来做,而不是一个不同的命名元组:

for line in csvfile:
    yield BSYMRecord(**{k:v for k,v in line if k in BSYMRecord._fieldnames})

诀窍是首先设置DictReader。它需要一个类似文件的对象来产生字符串; ZipFile.open提供了一个类似文件的对象,它产生 bytes ,并且无法进行编码。 codecs模块在​​这里得到了解决 - 您可以获得一个StreamReader,它可以透明地将utf8字节解码为字符串,如下所示:

import codecs
utf8 = codecs.lookup('utf8').streamreader

并像这样使用它:

for filename in zipfile.namelist():
    with zipfile.open(filename) as f:
        csvfile = csv.DictReader(utf8(f))
        for line in csvfile:
             yield BSYMRecord(**{k:v for k,v in line if k in BSYMRecord._fieldnames})

答案 1 :(得分:1)

您可以使用index根据标题从每行中选择所需的值:

fields = ["NAME", "ID_BB_SEC_NUM_DES", ...]

# ...                       
headers = line.strip().replace(' ', '_').split('|')
indices = [headers.index(field) for field in fields)
while True:
    # ...
    line = line.strip().split('|')
    yield BSYMRecord._make((line[i] for i in indices))

这可以保护您当前的保护,使其不受更改字段和单个字段定义的影响,但这意味着您无需为每行创建TempRecord