【问题标题】:How to fill in empty column in a dataframe with a particular element from the list of another column?如何使用另一列列表中的特定元素填充数据框中的空列?
【发布时间】:2019-02-04 09:22:23
【问题描述】:

拥有一个由人和订单组成的数据框...

person     order                                 elements
Alice      [drink, snack, salad, fish, dessert]  5          
Tom        [drink, snack]                        2          
John       [drink, snack, soup, chicken]         4          
Mila       [drink, snack, soup]                  3          

我想知道顾客的主餐是什么。因此,我想添加另一列 [main_meal],这将是我的 df。

person     order                               elements   main_meal
Alice      [drink, snack, salad, fish, dessert]  5          fish
Tom        [drink, snack]                        2          none
John       [drink, snack, soup, chicken]         4          chicken
Mila       [drink, snack, soup]                  3          none

规则是,如果客户点了 4 餐或更多餐,则意味着第 4 个元素始终是主菜,所以我想从 order 列的列表中提取第 4 个元素。如果它包含少于 4 个元素,则将“main_meal”分配给 none。我的代码:

df['main_meal'] = ''
if df['elements'] >= 4:
     df['main_meal'] = df.order[3]
else:
     df['main_meal'] = 'none'

它不起作用:

 ValueError                                Traceback (most recent call last)
 <ipython-input-100-39b7809cc669> in <module>()
     1 df['main_meal'] = ''
     2 df.head(5)
 ----> 3 if df['elements'] >= 4:
       4     df['main_meal'] = df.order[3]
       5 else:

 ~\Anaconda\lib\site-packages\pandas\core\generic.py in __nonzero__(self)
 1571         raise ValueError("The truth value of a {0} is ambiguous. "
 1572                          "Use a.empty, a.bool(), a.item(), a.any() or 
 a.all()."
 -> 1573                          .format(self.__class__.__name__))
 1574 
 1575     __bool__ = __nonzero__

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

我的代码有什么问题?

【问题讨论】:

  • df['elements'] &gt;= 4 是一个数组。有些元素可能是真的,有些可能是假的。你必须解释if语句你想如何处理这种情况。
  • @DYZ 我认为他的意思是if len(df['elements']) &gt;= 4,但他没有对操作进行矢量化,他是在一行中思考。
  • 这个问题有几十个骗子。这是one
  • np.where(len(df.order.str.len()&gt;4),df.order.str[3],'None')

标签: python pandas


【解决方案1】:

使用str方法进行切片

In [324]: df['order'].str[3]
Out[324]:
0       fish
1        NaN
2    chicken
3        NaN
Name: order, dtype: object

In [328]: df['main_meal'] = df['order'].str[3].fillna('none')

In [329]: df
Out[329]:
  person                                 order  elements main_meal
0  Alice  [drink, snack, salad, fish, dessert]         5      fish
1    Tom                        [drink, snack]         2      none
2   John         [drink, snack, soup, chicken]         4   chicken
3   Mila                  [drink, snack, soup]         3      none

【讨论】:

    【解决方案2】:

    对于小型数据帧,您可以按照@Zero's solution 使用str 访问器。对于较大的数据帧,您可能希望使用 NumPy 表示来创建系列:

    # Benchmarking on Python 3.6, Pandas 0.19.2
    
    df = pd.concat([df]*100000)
    
    %timeit pd.DataFrame(df['order'].values.tolist())[3]  # 125 ms per loop
    %timeit df['order'].str[3]                            # 185 ms per loop
    
    # check results align
    x = pd.DataFrame(df['order'].values.tolist())[3].fillna('None').values
    y = df['order'].str[3].fillna('None').values
    assert (x == y).all()
    

    【讨论】:

      【解决方案3】:

      您还可以使用 apply 和 lambda 函数。

      df['main_meal'] = df['order'].apply(lambda r: r[3] if len(r) >= 4 else 'none')
      

      对于大型数据集,它比 @jpp's 答案慢,但对于较小数据集,它比 @jpp 和 @Zero's 答案更快(并且更详细)(请注意,我添加了 .fillna(),因此它们返回相同的结果):

      %timeit df['order'].apply(lambda r: r[3] if len(r) >= 4 else 'none')  # 242 µs
      %timeit pd.DataFrame(df['order'].values.tolist())[3].fillna('none')  # 1.17 ms
      %timeit df['order'].str[3].fillna('none')  # 487 µs
      
      # Large dataset
      df = pd.concat([df]*100000)
      
      %timeit df['order'].apply(lambda r: r[3] if len(r) >= 4 else 'none')  # 118ms
      %timeit pd.DataFrame(df['order'].values.tolist())[3].fillna('none')  # 51.8ms
      %timeit df['order'].str[3].fillna('none')  # 309ms
      

      如果你检查它们的值,它们就会匹配。

      x = df['order'].apply(lambda r: r[3] if len(r) >= 4 else 'none')
      y = pd.DataFrame(df['order'].values.tolist())[3].fillna('none')
      z = df['order'].str[3].fillna('none')
      
      (x.values == y.values).all()  # True
      (x.values == z.values).all()  # True
      

      Python 3.6.6 |熊猫 0.23.4

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-24
        • 2022-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多