【问题标题】:Pandas conditional creation of a series/dataframe columnPandas 有条件地创建系列/数据框列
【发布时间】:2021-12-28 02:59:29
【问题描述】:

我有一个如下所示的数据框:

    Type       Set
1    A          Z
2    B          Z           
3    B          X
4    C          Y

我想将另一列添加到数据帧(或生成一系列),其长度与数据帧相同(记录/行数相等),如果Set == 'Z' 设置颜色'green',如果@'red' 987654325@ 等于其他任何值。

最好的方法是什么?

【问题讨论】:

    标签: python pandas numpy dataframe


    【解决方案1】:

    如果您只有两个选择:

    df['color'] = np.where(df['Set']=='Z', 'green', 'red')
    

    例如,

    import pandas as pd
    import numpy as np
    
    df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
    df['color'] = np.where(df['Set']=='Z', 'green', 'red')
    print(df)
    

    产量

      Set Type  color
    0   Z    A  green
    1   Z    B  green
    2   X    B    red
    3   Y    C    red
    

    如果您有两个以上的条件,请使用np.select。例如,如果您希望 color

    • yellow(df['Set'] == 'Z') & (df['Type'] == 'A')
    • 否则blue(df['Set'] == 'Z') & (df['Type'] == 'B')
    • 否则purple(df['Type'] == 'B')
    • 否则black,

    然后使用

    df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
    conditions = [
        (df['Set'] == 'Z') & (df['Type'] == 'A'),
        (df['Set'] == 'Z') & (df['Type'] == 'B'),
        (df['Type'] == 'B')]
    choices = ['yellow', 'blue', 'purple']
    df['color'] = np.select(conditions, choices, default='black')
    print(df)
    

    产生

      Set Type   color
    0   Z    A  yellow
    1   Z    B    blue
    2   X    B  purple
    3   Y    C   black
    

    【讨论】:

    • 必须使用numpy的原因是什么?
    • 这是 pandas 构建的库。由于它已经具有此功能,因此无需 pandas 来实现它。
    • 此代码现在(2022 年 1 月)返回 A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead
    【解决方案2】:

    列表推导是另一种有条件地创建另一列的方法。如果您在列中使用对象 dtype,例如在您的示例中,列表推导通常优于大多数其他方法。

    示例列表理解:

    df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
    

    %timeit 测试:

    import pandas as pd
    import numpy as np
    
    df = pd.DataFrame({'Type':list('ABBC'), 'Set':list('ZZXY')})
    %timeit df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
    %timeit df['color'] = np.where(df['Set']=='Z', 'green', 'red')
    %timeit df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')
    
    1000 loops, best of 3: 239 µs per loop
    1000 loops, best of 3: 523 µs per loop
    1000 loops, best of 3: 263 µs per loop
    

    【讨论】:

    • 请注意,对于更大的数据帧(想想pd.DataFrame({'Type':list('ABBC')*100000, 'Set':list('ZZXY')*100000})-size),numpy.where 超过 map,但列表理解是王道(比 numpy.where 快大约 50%)。
    • 如果条件需要多列信息,是否可以使用列表推导法?我正在寻找这样的东西(这不起作用):df['color'] = ['red' if (x['Set'] == 'Z') & (x['Type'] == 'B') else 'green' for x in df]
    • 将 iterrows 添加到数据框,然后您可以通过 row 访问多个列: ['red' if (row['Set'] == 'Z') & (row['Type'] = = 'B') else 'green' 用于索引,在 df.iterrows() 中的行]
    • 请注意,如果您需要从数据框中的另一个系列中获取替换值,例如df['color_type'] = np.where(df['Set']=='Z', 'green', df['Type']),这个不错的解决方案将不起作用
    • @cheekybastard 或者不要,因为.iterrows() 是出了名的迟钝,在迭代时不应该修改 DataFrame。
    【解决方案3】:

    实现这一目标的另一种方法是

    df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')
    

    【讨论】:

      【解决方案4】:

      以下比定时here的方法要慢,但是我们可以根据多列的内容计算额外的列,并且可以为额外的列计算两个以上的值。

      仅使用“Set”列的简单示例:

      def set_color(row):
          if row["Set"] == "Z":
              return "red"
          else:
              return "green"
      
      df = df.assign(color=df.apply(set_color, axis=1))
      
      print(df)
      
        Set Type  color
      0   Z    A    red
      1   Z    B    red
      2   X    B  green
      3   Y    C  green
      

      考虑更多颜色和更多列的示例:

      def set_color(row):
          if row["Set"] == "Z":
              return "red"
          elif row["Type"] == "C":
              return "blue"
          else:
              return "green"
      
      df = df.assign(color=df.apply(set_color, axis=1))
      
      print(df)
      
        Set Type  color
      0   Z    A    red
      1   Z    B    red
      2   X    B  green
      3   Y    C   blue
      

      编辑(21/06/2019):使用 plydata

      也可以使用plydata 来做这种事情(不过这似乎比使用assignapply 还要慢)。

      from plydata import define, if_else
      

      简单if_else

      df = define(df, color=if_else('Set=="Z"', '"red"', '"green"'))
      
      print(df)
      
        Set Type  color
      0   Z    A    red
      1   Z    B    red
      2   X    B  green
      3   Y    C  green
      

      嵌套if_else:

      df = define(df, color=if_else(
          'Set=="Z"',
          '"red"',
          if_else('Type=="C"', '"green"', '"blue"')))
      
      print(df)                            
      
        Set Type  color
      0   Z    A    red
      1   Z    B    red
      2   X    B   blue
      3   Y    C  green
      

      【讨论】:

      • 我们如何使用这种函数引用其他行?例如。 if row["Set"].shift(1) == "Z":,但这不起作用
      • @ChrisDixon 据我所知,apply 只能看到一行或一列(取决于选择的轴),但不能看到当前处理的行或列之外的其他行或列。跨度>
      【解决方案5】:

      这是给这只猫换皮的另一种方法,使用字典将新值映射到列表中的键:

      def map_values(row, values_dict):
          return values_dict[row]
      
      values_dict = {'A': 1, 'B': 2, 'C': 3, 'D': 4}
      
      df = pd.DataFrame({'INDICATOR': ['A', 'B', 'C', 'D'], 'VALUE': [10, 9, 8, 7]})
      
      df['NEW_VALUE'] = df['INDICATOR'].apply(map_values, args = (values_dict,))
      

      它是什么样子的:

      df
      Out[2]: 
        INDICATOR  VALUE  NEW_VALUE
      0         A     10          1
      1         B      9          2
      2         C      8          3
      3         D      7          4
      

      当您要创建许多 ifelse-type 语句(即要替换许多唯一值)时,这种方法会非常强大。

      当然你也可以这样做:

      df['NEW_VALUE'] = df['INDICATOR'].map(values_dict)
      

      但在我的机器上,这种方法比上面的 apply 方法慢三倍以上。

      你也可以这样做,使用dict.get:

      df['NEW_VALUE'] = [values_dict.get(v, None) for v in df['INDICATOR']]
      

      【讨论】:

      • 我喜欢这个答案,因为它展示了如何对值进行多次替换
      • 但在我的机器上,这种方法比上面的 apply 方法慢三倍以上。 你是如何对这些进行基准测试的?根据我的快速测量,.map() 解决方案比.apply() 快约 10 倍。
      • 更新:在 100,000,000 行,52 个字符串值上,.apply() 需要 47 秒,而 .map() 只需 5.91 秒。
      【解决方案6】:

      您可以简单地使用强大的.loc 方法并根据需要使用一个或多个条件(使用pandas=1.0.5 测试)。

      代码摘要:

      df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
      df['Color'] = "red"
      df.loc[(df['Set']=="Z"), 'Color'] = "green"
      
      #practice!
      df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"
      
      

      解释:

      df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
      
      # df so far: 
        Type Set  
      0    A   Z 
      1    B   Z 
      2    B   X 
      3    C   Y
      

      添加一个“颜色”列并将所有值设置为“红色”

      df['Color'] = "red"
      

      应用你的单一条件:

      df.loc[(df['Set']=="Z"), 'Color'] = "green"
      
      
      # df: 
        Type Set  Color
      0    A   Z  green
      1    B   Z  green
      2    B   X    red
      3    C   Y    red
      

      或多个条件,如果你想:

      df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"
      

      您可以在此处阅读 Pandas 逻辑运算符和条件选择: Logical operators for boolean indexing in Pandas

      【讨论】:

        【解决方案7】:

        你可以使用pandas方法wheremask

        df['color'] = 'green'
        df['color'] = df['color'].where(df['Set']=='Z', other='red')
        # Replace values where the condition is False
        

        df['color'] = 'red'
        df['color'] = df['color'].mask(df['Set']=='Z', other='green')
        # Replace values where the condition is True
        

        或者,您可以将方法 transform 与 lambda 函数一起使用:

        df['color'] = df['Set'].transform(lambda x: 'green' if x == 'Z' else 'red')
        

        输出:

          Type Set  color
        1    A   Z  green
        2    B   Z  green
        3    B   X    red
        4    C   Y    red
        

        来自@chai 的性能比较:

        import pandas as pd
        import numpy as np
        df = pd.DataFrame({'Type':list('ABBC')*1000000, 'Set':list('ZZXY')*1000000})
         
        %timeit df['color1'] = 'red'; df['color1'].where(df['Set']=='Z','green')
        %timeit df['color2'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
        %timeit df['color3'] = np.where(df['Set']=='Z', 'red', 'green')
        %timeit df['color4'] = df.Set.map(lambda x: 'red' if x == 'Z' else 'green')
        
        397 ms ± 101 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
        976 ms ± 241 ms per loop
        673 ms ± 139 ms per loop
        796 ms ± 182 ms per loop
        

        【讨论】:

        • 也更快: import pandas as pd import numpy as np df = pd.DataFrame({'Type':list('ABBC')*1000000, 'Set':list('ZZXY' )*1000000}) %timeit df['color1'] = 'red'; df['color1'].where(df['Set']=='Z','green') %timeit df['color2'] = ['red' if x == 'Z' else 'green' for x in df['Set']] %timeit df['color3'] = np.where(df['Set']=='Z', 'red', 'green') %timeit df['color4'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green') 397 ms ± 101 ms 每个循环(平均值±标准差。7 次运行,每个循环 1 个循环)976 ms ±每个循环 241 毫秒 每个循环 673 毫秒 ± 139 毫秒 每个循环 796 毫秒 ± 182 毫秒
        • @chai 将您的评价添加到我的回答中。谢谢!
        【解决方案8】:

        .apply() 方法的一个班轮如下:

        df['color'] = df['Set'].apply(lambda set_: 'green' if set_=='Z' else 'red')
        

        之后,df 数据框如下所示:

        >>> print(df)
          Type Set  color
        0    A   Z  green
        1    B   Z  green
        2    B   X    red
        3    C   Y    red
        

        【讨论】:

          【解决方案9】:

          如果您只有 2 个选择,请使用 np.where()

          df = pd.DataFrame({'A':range(3)})
          df['B'] = np.where(df.A>2, 'yes', 'no')
          

          如果您有超过 2 个选择,也许apply() 可以工作 输入

          arr = pd.DataFrame({'A':list('abc'), 'B':range(3), 'C':range(3,6), 'D':range(6, 9)})
          

          而arr是

              A   B   C   D
          0   a   0   3   6
          1   b   1   4   7
          2   c   2   5   8
          

          如果您希望 E 列成为 if arr.A =='a' then arr.B elif arr.A=='b' then arr.C elif arr.A == 'c' then arr.D else something_else

          arr['E'] = arr.apply(lambda x: x['B'] if x['A']=='a' else(x['C'] if x['A']=='b' else(x['D'] if x['A']=='c' else 1234)), axis=1)
          

          最后 arr 是

              A   B   C   D   E
          0   a   0   3   6   0
          1   b   1   4   7   4
          2   c   2   5   8   8
          

          【讨论】:

            【解决方案10】:

            如果您要处理海量数据,最好采用记忆化方法:

            # First create a dictionary of manually stored values
            color_dict = {'Z':'red'}
            
            # Second, build a dictionary of "other" values
            color_dict_other = {x:'green' for x in df['Set'].unique() if x not in color_dict.keys()}
            
            # Next, merge the two
            color_dict.update(color_dict_other)
            
            # Finally, map it to your column
            df['color'] = df['Set'].map(color_dict)
            

            当你有很多重复值时,这种方法最快。我的一般经验法则是在以下情况下记忆:data_size > 10**4 & n_distinct data_size/4

            E.x.在一个案例中记忆 10,000 行具有 2,500 个或更少的不同值。

            【讨论】:

            • 好吧,所以只有 2 个不同的值要映射,100,000,000 行,没有“记忆”的情况下运行需要 6.67 秒,而有 9.86 秒。
            • 100,000,000 行,52 个不同的值,其中 1 个映射到第一个输出值,其他 51 个都对应另一个:没有记忆的 7.99 秒,有记忆的 11.1 秒。
            • 你的值是随机排列的吗?还是他们背靠背?大熊猫的高速可能是由于缓存@AMC
            • 你的值是随机排列的吗?还是它们是背靠背的? 值是随机的,使用random.choices() 选择。
            【解决方案11】:

            来自pyjanitorcase_when 函数是pd.Series.mask 的包装器,并为多种条件提供了可链接/方便的形式:

            对于单个条件:

            df.case_when(
                df.col1 == "Z",  # condition
                "green",         # value if True
                "red",           # value if False
                column_name = "color"
                )
            
              Type Set  color
            1    A   Z  green
            2    B   Z  green
            3    B   X    red
            4    C   Y    red
            

            对于多个条件:

            df.case_when(
                df.Set.eq('Z') & df.Type.eq('A'), 'yellow', # condition, result
                df.Set.eq('Z') & df.Type.eq('B'), 'blue',   # condition, result
                df.Type.eq('B'), 'purple',                  # condition, result
                'black',              # default if none of the conditions evaluate to True
                column_name = 'color'  
            )
              Type  Set   color
            1    A   Z  yellow
            2    B   Z    blue
            3    B   X  purple
            4    C   Y   black
            

            更多例子可以在here找到

            【讨论】:

              猜你喜欢
              • 2020-04-23
              相关资源
              最近更新 更多