La aplicación de una función lambda a una columna falló en pandas

No sé por qué el método de índice tiene un comportamiento inconsistente al hacer la función de aplicación de columna.

El dataframe es:

df = pd.DataFrame( [(1, 'Hello'), (2, "World")]) df.columns=['A', 'B'] 

¿Y quiero aplicar lambda a las segundas columnas, es decir que el objeto Serie no puede aplicarse?

 print df.iloc[:, 1:2].apply(lambda x: x.upper()).head() **AttributeError**:("'Series' object has no attribute 'upper'", u'occurred at index B') print df.loc[:, ['B']].apply(lambda x: x.upper()).head() **AttributeError**:("'Series' object has no attribute 'upper'", u'occurred at index B') 

Pero más bien el siguiente método de indexación funciona bien.

 print df.loc[:, 'B'].apply(lambda x: x.upper()).head() 

¿Por qué? Creo que los tres métodos de índice son equivalentes? Los tres métodos de indexación anteriores tienen casi el mismo resultado si la impresión es:

  B 0 Hello 1 World 

e imprimir df.loc [:, ‘B’] obtiene

 0 Hello 1 World Name: B, dtype: object 

¿Qué significan las diferencias?

Cuando indexas con 'B' obtienes una serie. Cuando indexas con 1:2 o con ['B'] , obtienes un DataFrame con una columna. Cuando usas apply en una serie, tu función se llama en cada elemento. Cuando utiliza apply en un DataFrame, su función se llama en cada columna .

Así que no, no son equivalentes. Cuando tienes una serie puedes usar tu función como quieras. Cuando tiene un DataFrame de una columna, no puede, porque se pasa la columna como su argumento, y la columna es una Serie que no tiene un método upper .

Puede ver que no son iguales porque los resultados son diferentes cuando los imprime. Sí, son casi lo mismo, pero no lo mismo. El primero tiene un encabezado de columna, lo que indica que es un DataFrame; el segundo no tiene encabezado de columna, pero tiene el “Nombre” en la parte inferior, lo que indica que es una Serie.

Como mencionó @BrenBarn, la diferencia es que en el caso de df.iloc[:, 1:2] usted tiene DataFrame con una columna, mientras que en el caso de df.loc[:, 'B'] tiene una Serie. Solo una pequeña adición, para convertir DataFrame con una columna en serie, puede utilizar el método pandas.squeeze () :

 >>> df.iloc[:, 1:2] B 0 Hello 1 World >>> df.iloc[:, 1:2].squeeze() 0 Hello 1 World Name: B, dtype: object 

y luego puede usar apply (no tiene que usar lambda , BTW):

 >>> df.iloc[:, 1:2].squeeze().apply(str.upper) 0 HELLO 1 WORLD Name: B, dtype: object 

Si desea aplicar la parte upper de DataFrame, puede usar pandas.applymap () :

 >>> df.iloc[:, 1:2].applymap(str.upper) B 0 HELLO 1 WORLD