在pandas / python中的数据框中合并两列文本


485

我在Python中使用熊猫有20 x 4000数据框。其中两列分别命名为Yearquarter。我想创建一个名为periodmake Year = 2000quarter= q2into 的变量2000q2

有人可以帮忙吗?

Answers:


528

如果两个列都是字符串,则可以直接将它们连接起来:

df["period"] = df["Year"] + df["quarter"]

如果其中一列(或两列)均未输入字符串,则应首先将其转换为字符串,

df["period"] = df["Year"].astype(str) + df["quarter"]

这样做时要小心NaN!


如果需要连接多个字符串列,则可以使用agg

df['period'] = df[['Year', 'quarter', ...]].agg('-'.join, axis=1)

其中“-”是分隔符。


13
是否可以在不键入所有列的情况下将多个列加在一起?比方说add(dataframe.iloc[:, 0:10])
海森堡,2015年

5
@Heisenberg使用内置的Python应该可以实现sum
silvado

6
@silvado您能举个添加多个列的例子吗?谢谢
c1c1c1 16-10-25

6
注意,首先需要将map(str)应用于不是字符串的所有列。如果四分之一是一个数字,您将要做的 dataframe["period"] = dataframe["Year"].map(str) + dataframe["quarter"].map(str)映射只是将字符串转换应用于所有条目。
Ozgur Ozturk

13
此解决方案可能会产生一些问题,例如,您有nan值,请小心

269
df = pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']})
df['period'] = df[['Year', 'quarter']].apply(lambda x: ''.join(x), axis=1)

产生此数据框

   Year quarter  period
0  2014      q1  2014q1
1  2015      q2  2015q2

此方法通过替换df[['Year', 'quarter']]为数据框的任何列切片(例如)将其推广为任意数量的字符串列df.iloc[:,0:2].apply(lambda x: ''.join(x), axis=1)

您可以在此处查看有关apply()方法的更多信息


20
lambda x: ''.join(x)只是''.join,不?
DSM

6
@OzgurOzturk:问题是lambda x: ''.join(x)构造的lambda部分什么也没做;就像使用lambda x: sum(x)而不是sum
DSM'2

4
使用时确认相同的结果''.join,即:df['period'] = df[['Year', 'quarter']].apply(''.join, axis=1)
马克斯·吉尼斯

1
@Archie joinstr接受可迭代的实例。使用map将它们全部转换为str,然后使用join
John Strood

16
'-'。join(x.map(str))
Manjul

257

小型数据集(<150行)

[''.join(i) for i in zip(df["Year"].map(str),df["quarter"])]

或稍慢但更紧凑:

df.Year.str.cat(df.quarter)

更大的数据集(> 150行)

df['Year'].astype(str) + df['quarter']

更新:时序图熊猫0.23.4

在此处输入图片说明

让我们在200K行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)

更新:使用Pandas 0.19.0的新计时

定时不CPU / GPU优化(从排序最快到最慢):

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

时序采用CPU / GPU优化:

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

回答@ anton-vbr的贡献


261和264在时间上有何不同?
安东·普罗托波夫

@AntonProtopopov似乎无处可去100ms :)
Dennis Golomazov '16

@AntonProtopopov,我想这是两种时间的混合-一种使用CPU / GPU优化,另一种则没有。我已经更新了答案,并将两个时间设置都放在了那里……
MaxU

如果所有列看起来都可能是整数(即整数的字符串形式),则对.sum()的使用将失败。取而代之的是,熊猫在求和之前将它们转换回数字!
CPBL

@CPBL,请尝试以下方法:df.T.apply(lambda x: x.str.cat(sep=''))
MaxU

157

该方法cat()的的.str访问可以很好地表现这一点:

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

cat() 甚至允许您添加分隔符,因此,例如,假设年份和期间只有整数,则可以执行以下操作:

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

连接多列只是传递一系列列表或包含除第一列之外的所有列的数据框作为要str.cat()在第一列(系列)上调用的参数的问题:

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

请注意,如果您的pandas数据框/系列具有空值,则需要包括参数na_rep以用字符串替换NaN值,否则合并的列将默认为NaN。


12
这似乎比lambda或更好(也可能更有效率)map。读起来也很干净。
dwanderson '16

1
@ZakS,将其余的列作为数据框而不是系列作为第一个参数传递给str.cat()。我将修改答案
LeoRochael

您正在使用哪个版本的熊猫?我收到ValueError:您是要提供sep关键字吗?在pandas-0.23.4中。谢谢!
刘庆清

@QinqingLiu,我用pandas-0.23.4重新测试了它们,它们似乎有效。sep仅当您打算分隔串联字符串的各个部分时,才需要该参数。如果遇到错误,请向我们显示您的失败示例。
LeoRochael

31

这次通过string.format()使用lamba函数。

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

这使您可以根据需要使用非字符串并重新格式化值。

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

1
快得多:.apply(''。join(x),axis = 1)
加纳姆

19

您问题的简单答案。

    year    quarter
0   2000    q1
1   2000    q2

> df['year_quarter'] = df['year'] + '' + df['quarter']

> print(df['year_quarter'])
  2000q1
  2000q2

3
如果Year不是字符串,将失败
geher19,

4
使用df['Year'].astype(str) + '' + df['quarter'].astype(str)
Yedhrab

2
该解决方案的确切含义是什么,因为它与最佳答案相同?
AMC

14

虽然@silvado的答案很好,但如果更改df.map(str)df.astype(str)它会更快:

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

12

让我们假设您 dataframedfYear和为Quarter

import pandas as pd
df = pd.DataFrame({'Quarter':'q1 q2 q3 q4'.split(), 'Year':'2000'})

假设我们要看数据框;

df
>>>  Quarter    Year
   0    q1      2000
   1    q2      2000
   2    q3      2000
   3    q4      2000

最后,将Year和连接Quarter如下。

df['Period'] = df['Year'] + ' ' + df['Quarter']

现在print df ,您可以查看生成的数据框。

df
>>>  Quarter    Year    Period
    0   q1      2000    2000 q1
    1   q2      2000    2000 q2
    2   q3      2000    2000 q3
    3   q4      2000    2000 q4

如果您不想在年份和季度之间留出空间,只需将其删除即可;

df['Period'] = df['Year'] + df['Quarter']

3
指定为字符串df['Period'] = df['Year'].map(str) + df['Quarter'].map(str)
Stuber '18

TypeError: Series cannot perform the operation +跑步df2['filename'] = df2['job_number'] + '.' + df2['task_number']或跑步时都会得到df2['filename'] = df2['job_number'].map(str) + '.' + df2['task_number'].map(str)
卡尔·贝克

但是,df2['filename'] = df2['job_number'].astype(str) + '.' + df2['task_number'].astype(str)确实可行。
Karl Baker

@KarlBaker,我认为您的输入中没有字符串。但我很高兴您知道了这一点。如果您看一下dataframe我在上面创建的示例,您将看到所有列都是string
塞缪尔·恩德

该解决方案的确切含义是什么,因为它与最佳答案相同?
AMC

10

这是我发现非常通用的实现:

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

FYI:此方法的伟大工程与Python 3,但给我的麻烦在Python 2
亚历克斯·米勒体育

10

将数据插入数据框时,此命令应该可以解决您的问题:

df['period'] = df[['Year', 'quarter']].apply(lambda x: ' '.join(x.astype(str)), axis=1)

此答案与较旧的,更受欢迎的答案相同。
AMC

9

更有效的是

def concat_df_str1(df):
    """ run time: 1.3416s """
    return pd.Series([''.join(row.astype(str)) for row in df.values], index=df.index)

这是一个时间测试:

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

最后,当使用sum(concat_df_str2)时,结果不是简单的concat,它将转换为整数。


+1 Neat解决方案,这还使我们可以指定列:例如df.values[:, 0:3]df.values[:, [0,2]]


6

使用zip甚至可以更快:

df["period"] = [''.join(i) for i in zip(df["Year"].map(str),df["quarter"])]

图形:

在此处输入图片说明

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

6

最简单的解决方案:

通用解决方案

df['combined_col'] = df[['col1', 'col2']].astype(str).apply('-'.join, axis=1)

特定问题的解决方案

df['quarter_year'] = df[['quarter', 'year']].astype(str).apply(''.join, axis=1)

.join之前的引号内指定首选的分隔符



5

此解决方案使用中间步骤将DataFrame的两列压缩为包含列表单列。这不仅适用于字符串,而且适用于所有类型的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)

结果:

   Year quarter        list  period
0  2014      q1  [2014, q1]  2014q1
1  2015      q2  [2015, q2]  2015q2

看起来其他dtypes无法正常工作。我收到了TypeError:序列项1:预期的str实例,找到了float
Prometheus

首先对字符串进行转换。
联接

此解决方案无法将具有不同dtype的两个列组合在一起,请参见我的答案以获取此类情况的正确解决方案。
善意

2

如前所述,您必须将每一列转换为字符串,然后使用加号运算符组合两个字符串列。使用NumPy可以大大提高性能。

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

我想使用数字化版本,但出现错误:输入df2['filename'] = df2['job_number'].values.astype(str) + '.' + df2['task_number'].values.astype(str)-> 输出TypeError: ufunc 'add' did not contain a loop with signature matching types dtype('<U21') dtype('<U21') dtype('<U21')。job_number和task_number均为整数。
Karl Baker

那是因为您要组合两个numpy数组。如果将numpy数组与pandas Series结合使用,它将起作用。asdf['Year'].values.astype(str) + df.quarter
AbdulRehmanLiaqat

2

我认为在pandas中组合列的最好方法是将两个列都转换为整数,然后转换为str。

df[['Year', 'quarter']] = df[['Year', 'quarter']].astype(int).astype(str)
df['Period']= df['Year'] + 'q' + df['quarter']

将两列都转换为整数为什么先转换为int?一旦消除了这种可能性,该解决方案便与当前的最佳答案相同。
AMC

2

这是我上面的解决方案的摘要,该方法使用列值之间的分隔符将具有int和str值的两列连接/合并为新列。为此有三种解决方案。

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

谢谢!您的F字串解决方案正是我希望找到的!!!
leerssej

1

使用.combine_first

df['Period'] = df['Year'].combine_first(df['Quarter'])

这是不正确的。.combine_first将导致该值'Year'存储在中'Period',或者如果为Null,则来自的值'Quarter'。它将不会连接两个字符串并将其存储在中'Period'
史蒂夫·G

这是完全错误的。
AMC

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

例如:

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

0

一个可以使用DataFrame的分配方法:

df= (pd.DataFrame({'Year': ['2014', '2015'], 'quarter': ['q1', 'q2']}).
  assign(period=lambda x: x.Year+x.quarter ))

-1
dataframe["period"] = dataframe["Year"].astype(str).add(dataframe["quarter"])

或者如果值类似于[2000] [4]并想要设为[2000q4]

dataframe["period"] = dataframe["Year"].astype(str).add('q').add(dataframe["quarter"]).astype(str)

.astype(str).map(str)作品代替。


这基本上与最佳答案相同。
AMC
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.