wake-up-neo.com

String-Verkettung von zwei Pandasäulen

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.

50
nat

df['bar'] = df.bar.map(str) + " is " + df.foo.

99
BrenBarn

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).

36
Daniel Velkov

Sie könnten auch verwenden

df['bar'] = df['bar'].str.cat(df['foo'].values.astype(str), sep=' is ')
11
chrimuelle
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
4
Vladimir Yashin

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()
)

enter image description here

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'])])
2
coldspeed

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)
0
erickfis