libreria - Combina dos columnas de texto en el marco de datos en pandas/python
pandas merge by index (15)
Tengo una trama de datos de 20 x 4000 en Python usando pandas. Dos de estas columnas se denominan año y trimestre. Me gustaría crear una variable llamada período que hace que Year = 2000 y quarter = q2 en 2000q2
¿Alguien puede ayudar con eso?
A medida que sus datos se insertan en un marco de datos, este comando debería resolver su problema:
df[''period''] = df[[''Year'', ''quarter'']].apply(lambda x: '' ''.join(x.astype(str)), axis=1)
Aquí hay una implementación que encuentro muy versátil:
In [1]: import pandas as pd
In [2]: df = pd.DataFrame([[0, ''the'', ''quick'', ''brown''],
...: [1, ''fox'', ''jumps'', ''over''],
...: [2, ''the'', ''lazy'', ''dog'']],
...: columns=[''c0'', ''c1'', ''c2'', ''c3''])
In [3]: def str_join(df, sep, *cols):
...: from functools import reduce
...: return reduce(lambda x, y: x.astype(str).str.cat(y.astype(str), sep=sep),
...: [df[col] for col in cols])
...:
In [4]: df[''cat''] = str_join(df, ''-'', ''c0'', ''c1'', ''c2'', ''c3'')
In [5]: df
Out[5]:
c0 c1 c2 c3 cat
0 0 the quick brown 0-the-quick-brown
1 1 fox jumps over 1-fox-jumps-over
2 2 the lazy dog 2-the-lazy-dog
Aunque la respuesta de @silvado es buena si cambia df.map(str)
a df.astype(str)
será más rápido:
import pandas as pd
df = pd.DataFrame({''Year'': [''2014'', ''2015''], ''quarter'': [''q1'', ''q2'']})
In [131]: %timeit df["Year"].map(str)
10000 loops, best of 3: 132 us per loop
In [132]: %timeit df["Year"].astype(str)
10000 loops, best of 3: 82.2 us per loop
Como muchos han mencionado anteriormente, debe convertir cada columna en cadena y luego usar el operador más para combinar dos columnas de cadena. Puede obtener una gran mejora de rendimiento utilizando NumPy.
%timeit df[''Year''].values.astype(str) + df.quarter
71.1 ms ± 3.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit df[''Year''].astype(str) + df[''quarter'']
565 ms ± 22.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
El método cat()
del .str
funciona muy bien para esto:
>>> import pandas as pd
>>> df = pd.DataFrame([["2014", "q1"],
... ["2015", "q3"]],
... columns=(''Year'', ''Quarter''))
>>> print(df)
Year Quarter
0 2014 q1
1 2015 q3
>>> df[''Period''] = df.Year.str.cat(df.Quarter)
>>> print(df)
Year Quarter Period
0 2014 q1 2014q1
1 2015 q3 2015q3
cat()
incluso le permite agregar un separador por lo que, por ejemplo, suponga que solo tiene números enteros para el año y el período, puede hacer esto:
>>> import pandas as pd
>>> df = pd.DataFrame([[2014, 1],
... [2015, 3]],
... columns=(''Year'', ''Quarter''))
>>> print(df)
Year Quarter
0 2014 1
1 2015 3
>>> df[''Period''] = df.Year.astype(str).str.cat(df.Quarter.astype(str), sep=''q'')
>>> print(df)
Year Quarter Period
0 2014 1 2014q1
1 2015 3 2015q3
Unir varias columnas es solo una cuestión de pasar una lista de series o un marco de datos que contenga todas las columnas excepto la primera como parámetro para str.cat()
invocado en la primera columna (Series):
>>> df = pd.DataFrame(
... [[''USA'', ''Nevada'', ''Las Vegas''],
... [''Brazil'', ''Pernambuco'', ''Recife'']],
... columns=[''Country'', ''State'', ''City''],
... )
>>> df[''AllTogether''] = df[''Country''].str.cat(df[[''State'', ''City'']], sep='' - '')
>>> print(df)
Country State City AllTogether
0 USA Nevada Las Vegas USA - Nevada - Las Vegas
1 Brazil Pernambuco Recife Brazil - Pernambuco - Recife
Tenga en cuenta que si su marco de datos / serie de pandas tiene valores nulos, debe incluir el parámetro na_rep para reemplazar los valores de NaN por una cadena, de lo contrario, la columna combinada se establecerá de manera predeterminada en NaN.
Mi respuesta es un poco tarde, pero creo que más tarde es mejor que nunca. Supongamos que su dataframe
es df
con columnas Year
y Quarter
.
import pandas as pd
df = pd.DataFrame({''Quarter'':''q1 q2 q3 q4''.split(), ''Year'':''2000''})
Supongamos que queremos ver el marco de datos;
df
>>> Quarter Year
0 q1 2000
1 q2 2000
2 q3 2000
3 q4 2000
Finalmente, concatene el Year
y el Quarter
siguiente manera.
df[''Period''] = df[''Year''] + '' '' + df[''Quarter'']
Ahora puede print
df
para ver el marco de datos resultante.
df
>>> Quarter Year Period
0 q1 2000 2000 q1
1 q2 2000 2000 q2
2 q3 2000 2000 q3
3 q4 2000 2000 q4
Si no desea el espacio entre el año y el trimestre, simplemente elimínelo haciendo;
df[''Period''] = df[''Year''] + df[''Quarter'']
Espero que esto te ayude.
Usar zip
podría ser aún más rápido:
dataframe["period"] = ([''''.join(i) for i in
zip(dataframe["Year"].map(str),dataframe["quarter"])])
En el conjunto de datos a continuación, zip()
fue el más completo de todos: https://.com/a/50316945/7386332
import pandas as pd
data = ''''''/
ID,Host,Protocol,Port
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,445
1,10.0.0.10,tcp,49707
1,10.0.0.10,tcp,49672
1,10.0.0.10,tcp,49670''''''
df = pd.read_csv(pd.compat.StringIO(data)) # Recreates a sample dataframe
df = pd.concat([df]*10000)
%timeit df[''Host''] + "/" + df[''Protocol''] + "/" + df[''Port''].map(str)
%timeit [''/''.join(i) for i in zip(df[''Host''],df[''Protocol''],df[''Port''].map(str))]
%timeit [''/''.join(i) for i in df[[''Host'',''Protocol'',''Port'']].astype(str).values]
10 loops, best of 3: 39.7 ms per loop
10 loops, best of 3: 35.9 ms per loop
10 loops, best of 3: 162 ms per loop
Uso de una función lamba esta vez con string.format ().
import pandas as pd
df = pd.DataFrame({''Year'': [''2014'', ''2015''], ''Quarter'': [''q1'', ''q2'']})
print df
df[''YearQuarter''] = df[[''Year'',''Quarter'']].apply(lambda x : ''{}{}''.format(x[0],x[1]), axis=1)
print df
Quarter Year
0 q1 2014
1 q2 2015
Quarter Year YearQuarter
0 q1 2014 2014q1
1 q2 2015 2015q2
Esto le permite trabajar con cadenas y valores de formato según sea necesario.
import pandas as pd
df = pd.DataFrame({''Year'': [''2014'', ''2015''], ''Quarter'': [1, 2]})
print df.dtypes
print df
df[''YearQuarter''] = df[[''Year'',''Quarter'']].apply(lambda x : ''{}q{}''.format(x[0],x[1]), axis=1)
print df
Quarter int64
Year object
dtype: object
Quarter Year
0 1 2014
1 2 2015
Quarter Year YearQuarter
0 1 2014 2014q1
1 2 2015 2015q2
Utilice .combine_first
.
df[''Period''] = df[''Year''].combine_first(df[''Quarter''])
más eficiente es
def concat_df_str1(df):
""" run time: 1.3416s """
return pd.Series([''''.join(row.astype(str)) for row in df.values], index=df.index)
Y aquí hay una prueba de tiempo:
import numpy as np
import pandas as pd
from time import time
def concat_df_str1(df):
""" run time: 1.3416s """
return pd.Series([''''.join(row.astype(str)) for row in df.values], index=df.index)
def concat_df_str2(df):
""" run time: 5.2758s """
return df.astype(str).sum(axis=1)
def concat_df_str3(df):
""" run time: 5.0076s """
df = df.astype(str)
return df[0] + df[1] + df[2] + df[3] + df[4] + /
df[5] + df[6] + df[7] + df[8] + df[9]
def concat_df_str4(df):
""" run time: 7.8624s """
return df.astype(str).apply(lambda x: ''''.join(x), axis=1)
def main():
df = pd.DataFrame(np.zeros(1000000).reshape(100000, 10))
df = df.astype(int)
time1 = time()
df_en = concat_df_str4(df)
print(''run time: %.4fs'' % (time() - time1))
print(df_en.head(10))
if __name__ == ''__main__'':
main()
final, cuando se usa sum
(concat_df_str2), el resultado no es simplemente concat, se convertirá en entero.
otra forma de hacer esto:
df[''period''] = df[''Year''].astype(str) + df[''quarter'']
o un poco más lento:
df[''period''] = df[[''Year'',''quarter'']].astype(str).sum(axis=1)
Vamos a probarlo en 200K filas DF:
In [250]: df
Out[250]:
Year quarter
0 2014 q1
1 2015 q2
In [251]: df = pd.concat([df] * 10**5)
In [252]: df.shape
Out[252]: (200000, 2)
ACTUALIZACIÓN: Gráfico de tiempo Pandas 0.23.0
ACTUALIZACIÓN: nuevos tiempos usando Pandas 0.19.0
Tiempo sin optimización de CPU / GPU (ordenados de más rápido a más lento):
In [107]: %timeit df[''Year''].astype(str) + df[''quarter'']
10 loops, best of 3: 131 ms per loop
In [106]: %timeit df[''Year''].map(str) + df[''quarter'']
10 loops, best of 3: 161 ms per loop
In [108]: %timeit df.Year.str.cat(df.quarter)
10 loops, best of 3: 189 ms per loop
In [109]: %timeit df.loc[:, [''Year'',''quarter'']].astype(str).sum(axis=1)
1 loop, best of 3: 567 ms per loop
In [110]: %timeit df[[''Year'',''quarter'']].astype(str).sum(axis=1)
1 loop, best of 3: 584 ms per loop
In [111]: %timeit df[[''Year'',''quarter'']].apply(lambda x : ''{}{}''.format(x[0],x[1]), axis=1)
1 loop, best of 3: 24.7 s per loop
Tiempo usando la optimización de CPU / GPU:
In [113]: %timeit df[''Year''].astype(str) + df[''quarter'']
10 loops, best of 3: 53.3 ms per loop
In [114]: %timeit df[''Year''].map(str) + df[''quarter'']
10 loops, best of 3: 65.5 ms per loop
In [115]: %timeit df.Year.str.cat(df.quarter)
10 loops, best of 3: 79.9 ms per loop
In [116]: %timeit df.loc[:, [''Year'',''quarter'']].astype(str).sum(axis=1)
1 loop, best of 3: 230 ms per loop
In [117]: %timeit df[[''Year'',''quarter'']].astype(str).sum(axis=1)
1 loop, best of 3: 230 ms per loop
In [118]: %timeit df[[''Year'',''quarter'']].apply(lambda x : ''{}{}''.format(x[0],x[1]), axis=1)
1 loop, best of 3: 9.38 s per loop
dataframe["period"] = dataframe["Year"].astype(str).add(dataframe["quarter"])
o si los valores son como [2000] [4] y quieren hacer [2000q4]
dataframe["period"] = dataframe["Year"].astype(str).add(''q'').add(dataframe["quarter"]).astype(str)
La sustitución de .astype(str)
con .map(str)
también funciona.
dataframe["period"] = dataframe["Year"].map(str) + dataframe["quarter"]
def madd(x):
"""Performs element-wise string concatenation with multiple input arrays.
Args:
x: iterable of np.array.
Returns: np.array.
"""
for i, arr in enumerate(x):
if type(arr.item(0)) is not str:
x[i] = x[i].astype(str)
return reduce(np.core.defchararray.add, x)
Por ejemplo:
data = list(zip([2000]*4, [''q1'', ''q2'', ''q3'', ''q4'']))
df = pd.DataFrame(data=data, columns=[''Year'', ''quarter''])
df[''period''] = madd([df[col].values for col in [''Year'', ''quarter'']])
df
Year quarter period
0 2000 q1 2000q1
1 2000 q2 2000q2
2 2000 q3 2000q3
3 2000 q4 2000q4
df = pd.DataFrame({''Year'': [''2014'', ''2015''], ''quarter'': [''q1'', ''q2'']})
df[''period''] = df[[''Year'', ''quarter'']].apply(lambda x: ''''.join(x), axis=1)
Rinde este marco de datos
Year quarter period
0 2014 q1 2014q1
1 2015 q2 2015q2
Este método generaliza a un número arbitrario de columnas de cadena reemplazando df[[''Year'', ''quarter'']]
con cualquier segmento de columna de su marco de datos, por ejemplo, df.iloc[:,0:2].apply(lambda x: ''''.join(x), axis=1)
.
Puede consultar más información sobre el método apply () here