Autor Tema: Relacionar dataframes por coincidencia Python

0 Usuarios y 1 Visitante están viendo este tema.

08 Enero, 2022, 01:40 pm
Leído 827 veces

MeFon

  • $$\Large \color{#6a84c0}\pi$$
  • Mensajes: 16
  • País: co
  • Karma: +0/-0
Hola a todos, agradezco me puedan ayudar con lo siguiente en Python
Supongamos que tenemos dos dataframes
Código: [Seleccionar]
df1= pd.DataFrame(np.array([["Bogota"], ["Sevilla"], ["Venecia"],["Madrid"]]),
                   columns=['Ciudad'])
df2= pd.DataFrame(np.array([["Bogota - Cali - Medellin - Pasto - Bucaramanga", "Colombia"],\
                            ["Dijon - Paris - Marsella - Estrasburgo","Francia"], ["Birmingham - Londres - Hastings", "Inglaterra"],\
                            ["Roma - Turim - Bari - Venecia - Florencia","Italia"],["Buenos Aires - Mendoza - Rosaro - Bariloche","Argentina"],\
                            ["Madrid - Barcelona - Valencia - Sevilla","España"]]),
                   columns=['Ciudades', 'Pais'])

Estos dataframes lucen así


Quiero saber cómo puedo relacionar la columna "Ciudad" del dataframe 1 con la columna "Ciudades " del dataframe 2, teniendo en cuenta que no tienen los mismos registros sino que una está contenida en la otra. Lo que se desea es que se cree una nueva columna que asigne al df1 el país que le corresponde a cada ciudad teniendo en cuenta la información de df2.
Es decir, que muestre algo así


De nuevo mil gracias

08 Enero, 2022, 03:25 pm
Respuesta #1

geómetracat

  • Moderador Global
  • Mensajes: 3,924
  • País: es
  • Karma: +0/-0
  • Sexo: Masculino
Si sabes que cada ciudad de df1 está en una y solo una entrada de df2, puedes hacer esto:
Código: [Seleccionar]
df1["Pais"] = df1["Ciudad"].apply(lambda x: df2[df2["Ciudades"].str.contains(x)]["Pais"].iloc[0])
La lógica es que para cada ciudad en df1, x, primero filtras df2 para que te saque únicamente las filas en que la ciudad x está en el campo "Ciudades", df2[df2["Ciudades"].str.contains(x)]. Si la ciudad está en una única entrada de df2 este será un dataframe con una única fila, así que tomas el valor de "País" de la única fila que hay.
La ecuación más bonita de las matemáticas: \( d^2=0 \)

11 Enero, 2022, 03:01 am
Respuesta #2

MeFon

  • $$\Large \color{#6a84c0}\pi$$
  • Mensajes: 16
  • País: co
  • Karma: +0/-0
Hola, me funciona perfecto, sin embargo, estaba probando cómo hacer si una ciudad no está, por ejemplo que en campo ciudad dijera Guadalajara, si pasa eso genera un error, cómo puedo establecer que cuándo no esté genere en País algo como no identificado, y no que genere error.
Si no está la ciudad me sale esto
IndexError: single positional indexer is out-of-bounds

11 Enero, 2022, 07:45 am
Respuesta #3

geómetracat

  • Moderador Global
  • Mensajes: 3,924
  • País: es
  • Karma: +0/-0
  • Sexo: Masculino
Sí, por eso decía que sirve solo si sabes seguro que cada ciudad va a estar en algún país (y en un único país).

Si no se cumplen las condiciones es mejor dejar de hacerlo en una única linea y hacer la función aparte. Por ejemplo, el siguiente código devuelve "No identificado" si la ciudad no aparece en el df de los paises, el nombre de la ciudad si corresponde a un único país, y una cadena con todos los paises que tienen una ciudad con ese nombre, separados con ' - ', en caso de que haya más de uno. Puedes modificar la función para que te lo saque a tu gusto, con otro formato o que ponga otra cosa en vez de "No identificado", etc.

Código: [Seleccionar]
def identifica_pais(ciudad, df_paises):
    paises = df_paises[df_paises["Ciudades"].str.contains(ciudad)]["Pais"]
    if len(paises) == 0:
        return "No identificado"
    else:
        return ' - '.join(paises.tolist())
   

df1["Pais"] = df1["Ciudad"].apply(lambda x: identifica_pais(x, df2))
La ecuación más bonita de las matemáticas: \( d^2=0 \)

12 Enero, 2022, 03:16 am
Respuesta #4

MeFon

  • $$\Large \color{#6a84c0}\pi$$
  • Mensajes: 16
  • País: co
  • Karma: +0/-0
Muchas gracias, estuve mirando con varios países y está muy bien, aunque si escribo en df1 por ejemplo Sev, lo toma como si fuera correcto y estuviera en España. Cómo se podría evitar eso, quise mirar si con fullmatch o match, pero ellos requieren que sean casi que iguales las cadenas, no sé cómo restringir a que sea la palabra completa, ¿se puede?

12 Enero, 2022, 08:28 am
Respuesta #5

geómetracat

  • Moderador Global
  • Mensajes: 3,924
  • País: es
  • Karma: +0/-0
  • Sexo: Masculino
Muchas gracias, estuve mirando con varios países y está muy bien, aunque si escribo en df1 por ejemplo Sev, lo toma como si fuera correcto y estuviera en España. Cómo se podría evitar eso, quise mirar si con fullmatch o match, pero ellos requieren que sean casi que iguales las cadenas, no sé cómo restringir a que sea la palabra completa, ¿se puede?
Por poderse se puede hacer todo (o casi), otra cosa es que sea más o menos complicado.

Una manera sencilla de que solo identifique el país si el nombre de la ciudad está entera es hacer esto:
Código: [Seleccionar]
def identifica_pais(ciudad, df_paises):
    paises = df_paises[df_paises["Ciudades"].apply(lambda x: ciudad in x.split(' - '))]["Pais"]
    if len(paises) == 0:
        return "No identificado"
    else:
        return ' - '.join(paises.tolist())
   

df1["Pais"] = df1["Ciudad"].apply(lambda x: identifica_pais(x, df2))
La lógica aquí es que en vez de comprobar si el nombre de ciudad es una subcadena de las entradas de los países, primero partes la cadena de los países en una lista por donde aparece ' - ' (de manera que por ejemplo la cadena "Sevilla - Madrid - Barcelona" se transforma en ["Sevilla", "Madrid", "Barcelona"]). Esto es lo que hace el x.split(' - '), donde x es la cadena de ciudades de un país del df2. Y después simplemente miras si tu nombre de ciudad está en la lista o no.
La ecuación más bonita de las matemáticas: \( d^2=0 \)