Concatenación de cuerdas de dos columnas pandas.

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 

Concatenación basada en 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 

Comprensión de lista con 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() ) 

introduzca la descripción de la imagen aquí

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)