Tengo un siguiente DataFrame
:
from pandas import * df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})
Se parece a esto:
bar foo 0 1 a 1 2 b 2 3 c
Ahora quiero tener algo como:
bar 0 1 is a 1 2 is b 2 3 is c
¿Cómo puedo conseguir esto? Intenté lo siguiente:
df['foo'] = '%s is %s' % (df['bar'], df['foo'])
pero me da un resultado equivocado:
>>>print df.ix[0] bar a foo 0 a 1 b 2 c Name: bar is 0 1 1 2 2 Name: 0
Lo siento por una pregunta tonta, pero este pandas: combinar dos columnas en un DataFrame no fue útil para mí.
df['bar'] = df.bar.map(str) + " is " + df.foo
.
El problema en su código es que desea aplicar la operación en cada fila. La forma en que lo has escrito toma las columnas ‘bar’ y ‘foo’ completas, las convierte en cadenas y te devuelve una cadena grande. Puedes escribirlo como:
df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)
Es más largo que la otra respuesta pero es más genérico (puede usarse con valores que no son cadenas).
También podrías usar
df['bar'] = df['bar'].str.cat(df['foo'].values.astype(str), sep=' is ')
Aquí hay más soluciones, en orden creciente de rendimiento.
DataFrame.agg
Este es un enfoque simple basado en str.format
.
df['baz'] = df.agg('{0[bar]} is {0[foo]}'.format, axis=1) df foo bar baz 0 a 1 1 is a 1 b 2 2 is b 2 c 3 3 is c
También puede utilizar el formato de cadena f aquí:
df['baz'] = df.agg(lambda x: f"{x['bar']} is {x['foo']}", axis=1) df foo bar baz 0 a 1 1 is a 1 b 2 2 is b 2 c 3 3 is c
char.array
Convierta las columnas para concatenar como chararrays
, luego agréguelas.
a = np.char.array(df['bar'].values) b = np.char.array(df['foo'].values) df['baz'] = (a + b' is ' + b).astype(str) df foo bar baz 0 a 1 1 is a 1 b 2 2 is b 2 c 3 3 is c
zip
No puedo exagerar el grado de comprensión de las listas subestimadas en pandas.
df['baz'] = [str(x) + ' is ' + y for x, y in zip(df['bar'], df['foo'])]
Alternativamente, usar str.join
to concat (también escalará mejor):
df['baz'] = [ ' '.join([str(x), 'is', y]) for x, y in zip(df['bar'], df['foo'])]
df foo bar baz 0 a 1 1 is a 1 b 2 2 is b 2 c 3 3 is c
Las comprensiones de listas sobresalen en la manipulación de cadenas, porque las operaciones de cadenas son intrínsecamente difíciles de vectorizar, y la mayoría de las funciones “vectorizadas” de los pandas son básicamente envoltorios alrededor de los bucles. He escrito mucho sobre este tema en Para bucles con pandas: ¿cuándo debería importarme? . En general, si no tiene que preocuparse por la alineación del índice, use una comprensión de lista cuando trate con operaciones de cadenas y expresiones regulares.
La lista comp arriba de manera predeterminada no maneja los NaN. Sin embargo, siempre puede escribir una función envolviendo un bash, excepto si necesita manejarlo.
def try_concat(x, y): try: return str(x) + ' is ' + y except (ValueError, TypeError): return np.nan df['baz'] = [try_concat(x, y) for x, y in zip(df['bar'], df['foo'])]
perfplot
rendimiento de perfplot
: configuración y tiempos Podemos perfplot
estas soluciones usando perfplot
:
data = {'bar': {0: 1, 1: 2, 2: 3}, 'foo': {0: 'a', 1: 'b', 2: 'c'}} df_ = pd.DataFrame(data) perfplot.show( setup=lambda n: pd.concat([df_] * n, ignore_index=True), kernels=[ brenbarn, danielvelkov, chrimuelle, vladimiryashin, erickfis, cs1_format, cs1_fstrings, cs2, cs3 ], labels=[ 'brenbarn', 'danielvelkov', 'chrimuelle', 'vladimiryashin', 'erickfis', 'cs1_format', 'cs1_fstrings', 'cs2', 'cs3' ], n_range=[2**k for k in range(0, 8)], xlabel='N (x len(df_))', logy=True, equality_check=lambda x, y: (x == y).values.all() )
El rendimiento es relativo; La gráfica es logarítmica a lo largo del eje Y.
Funciones
def brenbarn(df): return df.assign(baz=df.bar.map(str) + " is " + df.foo) def danielvelkov(df): return df.assign(baz=df.apply( lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)) def chrimuelle(df): return df.assign( baz=df['bar'].astype(str).str.cat(df['foo'].values, sep=' is ')) def vladimiryashin(df): return df.assign(baz=df.astype(str).apply(lambda x: ' is '.join(x), axis=1)) def erickfis(df): return df.assign( baz=df.apply(lambda x: f"{x['bar']} is {x['foo']}", axis=1)) def cs1_format(df): return df.assign(baz=df.agg('{0[bar]} is {0[foo]}'.format, axis=1)) def cs1_fstrings(df): return df.assign(baz=df.agg(lambda x: f"{x['bar']} is {x['foo']}", axis=1)) def cs2(df): a = np.char.array(df['bar'].values) b = np.char.array(df['foo'].values) return df.assign(baz=(a + b' is ' + b).astype(str)) def cs3(df): return df.assign( baz=[str(x) + ' is ' + y for x, y in zip(df['bar'], df['foo'])])
df.astype(str).apply(lambda x: ' is '.join(x), axis=1) 0 1 is a 1 2 is b 2 3 is c dtype: object
La respuesta de @DanielVelkov es la correcta PERO el uso de literales de cadena es 10 veces más rápido
# Daniel's %timeit df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1) # String literals - python 3 %timeit df.apply(lambda x: f"{x['bar']} is {x['foo']}", axis=1)