代码之家  ›  专栏  ›  技术社区  ›  karlphillip

从另一个数据帧填充NaN值(形状不同)

  •  4
  • karlphillip  · 技术社区  · 8 年前

    我正在寻找一种更快的方法来提高以下问题解决方案的性能:某个数据帧有两列,其中 南安 其中的值。挑战在于如何替换这些 南斯 使用来自辅助数据帧的值。

    下面我将分享用于实现我的方法的数据和代码。让我解释一下场景: merged_df 是原始数据帧,其中有几列,其中一些列的行具有 南安 值:

    enter image description here

    如上图所示,列 day_of_week 和 holiday_flg 特别感兴趣。我想填写 南安 通过查看第二个名为 date_info_df ,如下所示:

    enter image description here

    使用列中的值 visit_date 在里面 合并的\u df 可以在上搜索第二个数据帧 calendar_date 并找到等效匹配项。此方法允许获取 周的第天 和 假日\u flg 从第二个数据帧。

    本练习的最终结果是一个如下所示的数据帧:

    enter image description here

    你会注意到我使用的方法依赖于 apply() 在 合并的\u df :

    • 对于每行,搜索 南安 中的值 周的第天 和 假日\u flg ;
    • 当 南安 ,请使用该行中的 访问\u日期 要在第二个数据帧中查找等效匹配,特别是 date_info_df['calendar_date'] 柱
    • 成功匹配后,来自 date_info_df['day_of_week'] 必须复制到 merged_df['day_of_week'] 以及 date_info_df['holiday_flg'] 还必须复制到 date\u info\u df['holiday\u flg'] .

    这是一个工作 源代码 :

    import math
    import pandas as pd
    import numpy as np
    from IPython.display import display
    
    ### Data for df
    data = { 'air_store_id':     [              'air_a1',     'air_a2',     'air_a3',     'air_a4' ], 
             'area_name':        [               'Tokyo',       np.nan,       np.nan,       np.nan ], 
             'genre_name':       [            'Japanese',       np.nan,       np.nan,       np.nan ], 
             'hpg_store_id':     [              'hpg_h1',       np.nan,       np.nan,       np.nan ],          
             'latitude':         [                  1234,       np.nan,       np.nan,       np.nan ], 
             'longitude':        [                  5678,       np.nan,       np.nan,       np.nan ],         
             'reserve_datetime': [ '2017-04-22 11:00:00',       np.nan,       np.nan,       np.nan ], 
             'reserve_visitors': [                    25,           35,           45,       np.nan ], 
             'visit_datetime':   [ '2017-05-23 12:00:00',       np.nan,       np.nan,       np.nan ], 
             'visit_date':       [ '2017-05-23'         , '2017-05-24', '2017-05-25', '2017-05-27' ],
             'day_of_week':      [             'Tuesday',  'Wednesday',       np.nan,       np.nan ],
             'holiday_flg':      [                     0,       np.nan,       np.nan,       np.nan ]
           }
    
    merged_df = pd.DataFrame(data)
    display(merged_df)
    
    ### Data for date_info_df
    data = { 'calendar_date':     [ '2017-05-23', '2017-05-24', '2017-05-25', '2017-05-26', '2017-05-27', '2017-05-28' ], 
             'day_of_week':       [    'Tuesday',  'Wednesday',   'Thursday',     'Friday',   'Saturday',     'Sunday' ], 
             'holiday_flg':       [            0,            0,            0,            0,            1,            1 ]         
           }
    
    date_info_df = pd.DataFrame(data)
    date_info_df['calendar_date'] = pd.to_datetime(date_info_df['calendar_date']) 
    display(date_info_df)
    
    # Fix the NaN values in day_of_week and holiday_flg by inspecting data from another dataframe (date_info_df)
    def fix_weekday_and_holiday(row):
        weekday = row['day_of_week']   
        holiday = row['holiday_flg']
    
        # search dataframe date_info_df for the appropriate value when weekday is NaN
        if (type(weekday) == float and math.isnan(weekday)):
            search_date = row['visit_date']                               
            #print('  --> weekday search_date=', search_date, 'type=', type(search_date))        
            indexes = date_info_df.index[date_info_df['calendar_date'] == search_date].tolist()
            idx = indexes[0]                
            weekday = date_info_df.at[idx,'day_of_week']
            #print('  --> weekday search_date=', search_date, 'is', weekday)        
            row['day_of_week'] = weekday        
    
        # search dataframe date_info_df for the appropriate value when holiday is NaN
        if (type(holiday) == float and math.isnan(holiday)):
            search_date = row['visit_date']                               
            #print('  --> holiday search_date=', search_date, 'type=', type(search_date))        
            indexes = date_info_df.index[date_info_df['calendar_date'] == search_date].tolist()
            idx = indexes[0]                
            holiday = date_info_df.at[idx,'holiday_flg']
            #print('  --> holiday search_date=', search_date, 'is', holiday)        
            row['holiday_flg'] = int(holiday)
    
        return row
    
    
    # send every row to fix_day_of_week
    merged_df = merged_df.apply(fix_weekday_and_holiday, axis=1) 
    
    # Convert data from float to int (to remove decimal places)
    merged_df['holiday_flg'] = merged_df['holiday_flg'].astype(int)
    
    display(merged_df)
    

    我做了一些测量,以便您能够理解这场斗争:

    • 在具有 6. 排, 应用() 拿 3.01毫秒 ;
    • 在具有~ 250000 排, 应用() 拿 2分钟51秒 .
    • 在具有~ 1215000 排, 应用() 拿 4分钟2秒 .

    如何提高此任务的性能?

    3 回复  |  直到 8 年前
        1
  •  4
  •   HYRY    8 年前

    您可以使用 Index 要加快查找速度,请使用 combine_first() 填写NaN:

    cols = ["day_of_week", "holiday_flg"]
    visit_date = pd.to_datetime(merged_df.visit_date)
    merged_df[cols] = merged_df[cols].combine_first(
        date_info_df.set_index("calendar_date").loc[visit_date, cols].set_index(merged_df.index))
    
    print(merged_df[cols])
    

    结果:

     day_of_week  holiday_flg
    0     Tuesday          0.0
    1   Wednesday          0.0
    2    Thursday          0.0
    3    Saturday          1.0
    
        2
  •  1
  •   jpp    8 年前

    这是一个解决方案。它应该是有效的,因为没有明确的 merge 或 apply .

    merged_df['visit_date'] = pd.to_datetime(merged_df['visit_date']) 
    date_info_df['calendar_date'] = pd.to_datetime(date_info_df['calendar_date']) 
    
    s = date_info_df.set_index('calendar_date')['day_of_week']
    t = date_info_df.set_index('day_of_week')['holiday_flg']
    
    merged_df['day_of_week'] = merged_df['day_of_week'].fillna(merged_df['visit_date'].map(s))
    merged_df['holiday_flg'] = merged_df['holiday_flg'].fillna(merged_df['day_of_week'].map(t))
    

    后果

      air_store_id area_name day_of_week genre_name  holiday_flg hpg_store_id  \
    0       air_a1     Tokyo     Tuesday   Japanese          0.0       hpg_h1   
    1       air_a2       NaN   Wednesday        NaN          0.0          NaN   
    2       air_a3       NaN    Thursday        NaN          0.0          NaN   
    3       air_a4       NaN    Saturday        NaN          1.0          NaN   
    
       latitude  longitude     reserve_datetime  reserve_visitors visit_date  \
    0    1234.0     5678.0  2017-04-22 11:00:00              25.0 2017-05-23   
    1       NaN        NaN                  NaN              35.0 2017-05-24   
    2       NaN        NaN                  NaN              45.0 2017-05-25   
    3       NaN        NaN                  NaN               NaN 2017-05-27   
    
            visit_datetime  
    0  2017-05-23 12:00:00  
    1                  NaN  
    2                  NaN  
    3                  NaN  
    

    解释

    • s 是一个 pd.Series 将calendar\u date映射到day\u of\u week from date_info_df .
    • 使用 pd.Series.map ,需要 pd。系列 作为输入,尽可能更新缺失的值。
        3
  •  1
  •   Tai    8 年前

    编辑:也可以使用 merge 解决问题。比旧方法快10倍。(需要确保 "visit_date" 和 "calendar_date" 格式相同。)

    # don't need to `set_index` for date_info_df but select columns needed.
    merged_df.merge(date_info_df[["calendar_date", "day_of_week", "holiday_flg"]], 
                    left_on="visit_date", 
                    right_on="calendar_date", 
                    how="left") # outer should also work
    

    预期结果将为 "day_of_week_y" 和 "holiday_flg_y" 列当前。在这种方法中 map 方法,我们不使用旧的 "day_of_week" 和 "holiday_flg" 完全我们只需要绘制 data_info_df 到 merged_df .

    合并 也可以做这项工作,因为 data\u info\u df 的数据条目是唯一的。(不会创建重复项。)


    您也可以尝试使用 pandas.Series.map . 它的作用是

    使用输入对应关系映射序列值(可以是dict、序列或函数)

    # set "calendar_date" as the index such that 
    # mapping["day_of_week"] and mapping["holiday_flg"] will be two series
    # with date_info_df["calendar_date"] as their index.
    mapping = date_info_df.set_index("calendar_date")
    
    # this line is optional (depending on the layout of data.)
    merged_df.visit_date = pd.to_datetime(merged_df.visit_date)
    
    # do replacement here.
    merged_df["day_of_week"] = merged_df.visit_date.map(mapping["day_of_week"])
    merged_df["holiday_flg"] = merged_df.visit_date.map(mapping["holiday_flg"])
    

    笔记 merged_df.visit_date 最初是字符串类型。因此,我们使用

    merged_df.visit_date = pd.to_datetime(merged_df.visit_date)
    

    使其成为datetime。

    计时 date_info_df dataset 和 merged_df 由karlphillip提供。

    date_info_df = pd.read_csv("full_date_info_data.csv")
    merged_df = pd.read_csv("full_data.csv")   
    merged_df.visit_date = pd.to_datetime(merged_df.visit_date)
    date_info_df.calendar_date = pd.to_datetime(date_info_df.calendar_date)
    cols = ["day_of_week", "holiday_flg"]
    visit_date = pd.to_datetime(merged_df.visit_date)
    
    # merge method I proprose on the top.
    %timeit merged_df.merge(date_info_df[["calendar_date", "day_of_week", "holiday_flg"]], left_on="visit_date", right_on="calendar_date", how="left")
    511 ms ± 34.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    # HYRY's method without assigning it back
    %timeit merged_df[cols].combine_first(date_info_df.set_index("calendar_date").loc[visit_date, cols].set_index(merged_df.index))
    772 ms ± 11.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    # HYRY's method with assigning it back
    %timeit merged_df[cols] = merged_df[cols].combine_first(date_info_df.set_index("calendar_date").loc[visit_date, cols].set_index(merged_df.index))    
    258 ms ± 69.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    可以看出,HYRY的方法运行速度快了3倍 如果将结果分配回 合并的\u df . 这就是为什么我认为哈里的方法乍一看比我的快。我怀疑这是因为 combine_first . 我想HARY方法的速度将取决于它在 合并的\u df . 因此,当返回结果时,列变满;因此,在重新运行时,速度更快。

    的性能 合并 和 先合并\u 方法几乎是等效的。也许在某些情况下,一个比另一个快。应该让每个用户对其数据集进行一些测试。

    这两种方法之间需要注意的另一点是 合并 方法 假定的 中的每个日期 合并的\u df 包含在 data\u info\u df . 如果有一些日期包含在 合并的\u df 但不是 data\u info\u df ,它应该返回 NaN . 和 南安 可以覆盖 合并的\u df 最初包含值的! 这是什么时候 先合并\u 应首选方法。 参见MaxU在中的讨论 Pandas replace, multi column criteria