【问题标题】:Merged two dataframe columns with lists in order of lists按列表顺序将两个数据框列与列表合并
【发布时间】:2017-05-25 13:31:02
【问题描述】:

我正在尝试合并/连接两列,其中两列都有相关但由“|”分隔的单独文本数据除了用“”替换某些名称并替换|用'\n'。

例如,原始数据可能是:

    First Names            Last Names
0   Jim|James|Tim          Simth|Jacobs|Turner
1   Mickey|Mini            Mouse|Mouse
2   Mike|Billy|Natasha     Mills|McGill|Tsaka

如果我想合并/连接以派生全名并删除与“Smith”相关的条目,则最终 df 应如下所示:

    First Names            Last Names            Full Names
0   Jim|James|Tim          Simth|Jacobs|Turner   James Jacobs\nTim Turner
1   Mickey|Mini            Mouse|Mouse           Mickey Mouse\nMini Mouse
2   Mike|Billy|Natasha     Mills|McGill|Tsaka    Mike Mills\nBilly McGill\nNatasha Tsaka

到目前为止,我目前的做法是:

def parse_merge(df, col1, col2, splitter, new_col, list_to_exclude):

    orig_order = pd.Series(list(df.index)).rename('index')

    col1_df = pd.concat([orig_order, df[col1], df[col1].str.split(splitter, expand=True)], axis = 1)
    col2_df = pd.concat([orig_order, df[col2], df[col2].str.split(splitter, expand=True)], axis = 1)

    col1_melt = pd.melt(col1_df, id_vars=['index', col1], var_name='count')
    col2_melt = pd.melt(col2_df, id_vars=['index', col2], var_name='count')

    col2_melt['value'] = '(' + col2_melt['value'].astype(str) + ')'
    col2_melt = col2_melt.rename(columns={'value':'value2'})

    melted_merge = pd.concat([col1_melt, col2_melt['value2']], axis = 1 )

    if len(list_to_exclude) > 0:
         list_map = map(re.escape, list_to_exclude)

    melted_merge.ix[melted_merge['value2'].str.contains('|'.join(list_map)), ['value', 'value2']] = ''

    melted_merge[new_col] = melted_merge['value'] + " " + melted_merge['value2']

如果我打电话:

parse_merge(names, 'First Names', 'Last Names', 'Full Names', ['Smith'])

数据变成:

    Index   First Names        count    value            value2        Full Names
0   0       Jim|James|Tim      0        Jim              Smith         ''
1   1       Mickey|Mini        0        Mickey           Mouse         Mickey Mouse
2   2       Mike|Billy|Natasha 0        Mike             Mills         Mike Mills

只是不确定如何在没有任何循环的情况下完成此操作,或者是否有更有效/完全不同的方法。

感谢大家的意见!

【问题讨论】:

    标签: python performance pandas processing-efficiency


    【解决方案1】:

    这是使用pd.DataFrame.apply 和python 的一些不错的内置功能的精简解决方案:

    def combine_names(row):
    
        pairs = list(zip(row[0].split('|'), row[1].split('|')))
        return '\n'.join([' '.join(p) for p in pairs if p[1] != 'Simth'])
    
    df['Full Name'] = df.apply(combine_names, axis=1)
    

    【讨论】:

    • 不错的解决方案@AlexG
    • 欣赏解决方案!我如何将其概括为适用于具有 n 列的数据框,我只想添加一个由两列合并的列组成的列?另外,我对 .apply 不太熟悉 - 我可以将参数传递给 combine_names 吗?最好传递一个要跳过的名称列表,而不是像示例中那样仅传递 'Smith'。
    • 您可以将名称列表直接编码到combine_names 函数中,但它只能接受一个参数。传递的参数是行(假设轴设置为 1)。你也可以这样称呼它:df[['First Names', 'Last Names']].apply(combine_names, axis=1),如果你有不止这两列的话。回到您的第一点,您可以将:if p[1] != 'Simth' 更改为:if p[1] not in ['Simth', 'John', 'King']
    • 我已经通过将 combine_names 函数嵌入另一个函数来解决这个问题,该函数为 combine_names 创建一个单独的数据框,然后连接到原始 df。非常感谢,非常聪明!
    【解决方案2】:

    我真的很喜欢@AlexG's solution - 请使用它。

    这是我尝试创建一个创造性的单线解决方案 - 这绝对是反常的,所以不应该使用它 - 这只是为了好玩:

    In [78]: df
    Out[78]:
              First Names           Last Names
    0       Jim|James|Tim  Simth|Jacobs|Turner
    1         Mickey|Mini          Mouse|Mouse
    2  Mike|Billy|Natasha   Mills|McGill|Tsaka
    
    In [79]: df['Full Names'] = \
        ...: (df.stack()
        ...:    .str.split(r'\|', expand=True)
        ...:    .unstack(level=1)
        ...:    .groupby(level=0, axis=1)
        ...:    .apply(lambda x: x.add(' ').sum(axis=1).str.strip())
        ...:    .replace([r'\w+\s+Simth'], [np.nan], regex=True)
        ...:    .apply(lambda x: x.dropna().str.cat(sep='\n'), axis=1)
        ...: )
        ...:
    
    In [80]: df
    Out[80]:
              First Names           Last Names                               Full Names
    0       Jim|James|Tim  Simth|Jacobs|Turner                 James Jacobs\nTim Turner
    1         Mickey|Mini          Mouse|Mouse                 Mickey Mouse\nMini Mouse
    2  Mike|Billy|Natasha   Mills|McGill|Tsaka  Mike Mills\nBilly McGill\nNatasha Tsaka
    

    【讨论】:

      【解决方案3】:

      我有很多的理解

      l = df.values.tolist()
      
      ['|'.join(n)
       for n in [[' '.join(z)
       for z in zip(*[s.split('|')
       for s in r]) if z[1] != 'Smith']
       for r in l]]
      
      ['James Jacobs|Tim Turner',
       'Mickey Mouse|Mini Mouse',
       'Mike Mills|Billy McGill|Natasha Tsaka']
      

      l = df.values.tolist()
      
      df['Full Names'] = [
           '|'.join(n)
           for n in [[' '.join(z)
           for z in zip(*[s.split('|')
           for s in r]) if z[1] != 'Smith']
           for r in l]]
      
      df
      


      除了文字游戏之外,这对示例数据来说非常有趣


      更长的解释

      l
      
      [['Jim|James|Tim', 'Simth|Jacobs|Turner'],
       ['Mickey|Mini', 'Mouse|Mouse'],
       ['Mike|Billy|Natasha', 'Mills|McGill|Tsaka']]
      
      • l 是一个列表列表。我将广泛使用列表推导和迭代。
      • 每个子列表包含 2 个字符串,我将它们拆分并压缩在一起。
      • 拆分的结果将是一个由(first, last) 名称组成的元组“列表”。我将使用if z[1] != 'Smith' 过滤掉史密斯。
        • 顺便说一句,在这一行你可以使用z[1] not in list_of_names
      • 然后我将使用' '.join(这实际上是一个函数)将每个元组组合到first last
      • 然后我将使用另一个'|'.joinfirst last 的子列表合并到first1 last1|first2 last2... 以此类推

      之所以这样更快,是因为理解已经在很大程度上得到了优化。其他解决方案是使用apply,这是一种通用循环结构,只能在特殊情况下利用快速循环(知道更多的人,如果我错了,请纠正我)。使用lambda 绝对不是这些情况之一。

      【讨论】:

      • 你能解释一下它是如何如此高效的吗?它到底在做什么?我或多或少可以理解第一个答案(AlexG 的“combin_names”方法 - 但这超出了我的范围。抱歉我的知识有限。
      • @wingsoficarus116 更新了类似解释的内容
      猜你喜欢
      • 2018-05-20
      • 1970-01-01
      • 2019-09-19
      • 2021-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-27
      相关资源
      最近更新 更多