python - dividir una columna numérica de seis dígitos en columnas separadas con un dígito
pandas numpy (8)
¡Conviértelo primero en una cuerda!
Además, se incluye un
zfill
por si no todos los números son de 6 dígitos
dat = [list(map(int, str(x).zfill(6))) for x in df.Number]
d = pd.DataFrame(dat, df.index).rename(columns=lambda x: f''x{x + 1}'')
df.join(d)
Number x1 x2 x3 x4 x5 x6
0 654321 6 5 4 3 2 1
1 223344 2 2 3 3 4 4
Detalles
Esto obtiene los dígitos
dat = [list(map(int, str(x).zfill(6))) for x in df.Number]
dat
[[6, 5, 4, 3, 2, 1], [2, 2, 3, 3, 4, 4]]
Esto crea un nuevo marco de datos con el mismo índice que
df
Y
renombra las columnas para tener una
''x''
al frente y comenzar con
''x1''
y no
''x0''
d = pd.DataFrame(dat, df.index).rename(columns=lambda x: f''x{x + 1}'')
d
x1 x2 x3 x4 x5 x6
0 6 5 4 3 2 1
1 2 2 3 3 4 4
¿Cómo puedo usar pandas o numpy separar una columna de 6 dígitos enteros en 6 columnas con un dígito cada una?
import pandas as pd
import numpy as np
df = pd.Series(range(123456,123465))
df = pd.DataFrame(df)
df.head()
lo que tengo es como este a continuación
Number
654321
223344
El resultado deseado debe ser como este a continuación.
Number | x1 | x2 | x3 | x4 | x5 | x6 |
654321 | 6 | 5 | 4 | 3 | 2 | 1 |
223344 | 2 | 2 | 3 | 3 | 4 | 4 |
MCVE
Aquí hay una sugerencia simple:
import pandas as pd
# MCVE dataframe:
df = pd.DataFrame([123456, 456789, 135797, 123, 123456789], columns=[''number''])
def digit(x, n):
"""Return the n-th digit of integer in base 10"""
return (x // 10**n) % 10
def digitize(df, key, n):
"""Extract n less significant digits from an integer in base 10"""
for i in range(n):
df[''x%d'' % i] = digit(df[key], n-i-1)
# Apply function on dataframe (inplace):
digitize(df, ''number'', 6)
Para el marco de datos de prueba, devuelve:
number x0 x1 x2 x3 x4 x5
0 123456 1 2 3 4 5 6
1 456789 4 5 6 7 8 9
2 135797 1 3 5 7 9 7
3 123 0 0 0 1 2 3
4 123456789 4 5 6 7 8 9
Observaciones
Este método evita la necesidad de convertir en
string
y luego volver a
int
a
int
.
Se basa en aritmética de enteros modulares, debajo de los detalles de las operaciones:
10**3 # int: 1000 (integer power)
54321 // 10**3 # int: 54 (quotient of integer division)
(54321 // 10**3) % 10 # int: 4 (remainder of integer division, modulo)
Por último, pero no menos importante, es seguro y exacto para un número menor de
n
dígitos o mayor que (observe que devuelve los
n
dígitos menos significativos en el último caso).
Manera simple de evitar:
>>> df
number
0 123456
1 456789
2 135797
Primero convierta la columna en cadena
>>> df[''number''] = df[''number''].astype(str)
Cree las nuevas columnas usando indexación de cadenas
>>> df[''x1''] = df[''number''].str[0]
>>> df[''x2''] = df[''number''].str[1]
>>> df[''x3''] = df[''number''].str[2]
>>> df[''x4''] = df[''number''].str[3]
>>> df[''x5''] = df[''number''].str[4]
>>> df[''x6''] = df[''number''].str[5]
>>> df
number x1 x2 x3 x4 x5 x6
0 123456 1 2 3 4 5 6
1 456789 4 5 6 7 8 9
2 135797 1 3 5 7 9 7
>>> df.drop(''number'', axis=1, inplace=True)
>>> df
x1 x2 x3 x4 x5 x6
0 1 2 3 4 5 6
1 4 5 6 7 8 9
2 1 3 5 7 9 7
@ otro truco con
str.split()
>>> df = df[''number''].str.split(''(/d{1})'', expand=True).add_prefix(''x'').drop(columns=[''x0'', ''x2'', ''x4'', ''x6'', ''x8'', ''x10'', ''x12''])
>>> df
x1 x3 x5 x7 x9 x11
0 1 2 3 4 5 6
1 4 5 6 7 8 9
2 1 3 5 7 9 7
>>> df.rename(columns={''x3'':''x2'', ''x5'':''x3'', ''x7'':''x4'', ''x9'':''x5'', ''x11'':''x6''})
x1 x2 x3 x4 x5 x6
0 1 2 3 4 5 6
1 4 5 6 7 8 9
2 1 3 5 7 9 7
O
>>> df = df[''number''].str.split(r''(/d{1})'', expand=True).T.replace('''', np.nan).dropna().T
>>> df
1 3 5 7 9 11
0 1 2 3 4 5 6
1 4 5 6 7 8 9
2 1 3 5 7 9 7
>>> df.rename(columns={1:''x1'', 3:''x2'', 5:''x3'', 7:''x4'', 9:''x5'', 11:''x6''})
x1 x2 x3 x4 x5 x6
0 1 2 3 4 5 6
1 4 5 6 7 8 9
2 1 3 5 7 9 7
Podrías usar
np.unravel_index
df = pd.DataFrame({''Number'': [654321,223344]})
def split_digits(df):
# get data as numpy array
numbers = df[''Number''].to_numpy()
# extract digits
digits = np.unravel_index(numbers, 6*(10,))
# create column headers
columns = [''Number'', *(f''x{i}'' for i in "123456")]
# build and return new data frame
return pd.DataFrame(np.stack([numbers, *digits], axis=1), columns=columns, index=df.index)
split_digits(df)
# Number x1 x2 x3 x4 x5 x6
# 0 654321 6 5 4 3 2 1
# 1 223344 2 2 3 3 4 4
timeit(lambda:split_digits(df),number=1000)
# 0.3550272472202778
Gracias @ GZ0 por algunos consejos sobre
pandas
.
Realmente me gustó la respuesta de @ user3483203.
Creo que
.str.findall
podría funcionar con cualquier número de dígitos:
df = pd.DataFrame({
''Number'' : [65432178888, 22334474343]
})
u = df[''Number''].astype(str).str.findall(r''(/w)'')
df.join(pd.DataFrame(list(u)).rename(columns=lambda c: f''x{c+1}'')).apply(pd.to_numeric)
Number x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11
0 65432178888 6 5 4 3 2 1 7 8 8 8 8
1 22334474343 2 2 3 3 4 4 7 4 3 4 3
Si bien las soluciones basadas en cadenas son más simples y probablemente lo suficientemente buenas en la mayoría de los casos, puede hacerlo con las matemáticas que, si tiene un gran conjunto de datos, pueden marcar una diferencia significativa en la velocidad.
import numpy as np
import pandas as pd
df = pd.DataFrame({''Number'': [654321, 223344]})
num_cols = int(np.log10(df[''Number''].max() - 1)) + 1
vals = (df[''Number''].values[:, np.newaxis] // (10 ** np.arange(num_cols - 1, -1, -1))) % 10
df_digits = pd.DataFrame(vals, columns=[f''x{i + 1}'' for i in range(num_cols)
df2 = pd.concat([df, df_digits])], axis=1)
print(df2)
# Number x1 x2 x3 x4 x5 x6
# 0 654321 6 5 4 3 2 1
# 1 223344 2 2 3 3 4 4
Suponiendo que todos los números tienen la misma longitud (tienen el mismo número de dígitos), lo haría de la siguiente manera usando
numpy
:
import numpy as np
a = np.array([[654321],[223344]])
str_a = a.astype(str)
out = np.apply_along_axis(lambda x:list(x[0]),1,str_a)
print(out)
Salida:
[[''6'' ''5'' ''4'' ''3'' ''2'' ''1'']
[''2'' ''2'' ''3'' ''3'' ''4'' ''4'']]
Tenga en cuenta que
out
es actualmente
np.array
de
str
s, puede convertirlo en
int
si es necesario.
Un poco de diversión con vistas, suponiendo que cada número tenga 6 dígitos:
u = df[[''Number'']].to_numpy().astype(''U6'').view(''U1'').astype(int)
df.join(pd.DataFrame(u).rename(columns=lambda c: f''x{c+1}''))
Number x1 x2 x3 x4 x5 x6
0 654321 6 5 4 3 2 1
1 223344 2 2 3 3 4 4