【问题标题】:Pandas/Python: Set value of one column based on value in another columnPandas/Python:根据另一列中的值设置一列的值
【发布时间】:2018-08-16 02:22:00
【问题描述】:

我需要根据 Pandas 数据框中另一列的值设置一列的值。这是逻辑:

if df['c1'] == 'Value':
    df['c2'] = 10
else:
    df['c2'] = df['c3']

我无法让它做我想做的事,即简单地创建一个具有新值的列(或更改现有列的值:任何一个都适合我)。

如果我尝试运行上面的代码,或者如果我将其编写为函数并使用 apply 方法,我会得到以下结果:

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

【问题讨论】:

    标签: python pandas conditional


    【解决方案1】:

    一种方法是使用.loc 的索引。

    示例

    在没有示例数据框的情况下,我将在这里制作一个:

    import numpy as np
    import pandas as pd
    
    df = pd.DataFrame({'c1': list('abcdefg')})
    df.loc[5, 'c1'] = 'Value'
    
    >>> df
          c1
    0      a
    1      b
    2      c
    3      d
    4      e
    5  Value
    6      g
    

    假设您想创建一个新列c2,相当于c1,除非c1Value,在这种情况下,您想将其分配给10:

    首先,您可以创建一个新列 c2,并将其设置为与 c1 等效,使用以下两行之一(它们基本上做同样的事情):

    df = df.assign(c2 = df['c1'])
    # OR:
    df['c2'] = df['c1']
    

    然后,使用.loc 找到c1 等于'Value' 的所有索引,并在这些索引处在c2 中分配您想要的值:

    df.loc[df['c1'] == 'Value', 'c2'] = 10
    

    你最终会得到这个:

    >>> df
          c1  c2
    0      a   a
    1      b   b
    2      c   c
    3      d   d
    4      e   e
    5  Value  10
    6      g   g
    

    如果正如您在问题中所建议的那样,您有时可能只想替换您已有的列中的值,而不是创建新列,然后跳过列创建,并执行以下操作:

    df['c1'].loc[df['c1'] == 'Value'] = 10
    # or:
    df.loc[df['c1'] == 'Value', 'c1'] = 10
    

    给你:

    >>> df
          c1
    0      a
    1      b
    2      c
    3      d
    4      e
    5     10
    6      g
    

    【讨论】:

    • 第二个解决方案为我解决了问题。我没有意识到您可以像 SQL 中的 WHERE 语句一样使用 .loc 。说得通。谢谢!
    • 如何将 10 应用于多列而不是仅应用于一列?例如:df.loc[df['c1'] == 'Value', 'c2','c3','c4] = 10.
    • 我认为您必须将更新值所需的所有列放在一个列表中,然后遍历该列表并更改其中的列名参数?
    • 当我用df.loc[df['c1'] == 'Value', 'c1'] = 10 之类的语句赋值时,我得到了可怕的SettingWithCopyWarning——即使我使用.loc[] 语法。到目前为止,这对我来说还不是问题,但我发现我收到警告很奇怪,尽管使用了建议的 .loc[] 方法。有什么解决办法吗?这适用于 pandas 1.2.3、numpy 1.18.5 和 python 3.7.10。
    • 假设我有一个 int 列,如果它的值大于 1000,我想将它的值除以 1000。使用第一个选项 df['c1'].loc[df['c1'] > 1000] = df['c1'].loc[df['c1'] > 1000]/1000 我得到了 SettingWithCopyWarning。但是,使用第二个选项 df['c1'].loc[df['c1'] > 1000, 'c1'] 我没有收到该警告。
    【解决方案2】:

    您可以使用np.where()根据指定条件设置值:

    #df
       c1  c2  c3
    0   4   2   1
    1   8   7   9
    2   1   5   8
    3   3   3   5
    4   3   6   8
    

    现在根据您的情况更改['c2'] 列中的值(或设置)。

    df['c2'] = np.where(df.c1 == 8,'X', df.c3)
    
       c1  c2  c3
    0   4   1   1
    1   8   X   9
    2   1   8   8
    3   3   5   5
    4   3   8   8
    

    【讨论】:

    • 如果我想保留所有原始列怎么办
    • @mLstudent33,使用df['newColName'] = ...,您使用列名,而不是在数据框中创建新列,假设newColName 尚不存在
    • 优于所有其他解决方案,因为 (a) 由于术语顺序的不明确性而更具可读性,并且 (b) 它更具未来性,因为它更容易修改以考虑多个列并且 ( c) 它很快,在 lambda 中没有解释代码。
    • @DJK 我猜你代码中的c2 指的是输出表中的c4?你最好改正其中一个。
    【解决方案3】:

    尝试:

    df['c2'] = df['c1'].apply(lambda x: 10 if x == 'Value' else x)

    【讨论】:

    • 谢谢@AlexanderHughes。我原来的帖子有一个错字:实际上要考虑三列,所以这个解决方案行不通。
    • 应该是df.apply(lambda x: 10 if x['c1'] == 'Value' else x['c3'],axis=1)
    • 这可能会对大型数据集造成性能问题。 df.apply() 比较慢。
    • 正在寻找相同的东西,发现一个 lambda 对我有用的数据框。我的代码是 'ard['Hr'] = ard.apply(lambda x: x['Hr']+1 if x['Mi'] >= 45 and x['Mi']
    【解决方案4】:

    注意反转选择的波浪号。它使用 pandas 方法(即比 if/else 更快)。

    df.loc[(df['c1'] == 'Value'), 'c2'] = 10
    df.loc[~(df['c1'] == 'Value'), 'c2'] = df['c3']
    

    【讨论】:

      【解决方案5】:

      我建议分两步进行:

      # set fixed value to 'c2' where the condition is met
      df.loc[df['c1'] == 'Value', 'c2'] = 10
      
      # copy value from 'c3' to 'c2' where the condition is NOT met
      df.loc[df['c1'] != 'Value', 'c2'] = df[df['c1'] != 'Value', 'c3']
      

      【讨论】:

      • 这太混乱了,你能展示一些表格来澄清一下吗?
      【解决方案6】:

      您可以使用pandas.DataFrame.mask 添加几乎任意数量的条件:

      data = {'a': [1,2,3,4,5], 'b': [6,8,9,10,11]}
      
      d = pd.DataFrame.from_dict(data, orient='columns')
      c = {'c1': (2, 'Value1'), 'c2': (3, 'Value2'), 'c3': (5, d['b'])}
      
      d['new'] = np.nan
      for value in c.values():
          d['new'].mask(d['a'] == value[0], value[1], inplace=True)
      
      d['new'] = d['new'].fillna('Else')
      d
      

      输出:

          a   b   new
      0   1   6   Else
      1   2   8   Value1
      2   3   9   Value2
      3   4   10  Else
      4   5   11  11
      

      【讨论】:

        【解决方案7】:

        我相信Series.map() 非常易读和高效,例如:

        df["c2"] = df["c1"].map(lambda x: 10 if x == 'Value' else x)
        

        我喜欢它,因为如果条件逻辑变得更复杂,您可以将其移动到一个函数中,然后传入该函数而不是 lambda。

        如果您需要将条件逻辑基于多个列,您可以按照其他人的建议使用 DataFrame.apply()

        【讨论】:

          【解决方案8】:

          如果您有中小型数据框,请尝试 df.apply(),

          df['c2'] = df.apply(lambda x: 10 if x['c1'] == 'Value' else x['c1'], axis = 1)
          

          否则,如果您有大数据框,请遵循上述 cmets 中提到的切片技术。

          【讨论】:

            【解决方案9】:

            我有一个大数据集,而 .loc[] 花费的时间太长,所以我找到了一种矢量化的方法来完成它。回想一下,您可以将列设置为逻辑运算符,这样就可以了:

            file['Flag'] = (file['Claim_Amount'] > 0)

            这给出了一个我想要的布尔值,但是你可以将它乘以 1 来生成一个整数。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2022-07-29
              • 1970-01-01
              • 1970-01-01
              • 2016-10-24
              • 1970-01-01
              • 2021-03-30
              • 2019-04-04
              相关资源
              最近更新 更多