【问题标题】:How to concatenate multiple column values into a single column in Pandas dataframe如何将多个列值连接到 Pandas 数据框中的单个列中
【发布时间】:2017-01-10 12:24:13
【问题描述】:

这个问题与之前的this posted 相同。我想连接三列而不是连接两列:

这里是两列的组合:

df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3], 'new':['apple', 'banana', 'pear']})

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

df
    bar foo new combined
0   1   a   apple   a_1
1   2   b   banana  b_2
2   3   c   pear    c_3

我想用这个命令合并三列,但它不起作用,你知道吗?

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

【问题讨论】:

标签: python pandas dataframe


【解决方案1】:

使用DataFrame.apply() 的另一种解决方案,当您想加入更多列时,键入的内容略少且可扩展性更高:

cols = ['foo', 'bar', 'new']
df['combined'] = df[cols].apply(lambda row: '_'.join(row.values.astype(str)), axis=1)

【讨论】:

  • 这是列列表保存为变量并且每次可以容纳不同数量的列时的最佳解决方案
  • 你真是天赐之物,这一个班轮解决方案是如此简洁和优雅
  • 我遇到的小问题是 .values.astype(str)None 转换为字符串 'None' 而不是空字符串。显然。
  • 没有 lambda(更快更简洁):df[cols].astype(str).apply('_'.join, axis=1)。也就是说,使用.str.cat(...).str.cat(...)... 仍然更快。
【解决方案2】:

你可以这样做:

In[17]:df['combined']=df['bar'].astype(str)+'_'+df['foo']+'_'+df['new']

In[17]:df
Out[18]: 
   bar foo     new    combined
0    1   a   apple   1_a_apple
1    2   b  banana  2_b_banana
2    3   c    pear    3_c_pear

【讨论】:

  • 这个解决方案将比.apply(, axis=1)在更大的DFs上快得多
  • @MaxU 是的,而且非常简单。
  • 我已经针对 30K 行 DF 添加了 comparison...
  • 我在使用此解决方案时收到SettingWithCopyWarning - 如何避免触发该警告?
  • 然而,当您需要加入许多列时,这会很烦人。
【解决方案3】:

如果您想要合并更多列,使用 Series 方法 str.cat 可能会很方便:

df["combined"] = df["foo"].str.cat(df[["bar", "new"]].astype(str), sep="_")

基本上,您选择第一列(如果它还不是str 类型,则需要附加.astype(str)),然后将其他列附加到第一列(由可选的分隔符分隔)。

【讨论】:

  • 聪明,但这对我造成了巨大的内存错误。尽管可能很乏味,但写 df[col].map(str) + '_' df[col2].map(str) + ... + df[col9].map(str) 效率更高。
  • 这很有趣!我不知道我们可以使用 DataFrame 作为Series.str.cat()中的参数
  • 这对我来说是迄今为止最简单的,我喜欢 sep 参数
【解决方案4】:

只是想对两种解决方案进行时间比较(对于 30K 行 DF):

In [1]: df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3], 'new':['apple', 'banana', 'pear']})

In [2]: big = pd.concat([df] * 10**4, ignore_index=True)

In [3]: big.shape
Out[3]: (30000, 3)

In [4]: %timeit big.apply(lambda x:'%s_%s_%s' % (x['bar'],x['foo'],x['new']),axis=1)
1 loop, best of 3: 881 ms per loop

In [5]: %timeit big['bar'].astype(str)+'_'+big['foo']+'_'+big['new']
10 loops, best of 3: 44.2 ms per loop

更多选择:

In [6]: %timeit big.ix[:, :-1].astype(str).add('_').sum(axis=1).str.cat(big.new)
10 loops, best of 3: 72.2 ms per loop

In [11]: %timeit big.astype(str).add('_').sum(axis=1).str[:-1]
10 loops, best of 3: 82.3 ms per loop

【讨论】:

  • 非常不错的附加选项。
【解决方案5】:

@allen 给出的答案相当通用,但对于较大的数据帧可能缺乏性能:

Reduce 确实很多更好:

from functools import reduce

import pandas as pd

# make data
df = pd.DataFrame(index=range(1_000_000))
df['1'] = 'CO'
df['2'] = 'BOB'
df['3'] = '01'
df['4'] = 'BILL'


def reduce_join(df, columns):
    assert len(columns) > 1
    slist = [df[x].astype(str) for x in columns]
    return reduce(lambda x, y: x + '_' + y, slist[1:], slist[0])


def apply_join(df, columns):
    assert len(columns) > 1
    return df[columns].apply(lambda row:'_'.join(row.values.astype(str)), axis=1)

# ensure outputs are equal
df1 = reduce_join(df, list('1234'))
df2 = apply_join(df, list('1234'))
assert df1.equals(df2)

# profile
%timeit df1 = reduce_join(df, list('1234'))  # 733 ms
%timeit df2 = apply_join(df, list('1234'))   # 8.84 s

【讨论】:

  • 有没有办法不放弃空单元格,不加分隔符,比如要加入的字符串是“”、“a”和“b”,预期结果是“_a_b” ,但是否有可能有“a_b”。我找不到有效的方法,因为它需要逐行操作,因为每行的长度不同。
  • 我不确定你的意思@Yang,也许用一个可行的例子发布一个新问题?
【解决方案6】:

可能最快的解决方案是使用纯 Python 进行操作:

Series(
    map(
        '_'.join,
        df.values.tolist()
        # when non-string columns are present:
        # df.values.astype(str).tolist()
    ),
    index=df.index
)

与@MaxU 答案的比较(使用具有数字和字符串列的big 数据框):

%timeit big['bar'].astype(str) + '_' + big['foo'] + '_' + big['new']
# 29.4 ms ± 1.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)


%timeit Series(map('_'.join, big.values.astype(str).tolist()), index=big.index)
# 27.4 ms ± 2.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

与@derchambers 答案的比较(使用他们的df 数据框,其中所有列都是字符串):

from functools import reduce

def reduce_join(df, columns):
    slist = [df[x] for x in columns]
    return reduce(lambda x, y: x + '_' + y, slist[1:], slist[0])

def list_map(df, columns):
    return Series(
        map(
            '_'.join,
            df[columns].values.tolist()
        ),
        index=df.index
    )

%timeit df1 = reduce_join(df, list('1234'))
# 602 ms ± 39 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit df2 = list_map(df, list('1234'))
# 351 ms ± 12.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

【讨论】:

    【解决方案7】:

    我认为你缺少一个 %s

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

    【讨论】:

      【解决方案8】:

      如果您有一个要连接的列列表,并且您可能想使用一些分隔符,那么您可以这样做

      def concat_columns(df, cols_to_concat, new_col_name, sep=" "):
          df[new_col_name] = df[cols_to_concat[0]]
          for col in cols_to_concat[1:]:
              df[new_col_name] = df[new_col_name].astype(str) + sep + df[col].astype(str)
      
      

      这应该比apply 更快,并且可以连接任意数量的列。

      【讨论】:

        【解决方案9】:

        @derchambers 我又找到了一个解决方案:

        import pandas as pd
        
        # make data
        df = pd.DataFrame(index=range(1_000_000))
        df['1'] = 'CO'
        df['2'] = 'BOB'
        df['3'] = '01'
        df['4'] = 'BILL'
        
        def eval_join(df, columns):
        
            sum_elements = [f"df['{col}']" for col in columns]
            to_eval = "+ '_' + ".join(sum_elements)
        
            return eval(to_eval)
        
        
        #profile
        %timeit df3 = eval_join(df, list('1234')) # 504 ms
        

        【讨论】:

          【解决方案10】:
          df = DataFrame({'foo':['a','b','c'], 'bar':[1, 2, 3], 'new':['apple', 'banana', 'pear']})
          
          df['combined'] = df['foo'].astype(str)+'_'+df['bar'].astype(str)
          

          如果您与 string('_') 连接,请将列转换为您想要的字符串,然后您可以连接数据框。

          【讨论】:

            【解决方案11】:
            df['New_column_name'] = df['Column1'].map(str) + 'X' + df['Steps']
            

            X= x 是您想要分隔两个合并列的任何分隔符(例如:空格)。

            【讨论】:

              【解决方案12】:

              您可以创建一个使实现更整洁的函数(尤其是如果您在整个实现中多次使用此功能):

              def concat_cols(df, cols_to_concat, new_col_name, separator):  
                  df[new_col_name] = ''
                  for i, col in enumerate(cols_to_concat):
                      df[new_col_name] += ('' if i == 0 else separator) + df[col].astype(str)
                  return df
              

              示例用法:

              test = pd.DataFrame(data=[[1,2,3], [4,5,6], [7,8,9]], columns=['a', 'b', 'c'])
              test = concat_cols(test, ['a', 'b', 'c'], 'concat_col', '_')
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2021-06-14
                • 1970-01-01
                • 1970-01-01
                • 2021-02-07
                • 2019-01-05
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多