【问题标题】:Filling up dataframe columns based on condition in pandas根据熊猫中的条件填充数据框列
【发布时间】:2020-04-19 11:47:58
【问题描述】:

我有两个数据框,如下所示

         df_input                                    df_output
id       POLL_X  POLL_Y  POLL_Z ..     id   Pass_01  Pass_02  Pass_03 .....
110101       1       2       4       110101             
110102       2       1       3       110102

要求是根据df_input中的值填写df_ouput

            df_input                                    df_output
  id   POLL_X  POLL_Y  POLL_Z ....       id   Pass_01  Pass_02  Pass_03 .....
110101     1       2       3            110101     X       Y         Z  
110102     2       1       3            110102     Y       X         Z

所以基本上来自 df_input 的列值将是 df_output 中的单元格值,同时匹配并基于 df_input.id == df_output.id

我正在尝试如下

def function1(df_input, number):
       dfwithCols = df_input[df_input.columns[pd.Series(df_input.columns).str.startswith('POLL_')]]
       list_cols = dfwithCols .columns[(dfwithCols == float(number)).iloc[0]]
       colValue = (dfReduced == float(index)).idxmax(axis=1)[0]
       return colValue

--驱动函数--

for i in range(1,number_of_columnswithPass):
      df_output['Pass_'+i] = function1(df_input,i)

number_of_columnsiwthPass 是一个常数,它给出了名称为 pass 的总列数。

我也不能遍历每一行,因为这将花费大量时间,必须在基于列或基于 lambda 的情况下进行

两个数据框中还有其他列,df_input.id == df_output.id 也必须匹配

总列数可以在 40 左右,一些测试值包括 POLL_DNW , POLL_DO, POLL_DOES , POLL_SIG:2
所以我必须在 '_' 和 01,02,03,04----10,11,--21,---39,40 之类的列号之后取任何内容

【问题讨论】:

  • 假设 df_output 中的初始数据为空值是否安全?
  • 是的,它是。 null 或 nan
  • 但是它的 id 列被填满了

标签: python python-3.x pandas python-2.7 dataframe


【解决方案1】:

我假设一开始你有 df_output 有正确的列名 (因为它们应该在填充后)。

完成你的任务:

  1. import re(稍后会用到)。

  2. 根据源行定义以下生成输出行的函数:

    def genRow(row):
        ind = []
        vals = []
        for k, v in row.iteritems():
            mtch = re.match('POLL_(.+)', k)
            if mtch:
                ind.append('Pass_' + str(v).zfill(2))
                vals.append(mtch.group(1))
            else:
                ind.append(k)
                vals.append(v)
        return pd.Series(vals, index=ind).rename(row.name)
    

    请注意,此函数将任何 POLL_... 列“替换”为 相应的 Pass_... 列,其他列保持原样。

  3. 应用它:

    df_output = df_input.apply(genRow, axis=1).reindex(columns=df_output.columns)
    

步骤:

  • df_input.apply(...) - 生成“初步”输出数据帧。 请注意,目前列顺序是字母顺序
  • reindex(...) - 使用列名重新索引上述 DataFrame 来自 df_output,提供正确的列顺序。
  • df_output = - 用上述结果覆盖 df_output

编辑

如果您的输入 DataFrame 在 POLL_... 列中包含重复值, 需要稍作修改。 这种情况会导致这两个(或更多)元素具有的输出行 相同的索引,所以如果它不能构建整个DataFrame 包括这样一行。

补救方法是将这些元素“压缩”成一个元素 原始索引和所有转换为字符串的值,包含例如 逗号分隔的原始值列表。

为此,将 genRow 函数中的最后一行更改为:

out = pd.Series(vals, index=ind).rename(row.name)
return out.groupby(lambda key: key).apply(lambda lst: (', '.join(sorted(lst))))

【讨论】:

  • 两个数据框中还有其他列,df_input.id == df_output.id 也必须匹配
  • 我更正了我的解决方案以保留源索引。如果还有其他列要省略,请指定哪个(如何区分要处理的列与要省略的列)。
  • 以 PASS 开头的列必须按照这个逻辑进行,其他的可以保持原样,其他列我还是会处理的
  • 在执行解决方案时出现此错误“AttributeError: ("Can only use .str accessor with string values (i.e. inferred_type is 'string', 'unicode' or 'mixed')", '发生在索引 Pass_DNW'
  • 当然,它最多可以有 40 列,它可以是任何单词,而不仅仅是一个字符。像 PASS_DOES , PASS_DNW , PASS_SIG:2 。我也编辑了主要问题
【解决方案2】:

从 df_input 和 output 创建两个数据帧,合并它们并返回以获取您的最终数据帧:

#create first dataframe
    res1 = pd.wide_to_long(df,
                       stubnames='Pass',
                       i='id',
                       j='letters',
                       sep='_',
                       suffix='[A-Z]').reset_index()
                          )
res1

     id letters Pass
0   110101  X   1
1   110102  X   2
2   110101  Y   2
3   110102  Y   1
4   110101  Z   4
5   110102  Z   3

#create second dataframe
res2 = (df1
        .melt('id')
        .drop('value', axis=1)
        .assign(numbers=lambda x: x.variable.str.split('_').str.get(-1))
        .astype( {'numbers': int})
       )

res2

      id    variable    numbers
0   110101  Pass_01       1
1   110102  Pass_01       1
2   110101  Pass_02       2
3   110102  Pass_02       2
4   110101  Pass_03       3
5   110102  Pass_03       3

#merge the two dataframes, and pivot to get ur final output

outcome = (res1
           .merge(res2,
                  left_on=['id','Pass'],
                  right_on=['id','numbers'],
                  how='right')
           .pivot(columns='variable',values='letters',index='id')
           .bfill()
           .reset_index()
           .rename_axis(columns=None)
          )

outcome

      id    Pass_01 Pass_02 Pass_03
0   110101     X       Y       Z
1   110102     Y       X       Z

【讨论】:

    【解决方案3】:

    您可以使用stackunstack 以及一些set_indexreset_index 来处理逻辑中的哪些列。

    df2 = (df1.set_index('id') #set index any columns not in the logic of pass
              # remove the prefix Pass_
              .rename(columns=lambda col: col.replace('Pass_', ''))
              # stack the datafrae to make it a serie and sort the passes
              .stack().sort_values()
              # next two method exchange the old pass index to the new pass index
              .reset_index(level=1, name='Pass')
              .set_index('Pass', append=True)['level_1']
              # from the serie to the dataframe shape
              .unstack()
              # rename the columns with the prefix pass_
              .rename(columns=lambda col: f'Pass_{col:02}')
              # rename axis to none
              .rename_axis(None, axis=1)
              # but back the id as a column
              .reset_index())
    
    print (df2)
           id Pass_01 Pass_02 Pass_03
    0  110101       X       Y       Z
    1  110102       Y       X       Z
    

    注意:如果您不想在流程中包含其他列,则首先将它们设置为带有 id 的索引,例如 set_index(['id','col1', ...])

    【讨论】:

      猜你喜欢
      • 2019-05-04
      • 2016-12-11
      • 2019-04-28
      • 1970-01-01
      • 1970-01-01
      • 2020-01-19
      • 2018-06-20
      • 1970-01-01
      相关资源
      最近更新 更多