Combineer twee tekstkolommen in panda’s dataframe

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

voer hier de afbeeldingsbeschrijving in

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:

voer hier de afbeeldingsbeschrijving in

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

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes