Aaneenschakeling van twee panda’s kolommen

Ik heb een volgend DataFrame:

from pandas import *
df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})

Het ziet er zo uit:

   bar foo
0    1   a
1    2   b
2    3   c

Nu wil ik zoiets hebben als:

    bar
0    1 is a
1    2 is b
2    3 is c

Hoe kan ik dit bereiken?
Ik heb het volgende geprobeerd:

df['foo'] = '%s is %s' % (df['bar'], df['foo'])

maar ik krijg een verkeerd resultaat:

>>>print df.ix[0]
bar                                                    a
foo    0    a
1    b
2    c
Name: bar is 0    1
1    2
2
Name: 0

Sorry voor een domme vraag, maar deze panda’s: combineer twee kolommen in een DataFramewas niet nuttig voor mij.


Antwoord 1, autoriteit 100%

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


Antwoord 2, autoriteit 76%

Deze vraag is al beantwoord, maar ik denk dat het goed zou zijn om enkele nuttige methoden die nog niet eerder zijn besproken in de mix te gooien en alle tot nu toe voorgestelde methoden te vergelijken op het gebied van prestaties.

Hier zijn enkele handige oplossingen voor dit probleem, in oplopende volgorde van prestaties.


DataFrame.agg

Dit is een eenvoudige str.format-gebaseerde aanpak.

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

Je kunt hier ook f-string-opmaak gebruiken:

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-gebaseerde aaneenschakeling

Converteer de kolommen om samen te voegen als chararraysen voeg ze vervolgens samen.

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

Lijst begripmet zip

Ik kan niet genoeg benadrukken hoe ondergewaardeerd lijstbegrippen zijn bij panda’s.

df['baz'] = [str(x) + ' is ' + y for x, y in zip(df['bar'], df['foo'])]

Als alternatief, str.joingebruiken om samen te voegen (wordt ook beter geschaald):

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

Lijstbegrippen blinken uit in het manipuleren van strings, omdat stringbewerkingen inherent moeilijk te vectoriseren zijn, en de meeste “gevectoriseerde” functies van panda’s zijn in feite wrappers rond lussen. Ik heb uitgebreid over dit onderwerp geschreven in For-loops met panda’s – Wanneer moet ik zorg?. Als u zich geen zorgen hoeft te maken over indexuitlijning, kunt u in het algemeen een lijstbegrip gebruiken bij het omgaan met tekenreeks- en regex-bewerkingen.

De lijst comp hierboven verwerkt standaard geen NaN’s. U kunt echter altijd een functie schrijven die een try omwikkelt, behalve als u deze moet afhandelen.

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

perfplotPrestatiemetingen

Grafiek gegenereerd met perfplot. Hier is de volledige codelijst.

Functies

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

Antwoord 3, autoriteit 29%

Het probleem in uw code is dat u de bewerking op elke rij wilt toepassen. De manier waarop je het hebt geschreven, neemt echter de hele ‘bar’- en ‘foo’-kolommen, converteert ze naar strings en geeft je één grote string terug. Je kunt het zo schrijven:

df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)

Het is langer dan het andere antwoord, maar is algemener (kan worden gebruikt met waarden die geen strings zijn).


Antwoord 4, autoriteit 8%

Je zou ook kunnen gebruiken

df['bar'] = df['bar'].str.cat(df['foo'].values.astype(str), sep=' is ')

Antwoord 5, autoriteit 7%

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

Antwoord 6, autoriteit 4%

series.str.catis de meest flexibele manier om dit probleem aan te pakken:

Voor df = pd.DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3]})

df.foo.str.cat(df.bar.astype(str), sep=' is ')
>>>  0    a is 1
     1    b is 2
     2    c is 3
     Name: foo, dtype: object

OF

df.bar.astype(str).str.cat(df.foo, sep=' is ')
>>>  0    1 is a
     1    2 is b
     2    3 is c
     Name: bar, dtype: object

In tegenstelling tot .join()(die voor het samenvoegen van een lijst in een enkele serie is), is deze methode voor het samenvoegen van 2 series. Het stelt je ook in staat om NaN-waarden naar wens te negeren of te vervangen.


Antwoord 7, autoriteit 3%

@DanielVelkov antwoord is de juiste MAAR
het gebruik van letterlijke tekenreeksen is sneller:

# Daniel's
%timeit df.apply(lambda x:'%s is %s' % (x['bar'],x['foo']),axis=1)
## 963 µs ± 157 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# String literals - python 3
%timeit df.apply(lambda x: f"{x['bar']} is {x['foo']}", axis=1)
## 849 µs ± 4.28 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Antwoord 8

Ik ben een specifiek geval van mijn kant tegengekomen met 10^11 rijen in mijn dataframe, en in dit geval is geen van de voorgestelde oplossingen geschikt. Ik heb categorieën gebruikt en dit zou in alle gevallen goed moeten werken als het aantal unieke strings niet te groot is. Dit is gemakkelijk te doen in de R-software met XxY met factoren, maar ik kon geen andere manier vinden om het in python te doen (ik ben nieuw in python). Als iemand een plaats weet waar dit wordt geïmplementeerd, hoor ik het graag.

def Create_Interaction_var(df,Varnames):
    '''
    :df data frame
    :list of 2 column names, say "X" and "Y". 
    The two columns should be strings or categories
    convert strings columns to categories
    Add a column with the "interaction of X and Y" : X x Y, with name 
    "Interaction-X_Y"
    '''
    df.loc[:, Varnames[0]] = df.loc[:, Varnames[0]].astype("category")
    df.loc[:, Varnames[1]] = df.loc[:, Varnames[1]].astype("category")
    CatVar = "Interaction-" + "-".join(Varnames)
    Var0Levels = pd.DataFrame(enumerate(df.loc[:,Varnames[0]].cat.categories)).rename(columns={0 : "code0",1 : "name0"})
    Var1Levels = pd.DataFrame(enumerate(df.loc[:,Varnames[1]].cat.categories)).rename(columns={0 : "code1",1 : "name1"})
    NbLevels=len(Var0Levels)
    names = pd.DataFrame(list(itertools.product(dict(enumerate(df.loc[:,Varnames[0]].cat.categories)),
                                                dict(enumerate(df.loc[:,Varnames[1]].cat.categories)))),
                         columns=['code0', 'code1']).merge(Var0Levels,on="code0").merge(Var1Levels,on="code1")
    names=names.assign(Interaction=[str(x) + '_' + y for x, y in zip(names["name0"], names["name1"])])
    names["code01"]=names["code0"] + NbLevels*names["code1"]
    df.loc[:,CatVar]=df.loc[:,Varnames[0]].cat.codes+NbLevels*df.loc[:,Varnames[1]].cat.codes
    df.loc[:, CatVar]=  df[[CatVar]].replace(names.set_index("code01")[["Interaction"]].to_dict()['Interaction'])[CatVar]
    df.loc[:, CatVar] = df.loc[:, CatVar].astype("category")
    return df

Antwoord 9

Ik denk dat de meest beknopte oplossing voor een willekeurig aantal kolommen een verkorte versie is van dit antwoord:

df.astype(str).apply(' is '.join, axis=1)

Je kunt nog twee tekens afschaven met df.agg(), maar het is langzamer:

df.astype(str).agg(' is '.join, axis=1)

Other episodes