Establecer el valor de una columna de Pandas en función del valor en otra columna

6 minutos de lectura

Avatar de usuario de NLR
NLR

Necesito establecer el valor de una columna en función del valor de otra en un marco de datos de Pandas. Esta es la lógica:

if df['c1'] == 'Value':
    df['c2'] = 10
else:
    df['c2'] = df['c3']

No puedo hacer que esto haga lo que quiero, que es simplemente crear una columna con nuevos valores (o cambiar el valor de una columna existente: cualquiera funciona para mí).

Si trato de ejecutar el código anterior o si lo escribo como una función y uso el método de aplicación, obtengo lo siguiente:

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

avatar de usuario de sacuL
saco

una forma de hacer esto sería utilizar la indexación con .loc.

Ejemplo

En ausencia de un marco de datos de ejemplo, inventaré uno aquí:

import numpy as np
import pandas as pd

df = pd.DataFrame({'c1': list('abcdefg')})
df.loc[5, 'c1'] = 'Value'

>>> df
      c1
0      a
1      b
2      c
3      d
4      e
5  Value
6      g

Suponiendo que quisieras crear una nueva columna c2equivalente a c1 excepto donde c1 es Valueen cuyo caso, le gustaría asignarlo a 10:

Primero, podría crear una nueva columna c2y configúrelo en equivalente como c1usando una de las siguientes dos líneas (esencialmente hacen lo mismo):

df = df.assign(c2 = df['c1'])
# OR:
df['c2'] = df['c1']

Luego, encuentra todos los índices donde c1 es igual a 'Value' usando .locy asigne el valor deseado en c2 en esos índices:

df.loc[df['c1'] == 'Value', 'c2'] = 10

Y terminas con esto:

>>> df
      c1  c2
0      a   a
1      b   b
2      c   c
3      d   d
4      e   e
5  Value  10
6      g   g

Si, como sugirió en su pregunta, tal vez a veces solo quiera reemplace los valores en la columna que ya tieneen lugar de crear una nueva columna, omita la creación de la columna y haga lo siguiente:

df['c1'].loc[df['c1'] == 'Value'] = 10
# or:
df.loc[df['c1'] == 'Value', 'c1'] = 10

Dandote:

>>> df
      c1
0      a
1      b
2      c
3      d
4      e
5     10
6      g

  • La segunda solución lo clavó para mí. No me di cuenta de que podía usar .loc como una declaración WHERE en SQL. Tiene sentido. ¡Gracias!

    – NLR

    7 de marzo de 2018 a las 22:12


  • ¿Cómo aplicaría 10 a varias columnas en lugar de solo una? por ejemplo: df.loc[df[‘c1’] == ‘Valor’, ‘c2’, ‘c3’, ‘c4]= 10.

    –Steve

    24 de mayo de 2019 a las 16:12

  • Creo que debe colocar todas las columnas con las que necesita actualizar el valor en una lista, luego recorrer esa lista y cambiar el parámetro de nombre de columna en ella.

    – José

    13 de agosto de 2019 a las 10:48

  • Cuando asigno valores con declaraciones como df.loc[df['c1'] == 'Value', 'c1'] = 10 me sale el temido SettingWithCopyWarning — incluso cuando uso el .loc[] sintaxis. Hasta ahora no ha sido un problema para mí, pero me parece extraño que me llegue la advertencia, a pesar de usar la sugerida .loc[] Acercarse. ¿Alguna solución para eso? Esto es con pandas 1.2.3, numpy 1.18.5 y python 3.7.10.

    – Darren

    31 de marzo de 2021 a las 17:14

  • Digamos que tengo una columna int y quiero dividir su valor por 1000 si su valor es más de 1000. Usando la primera opción df['c1'].loc[df['c1'] > 1000] = df['c1'].loc[df['c1'] > 1000]/1000 tengo el SettingWithCopyWarning. Sin embargo, con la segunda opción df['c1'].loc[df['c1'] > 1000, 'c1'] No recibo esa advertencia.

    – Avell

    24 de junio de 2021 a las 0:14

Avatar de usuario de DJK
DJK

Puedes usar np.where() para establecer valores basados ​​en una condición específica:

#df
   c1  c2  c3
0   4   2   1
1   8   7   9
2   1   5   8
3   3   3   5
4   3   6   8

Ahora cambie los valores (o establezca) en la columna ['c2'] basado en su condición.

df['c2'] = np.where(df.c1 == 8,'X', df.c3)

   c1  c2  c3
0   4   1   1
1   8   X   9
2   1   8   8
3   3   5   5
4   3   8   8

  • ¿Qué pasa si quiero mantener todas las columnas originales?

    – mlestudiante33

    13 de marzo de 2020 a las 1:37

  • @mLstudent33, utilizando df['newColName'] = ...usa un nombre de columna, no en su marco de datos para crear una nueva columna, asumiendo newColName ya no existe

    – DJK

    16 de julio de 2020 a las 20:37

  • Superior a todas las demás soluciones, ya que (a) es más legible debido a la menor ambigüedad del orden de los términos y (b) es más resistente al futuro, ya que es más fácil de modificar para tener en cuenta varias columnas y (c) es rápido, sin código interpretado en una lambda.

    – Contango

    15 de julio de 2021 a las 8:17


  • @DJK supongo c2 en su código se refiere a c4 en la tabla de salida? Será mejor que corrijas uno de ellos.

    – Abu

    28 de agosto de 2021 a las 8:03


avatar de usuario de aggis
aggis

probar:

df['c2'] = df['c1'].apply(lambda x: 10 if x == 'Value' else x)

  • Gracias @AlexanderHughes. Mi publicación original tenía un error tipográfico: en realidad hay tres columnas a considerar, por lo que esta solución no funcionaría.

    – NLR

    7 de marzo de 2018 a las 22:06

  • debiera ser df.apply(lambda x: 10 if x['c1'] == 'Value' else x['c3'],axis=1)

    – DJK

    07/03/2018 a las 22:32

  • Esto podría tener problemas de rendimiento con grandes conjuntos de datos. df.apply() es más lento.

    – ErnestScribbler

    1 de noviembre de 2018 a las 10:21

  • Estaba buscando lo mismo, encontré una lambda que me funcionó con un marco de datos. mi codigo es duro[‘Hr’] = ard.aplicar(lambda x: x[‘Hr’]+1 si x[‘Mi’] >= 45 y x[‘Mi’]

    – Runawaygeek

    13 mayo 2020 a las 13:55

avatar de usuario de vkerov
vkerov

Tenga en cuenta la tilda que invierte la selección. Utiliza métodos pandas (es decir, es más rápido que if/else).

df.loc[(df['c1'] == 'Value'), 'c2'] = 10
df.loc[~(df['c1'] == 'Value'), 'c2'] = df['c3']

Sugiero hacerlo en dos pasos:

# set fixed value to 'c2' where the condition is met
df.loc[df['c1'] == 'Value', 'c2'] = 10

# copy value from 'c3' to 'c2' where the condition is NOT met
df.loc[df['c1'] != 'Value', 'c2'] = df[df['c1'] != 'Value', 'c3']

  • Esto es muy confuso, ¿puede mostrar algunas tablas para aclarar?

    – mlestudiante33

    13 de marzo de 2020 a las 1:38

avatar de usuario de nimbous
nimbo

Puedes usar pandas.DataFrame.mask para agregar virtualmente tantas condiciones como necesite:

data = {'a': [1,2,3,4,5], 'b': [6,8,9,10,11]}

d = pd.DataFrame.from_dict(data, orient="columns")
c = {'c1': (2, 'Value1'), 'c2': (3, 'Value2'), 'c3': (5, d['b'])}

d['new'] = np.nan
for value in c.values():
    d['new'].mask(d['a'] == value[0], value[1], inplace=True)

d['new'] = d['new'].fillna('Else')
d

Producción:

    a   b   new
0   1   6   Else
1   2   8   Value1
2   3   9   Value2
3   4   10  Else
4   5   11  11

  • Esto es muy confuso, ¿puede mostrar algunas tablas para aclarar?

    – mlestudiante33

    13 de marzo de 2020 a las 1:38

Yo creo Serie.mapa() ser muy legible y eficiente, por ejemplo:

df["c2"] = df["c1"].map(lambda x: 10 if x == 'Value' else x)

Me gusta porque si la lógica condicional se vuelve más compleja, puede moverla a una función y simplemente pasar esa función en lugar de la lambda.

Si necesita basar su lógica condicional en más de una columna, puede usar Marco de datos.apply() como otros sugieren.

¿Ha sido útil esta solución?