Crear columna dataframe a partir de otra

publicado por: Anonymous

necesito crear una columna dataframe según los valores de otra.

Dataframe:

Ocurrences  level         movesRemaining
0   16      Level_0019    ["0","1"]
1   594     Level_0022    ["4","1"]
2   1109    Level_0023    ["5","2"]
3   300     Level_0024    ["0","3"]
4   9       Level_0028    ["2","4"]

Necesito crear una columnan moves1 y otra columna moves2 donde en una entre el primer número de la tupla y en otra el segundo. Decir que no es una lista lo que hay ahí, es decir, es tipo string, así me han llegado los datos.

Si hay alguna manera de pasarlo todo a lista entonces ya podría acceder facilmente.

Un saludo

solución

Lo primero sería convertir los elementos la columna movesRemaining en listas python, en vez de cadenas de caracteres. Puedes tratar de hacerlo “a mano”, quitando los corchetes que tienen alrededor, usando str.split() para partir por la coma, etc. Pero ¿por qué complicarse cuando json.loads() puede hacer lo mismo de forma mucho más robusta? Así:

import json

df.movesRemaining = df.movesRemaining.apply(json.loads)

Tras esto, los elementos de esa columna ya son listas python “de verdad”. Ahora, separarlo en otras dos columnas puede hacerse de muchas formas. Una de ellas:

df["moves1"], df["moves2"] = zip(*df.movesRemaining)

con el resultado:

    Ocurrences       level movesRemaining moves1 moves2
 0          16  Level_0019         [0, 1]      0      1
 1         594  Level_0022         [4, 1]      4      1
 2        1109  Level_0023         [5, 2]      5      2
 3         300  Level_0024         [0, 3]      0      3
 4           9  Level_0028         [2, 4]      2      4

Edición

En un comentario el usuario indica que algunas de las listas podrían tener tres elementos, y algunas otras sólo uno, y que en estos casos necesitaría desempaquetarlos en tres columnas o sólo una, respectivamente.

Naturalmente, la tabla no puede tener mayor o menor número de columnas en cada fila. Si al menos un elemento de movesRemainig es una lista de tres, habrá que desempaquetar en una tercera columna, y todas las filas que sólo tengan dos elementos en la lista tendrán None en esta tercera columna. Y si alguna fila sólo tiene un elemento en la lista, ese elemento irá a la columna moves1, mientras que en las columnas moves2 y moves3 habrá None.

Si este comportamiento te vale, la solución es usar zip_longest() en lugar de zip().

Pongamos un ejemplo. Este es el dataframe:

    Ocurrences       level movesRemaining
 0          16  Level_0019      ["0","1"]
 1         594  Level_0022      ["4","1"]
 2        1109  Level_0023      ["5","2"]
 3         300  Level_0024  ["0","3","5"]
 4           9  Level_0028      ["2","4"]
 5          50  Level_0029          ["4"]

Este es el código que sugiero:

import json
from itertools import zip_longest

df["movesRemaining"] = df["movesRemaining"].apply(json.loads)
df["moves1"], df["moves2"], df["moves3"] = zip_longest(*df["movesRemaining"])

Y este es el resultado:

    Ocurrences       level movesRemaining moves1 moves2 moves3
 0          16  Level_0019         [0, 1]      0      1   None
 1         594  Level_0022         [4, 1]      4      1   None
 2        1109  Level_0023         [5, 2]      5      2   None
 3         300  Level_0024      [0, 3, 5]      0      3      5
 4           9  Level_0028         [2, 4]      2      4   None
 5          50  Level_0029            [4]      4   None   None

Si en vez de None quieres cualquier otro valor, puedes especificarlo en el parámetro fillvalue de zip_longest(). Por ejemplo:

df["moves1"], df["moves2"], df["moves3"] = zip_longest(*df["movesRemaining"], fillvalue="-")

produce

    Ocurrences       level movesRemaining moves1 moves2 moves3
 0          16  Level_0019         [0, 1]      0      1      -
 1         594  Level_0022         [4, 1]      4      1      -
 2        1109  Level_0023         [5, 2]      5      2      -
 3         300  Level_0024      [0, 3, 5]      0      3      5
 4           9  Level_0028         [2, 4]      2      4      -
 5          50  Level_0029            [4]      4      -      -
Respondido por: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *