【问题标题】:Vectorizing pandas iteration向量化 pandas 迭代
【发布时间】:2019-07-23 18:20:06
【问题描述】:

我有一个工作代码,它迭代 df 并返回 return other_df。我正在尝试对其进行矢量化,因为它很慢。我正在尝试创建一个funcdf.apply(func)

生成的数据帧长度更长,这就是为什么我似乎需要返回另一个数据帧作为.apply 的结果。

我最初的df 是一个公寓列表,其中包含一列房间列表及其属性。

每一行都包含这样的东西:

rooms                 | apartment number 

[['375', 'LET', ''],  |   12345
['335', 'LET', ''],   |
['360', 'LET', ''],   |
['295', 'double', ''],|
['360', 'LET', '']]   |
__________________________________________________

我需要一个生成的 df,例如:

apartment number | room number | price | if let
   12345         | 12345-1     | 375   |  True
   12345         | 12345-2     | 335   |  True
   12345         | 12345-3     | 360   |  True
   12345         | 12345-4     | 295   |  False
   12345         | 12345-5     | 360   |  True

生成的 df 应该是 roomsdf。在转换时,会进行一些数据清理和提取,包括房间号分配,基于列表中的对象索引,存储在初始 df 单元格中,我不确定是否可以进行矢量化(?)

如果可能的话,我认为我的选择是使用.apply 一次性完成所有工作。如果不是,那么我需要将初始 df 覆盖到多索引中而不是数据透视表中。

我的草稿代码如下所示:

def rooms_df(row):
    columns=['room_price',
         'room_type',
         'en_suite',
         'if_let',
         'room_number',
         'listing_id']

df = pd.DataFrame(columns=columns)
    for room in row['rooms']:
        number=0
        if room[0] == 'na':
            room_price = None
            room_type = None
            en_suite = None
            if_let = None
        elif room[0] == 'occupied':
            room_price = None
            room_type = None
            en_suite = None
            if_let = True
        else:
            room_price = room[0]

            if 'single' in room:
                room_type = 'single'
            elif 'double' in room:
                room_type = 'double'
            else:
                room_type = None
            if 'suite' in room:
                en_suite = True
            else:
                en_suite = False

            if 'LET' in room:
                if_let = True
            else:
                if_let = False

        listing_id = row['listing_id']

        number = number+1
        room_number = f'{listing_id}-{number}'

感谢您的想法!

【问题讨论】:

    标签: python pandas vectorization apply


    【解决方案1】:
    1. rooms 列(列表列表的列)拆分为单独的行,每行都有一个列表。

      df_new = pd.DataFrame(df['rooms'].tolist()) \
      .merge(df, left_index = True, right_index = True) \
      .drop('rooms', axis=1) \
      .melt(id_vars = ['apt'], value_name = 'rooms') \
      .drop('variable', axis=1)
      

      输出:

      apt     rooms
      12345   ['375', 'LET', '']
      12345   ['335', 'LET', '']
      12345   ['360', 'LET', '']
      12345   ['295', 'double', '']
      12345   ['360', 'LET', '']
      
    2. 现在将rooms 中的每个元素拆分为单独的列:

      df_new[['price','if_let', 'foo']] = pd.DataFrame(df_new['rooms'].values.tolist(), index=df_new.index) 
      df_new = df_new.drop(['rooms', 'foo'], axis=1)
      

      输出:

      apt     price   if_let  
      12345   375     LET 
      12345   335     LET 
      12345   360     LET 
      12345   295     double  
      12345   360     LET 
      

      如果列表中的元素数量不相等,您可以使用add_prefix。这将创建新列,其数量等于列中列表的最大大小。

      pd.DataFrame(df_new['rooms'].values.tolist(), index=df_new.index).add_prefix('foo_')
      

      您可以稍后重命名这些列。

    3. 通过在apt 上分组并使用cumcount 为房间号创建新列:

      df_new['count'] = df_new.groupby('apt').cumcount()+1
      df_new['room_num'] = df_new['apt'].astype(str) + '-' + df_new['count'].astype(str)
      

      输出:

      apt     price   if_let  count   room_num
      12345   375     LET     1       12345-1
      12345   335     LET     2       12345-2
      12345   360     LET     3       12345-3
      12345   295     double  4       12345-4
      12345   360     LET     5       12345-5
      

    您现在可以根据需要修改列。 例如:

    df_new['if_let] = np.where(df_new['if_let'] == 'LET', True, False)
    

    如果你有一个大数据框,尽量不要使用df.apply,因为它会让你的操作非常慢。

    【讨论】:

    • 谢谢,真的很有帮助!唯一的问题似乎是列表彼此不一致,并且需要根据列表中特定元素的存在来填充表格。我只知道迭代这个.. 以下是可能的值类型范围:[351, 'single', ''], [295, 'NOW', 'LET', ''], ['na'], ['450', 'double', 'en', 'suite', ''],
    • @DmitriyGrankin 列表中元素的顺序是否一致(价格总是排在第一位)?我建议您进行一些预处理并在特定列不存在元素的地方添加空字符串。尽管如此,我已经更新了第 2 步以适应不相等的列,但这仅在保留订单的情况下才有效。
    • 价格确实是第一位的,但其他值会有所不同。我找到了使用 df['rooms'].apply(pd.Series) 将值分隔到列并使用 pd.where 对列之间的值进行排序的解决方案,但是正如您之前提到的那样,apply 很低。
    猜你喜欢
    • 2020-07-14
    • 2020-12-17
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    • 2017-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多