AttributeError: el objeto ‘DataFrame’ no tiene atributo ‘colmap’ en Python

Soy un principiante de python y trato de usar el siguiente código de esta fuente: Portfolio rebalancing con el método de ancho de banda en python

El código funciona bien hasta ahora.

El problema es que si quiero llamar a la función no como es habitual, como rebalance(df, tol) , pero desde una cierta ubicación en el dataframe, como: rebalance(df[500:], tol) , aparece el siguiente error :

AttributeError: 'DataFrame' object has no attribute 'colmap' . Entonces, mi pregunta es: ¿cómo debo ajustar el código para que esto sea posible?

Aquí está el código:


 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) | (df['ratio'] <= 1-tol) print(df.loc[mask]) 

El problema que encontraste se debe a una mala decisión de diseño de mi parte. colmap es un atributo definido en df en setup_df :

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

No es un atributo estándar de un DataFrame.

df[500:] devuelve un nuevo DataFrame que se genera al copiar datos de df en el nuevo DataFrame. Como colmap no es un atributo estándar, no se copia en el nuevo DataFrame.

Para llamar a rebalance en un DataFrame diferente al que devolvió setup_df , reemplace c = df.colmap con

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

He hecho este cambio en la publicación original también.

PD. En la otra pregunta, colmap definir colmap en df para que este dictamen no tuviera que volver a calcularse con cada llamada para rebalance e invest .

Su pregunta me muestra que esta pequeña optimización no vale la pena hacer que estas funciones sean tan dependientes de la especialidad del DataFrame devuelto por setup_df .


Hay un segundo problema que encontrará al rebalance(df[500:], tol) :

Como df[500:] devuelve una copia de una parte de df , el rebalance(df[500:], tol) modificará esta copia y no la df original. Si el objeto, df[500:] , no tiene ninguna referencia fuera del rebalance(df[500:], tol) , se recogerá la basura después de completar la llamada para rebalance . Entonces todo el cómputo se perdería. Por lo tanto, el rebalance(df[500:], tol) no es útil.

En su lugar, podría modificar el rebalance para aceptar i como un parámetro:

 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 

Luego puede reequilibrar df comenzando en la fila 500 usando

 rebalance(df, tol, i=500) 

Tenga en cuenta que esto encuentra la primera fila en o después de i = 500 que necesita un rebalanceo. No se rebalancea necesariamente en i = 500 en sí. Esto le permite llamar a rebalance(df, tol, i) para i arbitrario sin tener que determinar por adelantado si se requiere rebalanceo en la fila i .