2013-12-15 2 views
2

저는 Bloomberg의 Open Symbology에서 마스터 데이터 테이블을 다운로드 중입니다. csv로 난에 관심이 아니에요 열이 있습니다. csv 파일 내에서 발견 된 컬럼의 서브 세트에서 namedtuple 인스턴스를 생성 할 수있는 효율적인/파이썬 방법은csv의 열을 namedtuple 함수로 건너 뛰는보다 효율적인 방법은 무엇입니까?

질문

있습니까? 다음과 같이 내가

내 현재 프로세스 (아래 파이썬 3.3 코드)를 시도했습니다 무엇

은 다음과 같습니다

  1. 는 CSV의 모든 열이있는 TempRecord의 namedtuple을 만듭니다.
  2. csv 파일의 각 레코드에 대해 TempRecord 인스턴스를 만듭니다.
  3. 주어진 TempRecord에서 BSYMRecord (더 적은 수의 특성 및 이름이 바뀐 특성)를 만듭니다.
  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을 만들 수 있지만 가짜 인수는 무시하지 않습니다. 그래서 당신은 여전히 ​​수동으로 필터링해야합니다 -하지만 지금은 오히려 다른 namedtuple보다는 DICT 이해와 함께이 작업을 수행 할 수 있습니다

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

트릭은 DictReader가 처음에 설정지고 있습니다. 문자열을 생성하는 파일과 같은 객체가 필요합니다. ZipFile.open바이트의 파일과 비슷한 객체를 제공하며 인코딩을 사용할 수 없습니다. codecs 모듈은 여기에 구조에 온다 - 당신은 투명과 같이 당신을 위해 문자열을 UTF8 바이트를 디코딩하는 위해 StreamReader 얻을 수 있습니다 :

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을 만들지 않아도된다는 의미입니다.

관련 문제