2015-06-13 5 views
4

Я новичок питон, и я пытаюсь использовать следующий код из этого источника: Portfolio rebalancing with bandwidth method in pythonAttributeError: объект «DataFrame» не имеет атрибута «colmap» в Python

Код хорошо работает до сих пор.

Проблема заключается в том, что если я хочу, чтобы вызвать функцию не как обычно, как rebalance(df, tol), но с определенного места в dataframe на, как: rebalance(df[500:], tol), я получаю следующее сообщение об ошибке:

AttributeError: 'DataFrame' object has no attribute 'colmap'. Поэтому мой вопрос: как мне настроить код, чтобы сделать это возможным?

Вот код:


import datetime as DT 
import numpy as np 
import pandas as pd 
import pandas.io.data as PID 

def setup_df(): 
    df1 = PID.get_data_yahoo("IBM", 
          start=DT.datetime(1970, 1, 1), 
          end=DT.datetime.today()) 
    df1.rename(columns={'Adj Close': 'ibm'}, inplace=True) 

    df2 = PID.get_data_yahoo("F", 
          start=DT.datetime(1970, 1, 1), 
          end=DT.datetime.today()) 
    df2.rename(columns={'Adj Close': 'ford'}, inplace=True) 

    df = df1.join(df2.ford, how='inner') 
    df = df[['ibm', 'ford']] 
    df['sh ibm'] = 0 
    df['sh ford'] = 0 
    df['ibm value'] = 0 
    df['ford value'] = 0 
    df['ratio'] = 0 
    # This is useful in conjunction with iloc for referencing column names by 
    # index number 
    df.colmap = dict([(col, i) for i,col in enumerate(df.columns)]) 
    return df 

def invest(df, i, amount): 
    """ 
    Invest amount dollars evenly between ibm and ford 
    starting at ordinal index i. 
    This modifies df. 
    """ 
    c = df.colmap 
    halfvalue = amount/2 
    df.iloc[i:, c['sh ibm']] = halfvalue/df.iloc[i, c['ibm']] 
    df.iloc[i:, c['sh ford']] = halfvalue/df.iloc[i, c['ford']] 

    df.iloc[i:, c['ibm value']] = (
     df.iloc[i:, c['ibm']] * df.iloc[i:, c['sh ibm']]) 
    df.iloc[i:, c['ford value']] = (
     df.iloc[i:, c['ford']] * df.iloc[i:, c['sh ford']]) 
    df.iloc[i:, c['ratio']] = (
     df.iloc[i:, c['ibm value']]/df.iloc[i:, c['ford value']]) 

def rebalance(df, tol): 
    """ 
    Rebalance df whenever the ratio falls outside the tolerance range. 
    This modifies df. 
    """ 
    i = 0 
    amount = 100 
    c = df.colmap 
    while True: 
     invest(df, i, amount) 
     mask = (df['ratio'] >= 1+tol) | (df['ratio'] <= 1-tol) 
     # ignore prior locations where the ratio falls outside tol range 
     mask[:i] = False 
     try: 
      # Move i one index past the first index where mask is True 
      # Note that this means the ratio at i will remain outside tol range 
      i = np.where(mask)[0][0] + 1 
     except IndexError: 
      break 
     amount = (df.iloc[i, c['ibm value']] + df.iloc[i, c['ford value']]) 
    return df 

df = setup_df() 
tol = 0.05 #setting the bandwidth tolerance 
rebalance(df, tol) 

df['portfolio value'] = df['ibm value'] + df['ford value'] 
df["ibm_weight"] = df['ibm value']/df['portfolio value'] 
df["ford_weight"] = df['ford value']/df['portfolio value'] 

print df['ibm_weight'].min() 
print df['ibm_weight'].max() 
print df['ford_weight'].min() 
print df['ford_weight'].max() 

# This shows the rows which trigger rebalancing 
mask = (df['ratio'] >= 1+tol) | (df['ratio'] <= 1-tol) 
print(df.loc[mask]) 

ответ

4

Проблема вы столкнулись из-за плохого проектного решения с моей стороны. colmap это атрибут, определенный на df в setup_df:

df.colmap = dict([(col, i) for i,col in enumerate(df.columns)]) 

Это не является стандартным атрибутом DataFrame.

df[500:] возвращает новый DataFrame, который генерируется путем копирования данных из df в новый DataFrame. Поскольку colmap не является стандартным атрибутом, он не копируется в новый DataFrame.

Для вызова rebalance на DataFrame, кроме того, возвращенного setup_df, замените c = df.colmap с

c = dict([(col, j) for j,col in enumerate(df.columns)]) 

Я сделал это изменение в the original post, а также.

PS. В другом вопросе я решил определить colmap по адресу df, поэтому , что этот дикт не нужно перерабатывать при каждом вызове rebalance и invest.

Ваш вопрос показывает мне, что эта незначительная оптимизация не стоит делать эти функций так зависят от особости в DataFrame возвращенного setup_df.


Существует вторая проблема, вы столкнетесь с помощью rebalance(df[500:], tol):

С df[500:] возвращает копию из части df, rebalance(df[500:], tol) будет изменять эту копию, а не оригинал df. Если объект, df[500:], не имеет ссылки за пределами rebalance(df[500:], tol), это будет мусор , собранный после завершения звонка до rebalance. Таким образом, весь расчет будет потерян. Поэтому rebalance(df[500:], tol) не пригодится.

Вместо этого, вы можете изменить rebalance принять i в качестве параметра:

def rebalance(df, tol, i=0): 
    """ 
    Rebalance df whenever the ratio falls outside the tolerance range. 
    This modifies df. 
    """ 
    c = dict([(col, j) for j, col in enumerate(df.columns)]) 
    while True: 
     mask = (df['ratio'] >= 1+tol) | (df['ratio'] <= 1-tol) 
     # ignore prior locations where the ratio falls outside tol range 
     mask[:i] = False 
     try: 
      # Move i one index past the first index where mask is True 
      # Note that this means the ratio at i will remain outside tol range 
      i = np.where(mask)[0][0] + 1 
     except IndexError: 
      break 
     amount = (df.iloc[i, c['ibm value']] + df.iloc[i, c['ford value']]) 
     invest(df, i, amount) 
    return df 

Затем вы можете сбалансировать df, начиная с 500-й строки, используя

rebalance(df, tol, i=500) 

Обратите внимание, что это находит первую строку после или после i = 500, которому необходимо перебалансировка. Это не обязательно перебалансировка при i = 500. Это позволяет вам позвонить rebalance(df, tol, i) для произвольного i, не задумываясь заранее, если требуется перебалансировка в строке i.