J'ai une DataFrame
suivante:
from pandas import *
df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})
Cela ressemble à ceci:
bar foo
0 1 a
1 2 b
2 3 c
Maintenant, je veux avoir quelque chose comme:
bar
0 1 is a
1 2 is b
2 3 is c
Comment puis-je atteindre cet objectif? J'ai essayé ce qui suit:
df['foo'] = '%s is %s' % (df['bar'], df['foo'])
mais cela me donne un résultat erroné:
>>>print df.ix[0]
bar a
foo 0 a
1 b
2 c
Name: bar is 0 1
1 2
2
Name: 0
Désolé pour une question idiote, mais celle-ci pandas: combiner deux colonnes dans un DataFrame ne m'a pas été utile.
df['bar'] = df.bar.map(str) + " is " + df.foo
.
Le problème dans votre code est que vous souhaitez appliquer l'opération à chaque ligne. La façon dont vous l'avez écrit prend cependant toutes les colonnes "bar" et "foo", les convertit en chaînes et vous rend une grande chaîne. Vous pouvez l'écrire comme ceci:
df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)
Il est plus long que l'autre réponse mais est plus générique (peut être utilisé avec des valeurs qui ne sont pas des chaînes).
Vous pouvez aussi utiliser
df['bar'] = df['bar'].str.cat(df['foo'].values.astype(str), sep=' is ')
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
Voici d'autres solutions, par ordre croissant de performances.
DataFrame.agg
C’est une approche simple basée sur 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
Vous pouvez également utiliser le formatage f-string ici:
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
addition
Convertissez les colonnes pour les concaténer en tant que chararrays
, puis additionnez-les.
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
Liste Compréhension avec Zip
Je ne peux pas exagérer à quel point la compréhension des listes est sous-estimée dans les pandas.
df['baz'] = [str(x) + ' is ' + y for x, y in Zip(df['bar'], df['foo'])]
Sinon, utilisez str.join
pour concaténer (sera également amélioré):
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
Compréhension de liste Excel lors de la manipulation de chaînes, car les opérations sur les chaînes de caractères sont par nature difficiles à vectoriser, et la plupart des fonctions "vectorisées" de pandas sont essentiellement des wrappers autour de boucles. J'ai beaucoup écrit sur ce sujet dans Pour les boucles avec des pandas - Quand devrais-je m'en soucier? . En général, si vous n'avez pas à vous soucier de l'alignement d'index, utilisez une compréhension de la liste pour traiter des opérations de chaîne et de regex.
La liste comp ci-dessus par défaut ne gère pas les NaN. Cependant, vous pouvez toujours écrire une fonction encapsulant un essai, sauf si vous devez le gérer.
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
Mesures de performance - Configuration et minutages
Nous pouvons chronométrer ces solutions en utilisant 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()
)
La performance est relative; l'intrigue est logarithmique le long de l'axe des ordonnées.
Les fonctions
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'])])
La réponse de @DanielVelkov est la bonne MAIS L'utilisation de littéraux de chaîne est 10 fois plus rapide
# 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)