Ik heb een dataframe van 20 x 4000 in Python met panda’s. Twee van deze kolommen heten Year
en quarter
. Ik wil een variabele maken met de naam period
die Year = 2000
en quarter= q2
verandert in 2000q2
.
Kan iemand daarbij helpen?
Antwoord 1, autoriteit 100%
als beide kolommen strings zijn, kunt u ze direct aaneenschakelen:
df["period"] = df["Year"] + df["quarter"]
Als een (of beide) kolommen geen tekenreeks zijn, moet u deze (ze) eerst converteren,
df["period"] = df["Year"].astype(str) + df["quarter"]
Pas op voor NaN’s wanneer u dit doet!
Als u meerdere tekenreekskolommen wilt samenvoegen, kunt u agg
gebruiken:
df['period'] = df[['Year', 'quarter', ...]].agg('-'.join, axis=1)
Waarbij “-” het scheidingsteken is.
Antwoord 2, autoriteit 42%
Kleine datasets (< 150 rijen)
[''.join(i) for i in zip(df["Year"].map(str),df["quarter"])]
of iets langzamer maar compacter:
df.Year.str.cat(df.quarter)
Grotere datasets (> 150 rijen)
df['Year'].astype(str) + df['quarter']
UPDATE: Timinggrafiek Panda’s 0.23.4
Laten we het testen op 200K rijen DF:
In [250]: df
Out[250]:
Year quarter
0 2014 q1
1 2015 q2
In [251]: df = pd.concat([df] * 10**5)
In [252]: df.shape
Out[252]: (200000, 2)
UPDATE: nieuwe timings met Panda’s 0.19.0
Timing zonder CPU/GPU-optimalisatie (gesorteerd van snelst naar langzaamst):
In [107]: %timeit df['Year'].astype(str) + df['quarter']
10 loops, best of 3: 131 ms per loop
In [106]: %timeit df['Year'].map(str) + df['quarter']
10 loops, best of 3: 161 ms per loop
In [108]: %timeit df.Year.str.cat(df.quarter)
10 loops, best of 3: 189 ms per loop
In [109]: %timeit df.loc[:, ['Year','quarter']].astype(str).sum(axis=1)
1 loop, best of 3: 567 ms per loop
In [110]: %timeit df[['Year','quarter']].astype(str).sum(axis=1)
1 loop, best of 3: 584 ms per loop
In [111]: %timeit df[['Year','quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1)
1 loop, best of 3: 24.7 s per loop
Timing met CPU/GPU-optimalisatie:
In [113]: %timeit df['Year'].astype(str) + df['quarter']
10 loops, best of 3: 53.3 ms per loop
In [114]: %timeit df['Year'].map(str) + df['quarter']
10 loops, best of 3: 65.5 ms per loop
In [115]: %timeit df.Year.str.cat(df.quarter)
10 loops, best of 3: 79.9 ms per loop
In [116]: %timeit df.loc[:, ['Year','quarter']].astype(str).sum(axis=1)
1 loop, best of 3: 230 ms per loop
In [117]: %timeit df[['Year','quarter']].astype(str).sum(axis=1)
1 loop, best of 3: 230 ms per loop
In [118]: %timeit df[['Year','quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1)
1 loop, best of 3: 9.38 s per loop
Beantwoord de bijdrage van @anton-vbr
Antwoord 3, autoriteit 38%
df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})
df['period'] = df[['Year', 'quarter']].apply(lambda x: ''.join(x), axis=1)
Geeft dit dataframe op
Year quarter period
0 2014 q1 2014q1
1 2015 q2 2015q2
Deze methode generaliseert naar een willekeurig aantal stringkolommen door df[['Year', 'quarter']]
te vervangen door een kolomsegment van uw dataframe, b.v. df.iloc[:,0:2].apply(lambda x: ''.join(x), axis=1)
.
Je kunt meer informatie vinden over de apply()-methode hier
Antwoord 4, autoriteit 22%
De methode cat()
van de .str
-accessor werkt hier heel goed voor:
>>> import pandas as pd
>>> df = pd.DataFrame([["2014", "q1"],
... ["2015", "q3"]],
... columns=('Year', 'Quarter'))
>>> print(df)
Year Quarter
0 2014 q1
1 2015 q3
>>> df['Period'] = df.Year.str.cat(df.Quarter)
>>> print(df)
Year Quarter Period
0 2014 q1 2014q1
1 2015 q3 2015q3
Met
cat()
kunt u zelfs een scheidingsteken toevoegen, dus stel dat u bijvoorbeeld alleen gehele getallen heeft voor jaar en periode, dan kunt u dit doen:
>>> import pandas as pd
>>> df = pd.DataFrame([[2014, 1],
... [2015, 3]],
... columns=('Year', 'Quarter'))
>>> print(df)
Year Quarter
0 2014 1
1 2015 3
>>> df['Period'] = df.Year.astype(str).str.cat(df.Quarter.astype(str), sep='q')
>>> print(df)
Year Quarter Period
0 2014 1 2014q1
1 2015 3 2015q3
Het samenvoegen van meerdere kolommen is slechts een kwestie van een lijst met series of een dataframe dat alles behalve de eerste kolom bevat als parameter doorgeven aan str.cat()
die wordt aangeroepen in de eerste kolom (Series) :
>>> df = pd.DataFrame(
... [['USA', 'Nevada', 'Las Vegas'],
... ['Brazil', 'Pernambuco', 'Recife']],
... columns=['Country', 'State', 'City'],
... )
>>> df['AllTogether'] = df['Country'].str.cat(df[['State', 'City']], sep=' - ')
>>> print(df)
Country State City AllTogether
0 USA Nevada Las Vegas USA - Nevada - Las Vegas
1 Brazil Pernambuco Recife Brazil - Pernambuco - Recife
Houd er rekening mee dat als uw panda’s-dataframe/-serie null-waarden heeft, u de parameter na_rep moet opnemen om de NaN-waarden te vervangen door een tekenreeks, anders wordt de gecombineerde kolom standaard ingesteld op NaN.
Antwoord 5, autoriteit 4%
Gebruik deze keer een lamba-functie met string.format().
import pandas as pd
df = pd.DataFrame({'Year': ['2014', '2015'], 'Quarter': ['q1', 'q2']})
print df
df['YearQuarter'] = df[['Year','Quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1)
print df
Quarter Year
0 q1 2014
1 q2 2015
Quarter Year YearQuarter
0 q1 2014 2014q1
1 q2 2015 2015q2
Hierdoor kunt u werken met niet-strings en waarden indien nodig opnieuw formatteren.
import pandas as pd
df = pd.DataFrame({'Year': ['2014', '2015'], 'Quarter': [1, 2]})
print df.dtypes
print df
df['YearQuarter'] = df[['Year','Quarter']].apply(lambda x : '{}q{}'.format(x[0],x[1]), axis=1)
print df
Quarter int64
Year object
dtype: object
Quarter Year
0 1 2014
1 2 2015
Quarter Year YearQuarter
0 1 2014 2014q1
1 2 2015 2015q2
Antwoord 6, autoriteit 2%
generaliseren naar meerdere kolommen, waarom niet:
columns = ['whatever', 'columns', 'you', 'choose']
df['period'] = df[columns].astype(str).sum(axis=1)
Antwoord 7, autoriteit 2%
Hoewel het antwoord van @silvado goed is als je df.map(str)
verandert in df.astype(str)
, zal het sneller zijn:
import pandas as pd
df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})
In [131]: %timeit df["Year"].map(str)
10000 loops, best of 3: 132 us per loop
In [132]: %timeit df["Year"].astype(str)
10000 loops, best of 3: 82.2 us per loop
Antwoord 8, autoriteit 2%
Stel dat uw dataframe
df
is met de kolommen Year
en quarter
.
import pandas as pd
df = pd.DataFrame({'Quarter':'q1 q2 q3 q4'.split(), 'Year':'2000'})
Stel dat we het dataframe willen zien;
df
>>> Quarter Year
0 q1 2000
1 q2 2000
2 q3 2000
3 q4 2000
Voeg tot slot het Year
en het quarter
als volgt samen.
df['Period'] = df['Year'] + ' ' + df['Quarter']
U kunt nu print
df
om het resulterende dataframe te zien.
df
>>> Quarter Year Period
0 q1 2000 2000 q1
1 q2 2000 2000 q2
2 q3 2000 2000 q3
3 q4 2000 2000 q4
Als u de spatie tussen het jaar en het kwartaal niet wilt, verwijdert u deze gewoon door te doen;
df['Period'] = df['Year'] + df['Quarter']
Antwoord 9
Hier is een implementatie die ik erg veelzijdig vind:
In [1]: import pandas as pd
In [2]: df = pd.DataFrame([[0, 'the', 'quick', 'brown'],
...: [1, 'fox', 'jumps', 'over'],
...: [2, 'the', 'lazy', 'dog']],
...: columns=['c0', 'c1', 'c2', 'c3'])
In [3]: def str_join(df, sep, *cols):
...: from functools import reduce
...: return reduce(lambda x, y: x.astype(str).str.cat(y.astype(str), sep=sep),
...: [df[col] for col in cols])
...:
In [4]: df['cat'] = str_join(df, '-', 'c0', 'c1', 'c2', 'c3')
In [5]: df
Out[5]:
c0 c1 c2 c3 cat
0 0 the quick brown 0-the-quick-brown
1 1 fox jumps over 1-fox-jumps-over
2 2 the lazy dog 2-the-lazy-dog
Antwoord 10
efficiënter is
def concat_df_str1(df):
""" run time: 1.3416s """
return pd.Series([''.join(row.astype(str)) for row in df.values], index=df.index)
en hier is een tijdtest:
import numpy as np
import pandas as pd
from time import time
def concat_df_str1(df):
""" run time: 1.3416s """
return pd.Series([''.join(row.astype(str)) for row in df.values], index=df.index)
def concat_df_str2(df):
""" run time: 5.2758s """
return df.astype(str).sum(axis=1)
def concat_df_str3(df):
""" run time: 5.0076s """
df = df.astype(str)
return df[0] + df[1] + df[2] + df[3] + df[4] + \
df[5] + df[6] + df[7] + df[8] + df[9]
def concat_df_str4(df):
""" run time: 7.8624s """
return df.astype(str).apply(lambda x: ''.join(x), axis=1)
def main():
df = pd.DataFrame(np.zeros(1000000).reshape(100000, 10))
df = df.astype(int)
time1 = time()
df_en = concat_df_str4(df)
print('run time: %.4fs' % (time() - time1))
print(df_en.head(10))
if __name__ == '__main__':
main()
final, wanneer sum
(concat_df_str2) wordt gebruikt, is het resultaat niet alleen concat, het wordt omgezet in een geheel getal.
Antwoord 11
U kunt lambda gebruiken:
combine_lambda = lambda x: '{}{}'.format(x.Year, x.quarter)
En gebruik het dan bij het maken van de nieuwe kolom:
df['period'] = df.apply(combine_lambda, axis = 1)
Antwoord 12
Het gebruik van zip
kan nog sneller:
df["period"] = [''.join(i) for i in zip(df["Year"].map(str),df["quarter"])]
Grafiek:
import pandas as pd
import numpy as np
import timeit
import matplotlib.pyplot as plt
from collections import defaultdict
df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})
myfuncs = {
"df['Year'].astype(str) + df['quarter']":
lambda: df['Year'].astype(str) + df['quarter'],
"df['Year'].map(str) + df['quarter']":
lambda: df['Year'].map(str) + df['quarter'],
"df.Year.str.cat(df.quarter)":
lambda: df.Year.str.cat(df.quarter),
"df.loc[:, ['Year','quarter']].astype(str).sum(axis=1)":
lambda: df.loc[:, ['Year','quarter']].astype(str).sum(axis=1),
"df[['Year','quarter']].astype(str).sum(axis=1)":
lambda: df[['Year','quarter']].astype(str).sum(axis=1),
"df[['Year','quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1)":
lambda: df[['Year','quarter']].apply(lambda x : '{}{}'.format(x[0],x[1]), axis=1),
"[''.join(i) for i in zip(dataframe['Year'].map(str),dataframe['quarter'])]":
lambda: [''.join(i) for i in zip(df["Year"].map(str),df["quarter"])]
}
d = defaultdict(dict)
step = 10
cont = True
while cont:
lendf = len(df); print(lendf)
for k,v in myfuncs.items():
iters = 1
t = 0
while t < 0.2:
ts = timeit.repeat(v, number=iters, repeat=3)
t = min(ts)
iters *= 10
d[k][lendf] = t/iters
if t > 2: cont = False
df = pd.concat([df]*step)
pd.DataFrame(d).plot().legend(loc='upper center', bbox_to_anchor=(0.5, -0.15))
plt.yscale('log'); plt.xscale('log'); plt.ylabel('seconds'); plt.xlabel('df rows')
plt.show()
Antwoord 13
Deze oplossing gebruikt een tussenstap het comprimeren van twee kolommen van het DataFrame tot een enkele kolom met een lijst van de waarden.
Dit werkt niet alleen voor strings maar voor alle soorten column-dtypes
import pandas as pd
df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})
df['list']=df[['Year','quarter']].values.tolist()
df['period']=df['list'].apply(''.join)
print(df)
Resultaat:
Year quarter list period
0 2014 q1 [2014, q1] 2014q1
1 2015 q2 [2015, q2] 2015q2
Antwoord 14
Hier is mijn samenvatting van de bovenstaande oplossingen om twee kolommen met int- en str-waarde samen te voegen / te combineren in een nieuwe kolom, met behulp van een scheidingsteken tussen de waarden van kolommen. Hiervoor werken drie oplossingen.
# be cautious about the separator, some symbols may cause "SyntaxError: EOL while scanning string literal".
# e.g. ";;" as separator would raise the SyntaxError
separator = "&&"
# pd.Series.str.cat() method does not work to concatenate / combine two columns with int value and str value. This would raise "AttributeError: Can only use .cat accessor with a 'category' dtype"
df["period"] = df["Year"].map(str) + separator + df["quarter"]
df["period"] = df[['Year','quarter']].apply(lambda x : '{} && {}'.format(x[0],x[1]), axis=1)
df["period"] = df.apply(lambda x: f'{x["Year"]} && {x["quarter"]}', axis=1)
Antwoord 15
mijn mening….
listofcols = ['col1','col2','col3']
df['combined_cols'] = ''
for column in listofcols:
df['combined_cols'] = df['combined_cols'] + ' ' + df[column]
'''
Antwoord 16
Zoals velen eerder hebben vermeld, moet u elke kolom naar tekenreeks converteren en vervolgens de plus-operator gebruiken om twee tekenreekskolommen te combineren. U kunt een grote prestatieverbetering krijgen door NumPy te gebruiken.
%timeit df['Year'].values.astype(str) + df.quarter
71.1 ms ± 3.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit df['Year'].astype(str) + df['quarter']
565 ms ± 22.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Antwoord 17
Gebruik .combine_first
.
df['Period'] = df['Year'].combine_first(df['Quarter'])
Antwoord 18
def madd(x):
"""Performs element-wise string concatenation with multiple input arrays.
Args:
x: iterable of np.array.
Returns: np.array.
"""
for i, arr in enumerate(x):
if type(arr.item(0)) is not str:
x[i] = x[i].astype(str)
return reduce(np.core.defchararray.add, x)
Bijvoorbeeld:
data = list(zip([2000]*4, ['q1', 'q2', 'q3', 'q4']))
df = pd.DataFrame(data=data, columns=['Year', 'quarter'])
df['period'] = madd([df[col].values for col in ['Year', 'quarter']])
df
Year quarter period
0 2000 q1 2000q1
1 2000 q2 2000q2
2 2000 q3 2000q3
3 2000 q4 2000q4
Antwoord 19
Men kan de methode toewijzen van DataFrame gebruiken:
df= (pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']}).
assign(period=lambda x: x.Year+x.quarter ))