向量化 pandas 次迭代

Vectorizing pandas iteration

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

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

的结果

我的初始 df 公寓 的列表,其中包含 列表 的列 rooms 及其属性。

每一行都包含这样的内容:

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

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

我的代码草稿如下所示:

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}'

谢谢你的想法!

  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)

Try not to use df.apply if you have a big dataframe because it will make your operation really slow.