2013-12-15 4 views
2

Я загружаю таблицу основных данных из открытой символики Блумберга. CSV имеет столбцы, которые я не заинтересован.Более эффективный способ пропускать столбцы в csv для функции namedtuple?

Вопрос

Есть ли эффективный/Pythonic способ создания экземпляров namedtuple из подмножества столбцов, найденных в файл CSV?

То, что я пытался

Мой текущий процесс (Python 3.3 код ниже) выглядит следующим образом:

  1. Создать namedtuple TempRecord со всеми столбцами в формате CSV.
  2. Создайте экземпляр TempRecord для каждой записи в файле csv.
  3. Создайте BSYMRecord (с меньшим количеством и переименованными атрибутами) из заданного TempRecord.
  4. Выход BSYMRecord.

Этот запах действительно неэффективен.

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)) 

ответ

3

Вы в настоящее время импортируете модуль csv, но не используете его. Если вы использовали , вы можете использовать класс csv.DictReader для создания словаря вместо списка для каждой строки в файле. Вы можете построить namedtuple с использованием аргументов ключевого слова, но он не игнорирует ложные. Таким образом, вы все равно нужно фильтровать их вручную - но теперь вы можете сделать это с Dict пониманием, а не другой namedtuple:

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

Хитрость заключается в получении DictReader создан в первую очередь. Ему нужен файловый объект, который дает строки; ZipFile.open предоставляет файл-подобный объект, который дает байт и не может принимать кодировку. Модуль 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}) 
+0

Мне это нравится. Мне пришлось добавить фильтр в DictReader, чтобы пропустить нижние колонтитулы, но он работал хорошо. Благодарю. – MikeRand

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 для каждой строки.

Смежные вопросы