【问题标题】:Check if a list of dates falls between a list of date ranges检查日期列表是否介于日期范围列表之间
【发布时间】:2021-06-04 21:40:56
【问题描述】:

我有两个从大型酒店数据库中提取的数据框:

  1. 客户购物历史数据框 (df_hist)
    customer_id   item   date     
     1234         milk   2012-04-20       
     1234         sugar  2012-05-01      
     5678         salt   2017-07-15    
     5678         water  2017-08-10    
  1. 客户访问历史数据框 (df_visit)
    customer_id   start          end         visit
     1234         2012-04-06     2012-04-25  1    
     5678         2017-07-10     2017-07-20  5
     5678         2017-08-05     2017-08-11  6    
   

我正在尝试找出购买历史中每件商品的访问次数

  1. 结果(df_result):
    customer_id   item   date         visit
     1234         milk   2012-04-20   1  
     1234         sugar  2012-05-01   null 
     5678         salt   2017-07-15   5 
     5678         water  2017-08-10   6 

我尝试使用多个 for 循环,但由于 df_visit 有近 600 万行对应于大约 15,000 个唯一客户,因此它不可扩展。解决此问题的更有效方法是什么?

【问题讨论】:

  • 对于df_visit 中的任何特定customer_idstartend 日期值是否有重叠的可能性?
  • 不,日期范围不会在单个客户中重叠

标签: python python-3.x pandas dataframe datetime


【解决方案1】:

由于数据集很大,您无法直接执行合并操作,因为它会生成大量不必要的行。这是解决问题的一种方法,使用IntervalIndexdf_visit 中唯一的customer_id 标识的每个组创建一个映射系列:

def create_map():
    mappings = []
    for _, g in df_visit.groupby('customer_id', sort=False):
        i = pd.IntervalIndex.from_arrays(g['start'], g['end'])
        mappings.append(g.set_index(['customer_id', i])['visit'])
    return pd.concat(mappings)

out = df_hist.set_index(['customer_id', 'date'])
out = out.assign(visit=out.index.map(create_map())).reset_index()

df_visit 已经在customer_id 上排序时可以使用的替代方法:

def create_intervals():
    intervals = []
    for _, g in df_visit.groupby('customer_id', sort=False):
        intervals.append(pd.IntervalIndex.from_arrays(g['start'], g['end']))
    return intervals

i = create_intervals()
mapping = df_visit.set_index(['customer_id', np.hstack(i)])['visit']
df_hist['visit'] = df_hist.set_index(['customer_id', 'date']).index.map(mapping)

   customer_id       date   item  visit
0         1234 2012-04-20   milk    1.0
1         1234 2012-05-01  sugar    NaN
2         5678 2017-07-15   salt    5.0
3         5678 2017-08-10  water    6.0

【讨论】:

    【解决方案2】:

    这是一种方法:

    import io
    d1 = io.StringIO("""
        customer_id   item   date     
         1234         milk   2012-04-20       
         1234         sugar  2012-05-01      
         5678         salt   2017-07-15    
         5678         water  2017-08-10    
    """)
    
    d2 = io.StringIO("""
      customer_id   start          end         visit
         1234         2012-04-06     2012-04-25  1    
         5678         2017-07-10     2017-07-20  5
         5678         2017-08-05     2017-08-11  6
    """)
    
    import pandas as pd
    
    df1 = pd.read_csv(d1, sep='\s+', parse_dates=['date'])
    df2 = pd.read_csv(d2, sep='\s+', parse_dates=['start', 'end'])
    
    merged = pd.merge_asof(df1, df2, left_on=['date'], right_on=['start'], by='customer_id', direction='backward')
    
    mask_dates = (merged['end'] >= merged['date']) & (merged['date']>=merged['start'])
    
    merged['visit'] = merged.loc[mask_dates, 'visit']
    
    merged
    

    【讨论】:

      【解决方案3】:

      您可以对输入数据进行预处理,以减少每次迭代中遍历的项目集。由于您知道两个输入历史数据集中的客户 ID,这将是组织数据的自然选择。

      • 第一步:查看客户访问历史记录并根据客户 ID 创建历史记录。
      • 第二步:对于每个购物条目,查找客户的访问历史记录并在这个更小的子集中进行匹配。

      在这两个步骤中都可以进行许多实施优化。比如说,在第一步之后如何保留已处理的列表(复制记录或仅在大表中保留条目的索引等)。对于第二步,如果需要进一步加速,您可以将访问组织成尝试等。

      但即使在for 循环的最简单情况下,只需分离每个客户的历史记录,您将获得 10000 的加速(以初始历史处理为代价)。

      【讨论】:

        猜你喜欢
        • 2021-08-23
        • 1970-01-01
        • 1970-01-01
        • 2017-12-20
        • 2020-07-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多