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 chararrays
en 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.join
gebruiken 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'])]
perfplot
Prestatiemetingen
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.cat
is 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)