86589


以下のような、マルチインデックスなデータフレーム（価格データ）と、  
期間が含まれるデータフレーム（対象期間データ）から作成したスライスを利用して、  
対象の期間の行を抽出したいです。  
ループを利用すれば何とか出来たのですが、  
（パフォーマンス向上のために、）ループやapplyなしで、  
記述することはできますでしょうか？

[code]

    import io
    import pandas as pd
    
    text_price = '''
    area,date,price
    東京,2022-02-01,100
    東京,2022-02-03,200
    東京,2022-02-05,300
    埼玉,2022-02-11,400
    埼玉,2022-02-13,500
    埼玉,2022-02-15,600
    '''
    df_price = pd.read_csv(io.StringIO(text_price), index_col=['area', 'date'])
    df_price = df_price.sort_index()
    print('- 価格データ(入力)')
    display(df_price)
    
    text_history = '''
    area,start_date,end_date
    東京,2022-02-02,2022-02-04
    埼玉,2022-02-12,2022-02-16
    沖縄,2022-02-01,2022-02-20
    '''
    df_history = pd.read_csv(io.StringIO(text_history))
    print('- 対象期間データ(入力)')
    display(df_history)
    
    print('- 期待する結果(出力)')
    list_out = []
    for row in df_history.itertuples():
        if row.area in df_price.index.get_level_values(0):
            series = df_price.loc[([row.area], slice(row.start_date, row.end_date)), :]
            list_out.append(series)
    
    df_out = pd.concat(list_out, axis='index')
    display(df_out)
    
[/code]

[![画像の説明をここに入力](https://i.stack.imgur.com/93VDd.png)](https://i.stack.imgur.com/93VDd.png)


CC BY-SA 4.0
2022-02-25T15:40:05.443
86575
2022-02-28T05:45:11.387
35267
question
python pandas
マルチインデックスなデータフレームから、ループなしで、行を抽出する方法


> ループやapplyなしで、記述することはできますでしょうか？

`df_history`
において、期間(`start_date`〜`end_date`)に重なりが無いのであれば可能です。ですが、今回のデータでは重複があるので、以下の方法では
`apply` を使うしかありません。

[code]

    # 日時を datetime として読み込む
    df_price = pd.read_csv(io.StringIO(text_price), index_col=['area', 'date'], parse_dates=['date'])
    df_history = pd.read_csv(io.StringIO(text_history), parse_dates=['start_date', 'end_date'])
    
    # IntervalIndex を作成
    idx = pd.IntervalIndex.from_arrays(df_history['start_date'], df_history['end_date'], closed='both')
    new_idx = pd.date_range(df_history['start_date'].min(), df_history['end_date'].max(), freq='D')
    
    # 期間の重複がある場合、reindex() で duplicate label エラーが発生する
    #idx = df_history.set_axis(idx).reindex(new_idx).dropna().index
    idx = df_history.set_axis(idx).groupby('area').apply(lambda x: x.reindex(new_idx)).dropna().index
    
    # Indexing
    result = df_price[df_price.index.isin(idx)].sort_index(level='date')
    print(result)
    
    #
                     price
    area date             
    東京 2022-02-03    200
    埼玉 2022-02-13    500
         2022-02-15    600
    
[/code]

##### 別解

kirara0048 さんの回答を参考にしました。

[code]

    result = (
      df_price.join(df_history.set_index('area'))
              .query('start_date <= date <= end_date')
              .sort_index(level='date')[['price']])
    
    print(result)
    
    #
                     price
    area date             
    東京 2022-02-03    200
    埼玉 2022-02-13    500
         2022-02-15    600
    
[/code]


CC BY-SA 4.0
2022-02-26T13:46:08.150
86589
2022-02-28T05:45:11.387
2022-02-28T05:45:11.387
47127
47127
86575
answer


以下でどうでしょうか。  
単純にマージして、日付の比較を行っています。

[code]

    df_price = pd.read_csv(io.StringIO(text_price), index_col=["area", "date"], parse_dates=["date"])
    df_history = pd.read_csv(io.StringIO(text_history), parse_dates=["start_date", "end_date"])
    
    tmp_df = df_price.reset_index().merge(df_history, on="area")
    out = df_price.loc[((tmp_df["start_date"] <= tmp_df["date"])
                        & (tmp_df["date"] < tmp_df["end_date"])).tolist()]
    print(out)
    #                  price
    # area date             
    # 東京 2022-02-03    200
    # 埼玉 2022-02-13    500
    #      2022-02-15    600
    
[/code]


CC BY-SA 4.0
2022-02-28T04:38:06.727
86609
2022-02-28T04:38:06.727
37167
86575
answer