Ich habe folgende DataFrame
:
from pandas import *
df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})
Es sieht aus wie das:
bar foo
0 1 a
1 2 b
2 3 c
Jetzt möchte ich etwas haben wie:
bar
0 1 is a
1 2 is b
2 3 is c
Wie kann ich das erreichen? Ich habe folgendes ausprobiert:
df['foo'] = '%s is %s' % (df['bar'], df['foo'])
aber es gibt mir ein falsches Ergebnis:
>>>print df.ix[0]
bar a
foo 0 a
1 b
2 c
Name: bar is 0 1
1 2
2
Name: 0
Entschuldigung für eine dumme Frage, aber diese eine Pandas: Kombinieren Sie zwei Spalten in einem DataFrame war mir nicht hilfreich.
df['bar'] = df.bar.map(str) + " is " + df.foo
.
Das Problem in Ihrem Code besteht darin, dass Sie die Operation auf jede Zeile anwenden möchten. Die Art und Weise, wie Sie es geschrieben haben, nimmt die gesamten Spalten 'bar' und 'foo' in acht, wandelt sie in Strings um und gibt Ihnen einen großen String zurück. Sie können es schreiben wie:
df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)
Es ist länger als die andere Antwort, aber es ist allgemeiner (kann mit Werten verwendet werden, die keine Zeichenfolgen sind).
Sie könnten auch verwenden
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
Hier sind weitere Lösungen in aufsteigender Reihenfolge der Leistung.
DataFrame.agg
Dies ist ein einfacher, auf str.format
basierender Ansatz.
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
Sie können hier auch die F-String-Formatierung verwenden:
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
Zusatz
Konvertieren Sie die Spalten in chararrays
in verketten und fügen Sie sie dann zusammen.
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
LISTENVERSTEHEN MIT Zip
Ich kann nicht überschätzen, wie unterschätzte Listenverständnisse in Pandas sind.
df['baz'] = [str(x) + ' is ' + y for x, y in Zip(df['bar'], df['foo'])]
Alternativ können Sie str.join
für concat verwenden (wird auch besser skaliert):
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
Listenverstehen von Excel bei der Zeichenfolgenmanipulation, da Zeichenkettenoperationen von Natur aus schwer zu vektorisieren sind und die meisten "vektorisierten" Pandabefunktionen die Schleifen grundsätzlich umschließen. Ich habe ausführlich zu diesem Thema in Für Schleifen mit Pandas - Wann sollte ich mich interessieren? Wenn Sie sich nicht um die Indexausrichtung kümmern müssen, sollten Sie im Allgemeinen ein Listenverständnis verwenden, wenn Sie mit Zeichenfolgen- und Regex-Vorgängen arbeiten.
Die obige Listenkomposition behandelt standardmäßig keine NaNs. Sie können jedoch immer eine Funktion schreiben, die Try-Exceptions einschließt, es sei denn, Sie müssten damit umgehen.
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
Leistungsmessungen - Setup und Timings
Wir können diese Lösungen mit perfplot
zeitlich steuern:
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()
)
Die Leistung ist relativ; Die Darstellung ist entlang der Y-Achse logarithmisch.
Funktionen
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'])])
Die Antwort von @DanielVelkov ist die richtige, ABER Die Verwendung von String-Literalen ist zehnmal schneller
# 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)