矢量化熊猫迭代

时间:2019-03-01 17:10:19

标签: python pandas vectorization apply

我有一个有效的代码,该代码迭代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 应该是{strong>房间中的df。转换时,会进行一些数据清理和提取,包括根据列表中的对象索引分配房间号,并将其存储在初始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}'

谢谢您的想法!

1 个答案:

答案 0 :(得分:2)

  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,因为它会使您的操作非常缓慢。