Cómo aplicar una función a dos columnas del marco de datos de Pandas
Supongamos que tengo una función y un marco de datos definidos como se muestra a continuación:
def get_sublist(sta, end):
return mylist[sta:end+1]
df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']
Ahora quiero aplicar get_sublist
a df
las dos columnas 'col_1', 'col_2'
de para calcular por elementos una nueva columna 'col_3'
para obtener un resultado que se vea así:
ID col_1 col_2 col_3
0 1 0 1 ['a', 'b']
1 2 2 4 ['c', 'd', 'e']
2 3 3 5 ['d', 'e', 'f']
Lo intenté
df['col_3'] = df[['col_1','col_2']].apply(get_sublist, axis=1)
pero esto resulta en
TypeError: get_sublist() missing 1 required positional argument:
¿Cómo lo hago?
Hay una manera clara y de una sola línea de hacer esto en Pandas:
df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)
Esto permite f
ser una función definida por el usuario con múltiples valores de entrada y utiliza nombres de columnas (seguros) en lugar de índices numéricos (inseguros) para acceder a las columnas.
Ejemplo con datos (basado en la pregunta original):
import pandas as pd
df = pd.DataFrame({'ID':['1', '2', '3'], 'col_1': [0, 2, 3], 'col_2':[1, 4, 5]})
mylist = ['a', 'b', 'c', 'd', 'e', 'f']
def get_sublist(sta,end):
return mylist[sta:end+1]
df['col_3'] = df.apply(lambda x: get_sublist(x.col_1, x.col_2), axis=1)
Salida de print(df)
:
ID col_1 col_2 col_3
0 1 0 1 [a, b]
1 2 2 4 [c, d, e]
2 3 3 5 [d, e, f]
Si los nombres de sus columnas contienen espacios o comparten un nombre con un atributo de marco de datos existente, puede indexar con corchetes:
df['col_3'] = df.apply(lambda x: f(x['col 1'], x['col 2']), axis=1)
Aquí hay un ejemplo que utiliza apply
el marco de datos, al que estoy llamando axis = 1
.
Tenga en cuenta que la diferencia es que en lugar de intentar pasar dos valores a la función f
, reescriba la función para aceptar un objeto de la Serie pandas y luego indexe la Serie para obtener los valores necesarios.
In [49]: df
Out[49]:
0 1
0 1.000000 0.000000
1 -0.494375 0.570994
2 1.000000 0.000000
3 1.876360 -0.229738
4 1.000000 0.000000
In [50]: def f(x):
....: return x[0] + x[1]
....:
In [51]: df.apply(f, axis=1) #passes a Series object, row-wise
Out[51]:
0 1.000000
1 0.076619
2 1.000000
3 1.646622
4 1.000000
Dependiendo de su caso de uso, a veces es útil crear un group
objeto pandas y luego usarlo apply
en el grupo.
Una solución sencilla es:
df['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)
¡Una pregunta interesante! mi respuesta es la siguiente:
import pandas as pd
def sublst(row):
return lst[row['J1']:row['J2']]
df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']
df['J3'] = df.apply(sublst,axis=1)
print df
Producción:
ID J1 J2
0 1 0 1
1 2 2 4
2 3 3 5
ID J1 J2 J3
0 1 0 1 [a]
1 2 2 4 [c, d]
2 3 3 5 [d, e]
Cambié el nombre de la columna a ID, J1, J2, J3 para garantizar que ID <J1 <J2 <J3, de modo que la columna se muestre en la secuencia correcta.
Una versión breve más:
import pandas as pd
df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
print df
lst = ['a','b','c','d','e','f']
df['J3'] = df.apply(lambda row:lst[row['J1']:row['J2']],axis=1)
print df
El método que estás buscando es Series.combine. Sin embargo, parece que se debe tener cierto cuidado con los tipos de datos. En su ejemplo, usted (como lo hice yo al probar la respuesta) llamaría ingenuamente
df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)
Sin embargo, esto arroja el error:
ValueError: setting an array element with a sequence.
Mi mejor suposición es que parece esperar que el resultado sea del mismo tipo que la serie que llama al método (df.col_1 aquí). Sin embargo, lo siguiente funciona:
df['col_3'] = df.col_1.astype(object).combine(df.col_2, func=get_sublist)
df
ID col_1 col_2 col_3
0 1 0 1 [a, b]
1 2 2 4 [c, d, e]
2 3 3 5 [d, e, f]