Python – Iterar sobre una lista de atributos

Tengo una característica en mi conjunto de datos que es un objeto de marca de tiempo de pandas. Tiene (entre muchos otros) los siguientes atributos: año, hora, día de la semana, mes.

Puedo crear nuevas funciones basadas en estos atributos utilizando algunos métodos de fuerza bruta:

df["year"] = df["timeStamp"].apply(lambda x : x.year) df["hour"] = df["timeStamp"].apply(lambda x : x.hour) 

. . .

Sin embargo, quiero iterar sobre una lista:

 nomtimes = ["year", "hour", "month", "dayofweek"] for i in nomtimes: df[i] = df["timeStamp"].apply(lambda x : xi) 

Obtengo el siguiente atributo AttributeError: ‘Timestamp’ no tiene atributo ‘i’, y lo entiendo y entiendo por qué tengo este error.

¿Cómo puedo hacer que la cadena entre comillas no esté entre comillas para que pueda pasarla como un atributo?

Related of "Python – Iterar sobre una lista de atributos"

Sólo necesitas getattr() :

 df[i] = df["timeStamp"].apply(lambda x : getattr(x, i)) 

No use .apply aquí, pandas tiene varias utilidades integradas para manejar objetos de fecha y hora, use el atributo dt en los objetos de la serie:

 In [11]: start = datetime(2011, 1, 1) ...: end = datetime(2012, 1, 1) ...: In [12]: df = pd.DataFrame({'data':pd.date_range(start, end)}) In [13]: df.dtypes Out[13]: data datetime64[ns] dtype: object In [14]: df['year'] = df.data.dt.year In [15]: df['hour'] = df.data.dt.hour In [16]: df['month'] = df.data.dt.month In [17]: df['dayofweek'] = df.data.dt.dayofweek In [18]: df.head() Out[18]: data year hour month dayofweek 0 2011-01-01 2011 0 1 5 1 2011-01-02 2011 0 1 6 2 2011-01-03 2011 0 1 0 3 2011-01-04 2011 0 1 1 4 2011-01-05 2011 0 1 2 

O, dinámicamente como quisieras usando getattr :

 In [24]: df = pd.DataFrame({'data':pd.date_range(start, end)}) In [25]: nomtimes = ["year", "hour", "month", "dayofweek"] ...: In [26]: df.head() Out[26]: data 0 2011-01-01 1 2011-01-02 2 2011-01-03 3 2011-01-04 4 2011-01-05 In [27]: for t in nomtimes: ...: df[t] = getattr(df.data.dt, t) ...: In [28]: df.head() Out[28]: data year hour month dayofweek 0 2011-01-01 2011 0 1 5 1 2011-01-02 2011 0 1 6 2 2011-01-03 2011 0 1 0 3 2011-01-04 2011 0 1 1 4 2011-01-05 2011 0 1 2 

Y si debe usar una sola línea, vaya con:

 In [30]: df = pd.DataFrame({'data':pd.date_range(start, end)}) In [31]: df.head() Out[31]: data 0 2011-01-01 1 2011-01-02 2 2011-01-03 3 2011-01-04 4 2011-01-05 In [32]: df = df.assign(**{t:getattr(df.data.dt,t) for t in nomtimes}) In [33]: df.head() Out[33]: data dayofweek hour month year 0 2011-01-01 5 0 1 2011 1 2011-01-02 6 0 1 2011 2 2011-01-03 0 0 1 2011 3 2011-01-04 1 0 1 2011 4 2011-01-05 2 0 1 2011 

operator.attrgetter

Puede extraer atributos en un bucle:

 from operator import attrgetter for i in nomtimes: df[i] = df['timeStamp'].apply(attrgetter(i)) 

Aquí hay un ejemplo completo:

 df = pd.DataFrame({'timeStamp': ['2018-05-05 15:00', '2015-01-30 11:00']}) df['timeStamp'] = pd.to_datetime(df['timeStamp']) nomtimes = ['year', 'hour', 'month', 'dayofweek'] for i in nomtimes: df[i] = df['timeStamp'].apply(attrgetter(i)) print(df) timeStamp year hour month dayofweek 0 2018-05-05 15:00:00 2018 15 5 5 1 2015-01-30 11:00:00 2015 11 1 4 

Su código no funcionará porque está intentando pasar una cadena en lugar de extraer un atributo por nombre. Sin embargo, esto no es lo que está sucediendo: la syntax no alimenta la cadena, sino que intenta acceder a i directamente, como se muestra en su primer ejemplo.

Deshacerse del bucle for

Puede preguntar si hay alguna forma de extraer todos los atributos de un objeto de datetime y datetime de una vez en lugar de hacerlo de forma secuencial. El beneficio de attrgetter es que puede especificar múltiples atributos directamente para evitar for completo un bucle for :

 attributes = df['timeStamp'].apply(attrgetter(*nomtimes)) df[nomtimes] = pd.DataFrame(attributes.values.tolist()) 

Usando dt accessor en lugar de aplicar

Pero pd.Series.apply es solo un bucle ligeramente velado. A menudo, no es necesario. Tomando prestada la idea de @ juanpa.arrivillaga, usted puede acceder a los atributos directamente a través del pd.Series.dt :

 attributes = pd.concat(attrgetter(*nomtimes)(df['timeStamp'].dt), axis=1, keys=nomtimes) df = df.join(attributes)