【问题标题】:Lambda including if...elif...elseLambda 包括 if...elif...else
【发布时间】:2017-12-12 23:19:02
【问题描述】:

我想在 lambda 函数中使用 if...elif...else 将 lambda 函数应用于 DataFrame 列。

df 和代码类似于:

df=pd.DataFrame({"one":[1,2,3,4,5],"two":[6,7,8,9,10]})

df["one"].apply(lambda x: x*10 if x<2 elif x<4 x**2 else x+10)

显然,这是行不通的。有没有办法将 if....elif....else 应用于 lambda? 如何使用 List Comprehension 获得相同的结果?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    您可以使用多个 loc 运算符来完成。这是一个新创建的列,标记为“new”,并应用了条件计算:

    df.loc[(df['one'] < 2), 'new'] = df['one'] * 10
    df.loc[(df['one'] < 4), 'new'] = df['one'] ** 2
    df.loc[(df['one'] >= 4), 'new'] = df['one'] + 10
    

    【讨论】:

      【解决方案2】:

      我不建议在这里使用apply:如果有更好的选择,应该避免使用。

      例如,如果您对 Series 执行以下操作:

      if cond1:
          exp1
      elif cond2:
          exp2
      else:
          exp3
      

      这通常是 np.wherenp.select 的一个很好的用例。


      numpy.where

      上面的ifelse链可以用

      来写
      np.where(cond1, exp1, np.where(cond2, exp2, ...))
      

      np.where 允许嵌套。通过一级嵌套,您的问题可以解决,

      df['three'] = (
          np.where(
              df['one'] < 2, 
              df['one'] * 10, 
              np.where(df['one'] < 4, df['one'] ** 2, df['one'] + 10))
      df
      
         one  two  three
      0    1    6     10
      1    2    7      4
      2    3    8      9
      3    4    9     14
      4    5   10     15
      

      numpy.select

      允许灵活的语法并且易于扩展。它遵循形式,

      np.select([cond1, cond2, ...], [exp1, exp2, ...])
      

      或者,在这种情况下,

      np.select([cond1, cond2], [exp1, exp2], default=exp3)
      

      df['three'] = (
          np.select(
              condlist=[df['one'] < 2, df['one'] < 4], 
              choicelist=[df['one'] * 10, df['one'] ** 2], 
              default=df['one'] + 10))
      df
      
         one  two  three
      0    1    6     10
      1    2    7      4
      2    3    8      9
      3    4    9     14
      4    5   10     15
      

      and/or(类似于if/else

      类似于if-else,需要lambda

      df['three'] = df["one"].apply(
          lambda x: (x < 2 and x * 10) or (x < 4 and x ** 2) or x + 10) 
      
      df
         one  two  three
      0    1    6     10
      1    2    7      4
      2    3    8      9
      3    4    9     14
      4    5   10     15
      

      列表理解

      apply仍然快的循环解决方案。

      df['three'] = [x*10 if x<2 else (x**2 if x<4 else x+10) for x in df['one']]
      # df['three'] = [
      #    (x < 2 and x * 10) or (x < 4 and x ** 2) or x + 10) for x in df['one']
      # ]
      df
         one  two  three
      0    1    6     10
      1    2    7      4
      2    3    8      9
      3    4    9     14
      4    5   10     15
      

      【讨论】:

      • 也感谢分享+1,我也不明白为什么不在这里defafunction
      • @Wen Lambdas 通常非常简洁。如果您只使用一次,则无需定义函数。 Lambda 是使用和抛出。
      • 嗯,我的意思是定义 if..else 函数并应用它
      • @Wen 是的。这可能的,你可以做到。仅此而已,如果您不在多个地方使用它,则没有必要。
      • 另一种“模拟” if....elif...else 行为的方法是从第一个 lambda 函数中调用另一个 lambda 函数,例如:df["one"]=df["one"].apply(lambda x: x**2 if x==2 else lambda2(x)) lambda2= lambda x: x**3 if x==3 else lambda3(x) lambda3=lambda x: x**4 if x==4 else lambda4(x) lambda4=lambda x: x**5 if x==5 else x
      【解决方案3】:

      为了可读性,我更喜欢编写一个函数,尤其是当您处理许多条件时。对于原始问题:

      def parse_values(x):
          if x < 2:
             return x * 10
          elif x < 4:
             return x ** 2
          else:
             return x + 10
      
      df['one'].apply(parse_values)
      

      【讨论】:

      • 我也更喜欢显式函数,尤其是当您的 if-else 链变长时
      【解决方案4】:

      if .. elses:

      lambda x: x*10 if x<2 else (x**2 if x<4 else x+10)
      

      【讨论】:

      • @cᴏʟᴅsᴘᴇᴇᴅ 如果您的意思是“不仅在 lambda 内部”,那么是的,这在任何地方都有效。事实上,这是我知道的唯一一种制作单行 if-elif-else 的方法,除了使用 or/and 技巧。
      • @Rightleg 短路可能在这里不起作用。考虑x==0 - 在x*10 之后的or 会向前滑动,而不是返回0
      • @cᴏʟᴅsᴘᴇᴇᴅ a or b 不返回 TrueFalse,但如果计算结果为 a,则返回 a,否则 b
      • @Uriel 是的。无论如何,我不太喜欢这个技巧,因为当涉及到非常抽象的对象时,很难确定什么会被评估为True