This question has already been answered, but I believe that it would be nice to add some useful methods that were not previously discussed, and compare all the methods proposed so far in terms of performance.
Here are some useful solutions to this problem, in increasing order of productivity.
This is a simple str.format -based approach.
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
You can also use f-string formatting here:
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 -based Concatenation
Convert the columns to chararrays like chararrays and then stack them together.
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
I can not exaggerate how underestimated the understanding of lists in pandas is.
df['baz'] = [str(x) + ' is ' + y for x, y in zip(df['bar'], df['foo'])]
You can also use str.join for str.join (it will also scale better):
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
List comprehensions are excellent at manipulating strings, because string operations are inherently difficult to vectorize, and most of the "vectorized" pandas functions are mostly wrappers around loops. I wrote a lot on this topic in " For loops with pandas." When do I need it? In general, if you don't need to worry about index alignment, use list comprehension when dealing with strings and regular expression operations.
The above list does not process NaN by default. However, you can always write a function that contains an attempt, unless you need to process it.
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 performance perfplot

Chart created using perflot . Here is a complete list of codes .
the functions
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'])])