Добавить столбец из одного df в другой на основе столбца даты в обеих dfs — pandas

#python-3.x #pandas #dataframe #datetime

#python-3.x #pandas #фрейм данных #дата и время

Вопрос:

У меня есть два df, как показано ниже.

df1:

 Date                t_factor     
2020-02-01             5             
2020-02-02             23              
2020-02-03             14           
2020-02-04             23
2020-02-05             23  
2020-02-06             23          
2020-02-07             30            
2020-02-08             29            
2020-02-09             100
2020-02-10             38
2020-02-11             38               
2020-02-12             38                    
2020-02-13             70           
2020-02-14             70 
2020-02-15             38               
2020-02-16             38                    
2020-02-17             70           
2020-02-18             70 
2020-02-19             38               
2020-02-20             38                    
2020-02-21             70           
2020-02-22             70 
2020-02-23             38               
2020-02-24             38                    
2020-02-25             70           
2020-02-26             70 
2020-02-27             70 
  

df2:

 From                to                   plan          score
2020-02-03          2020-02-05           start         20
2020-02-07          2020-02-08           foundation    25
2020-02-10          2020-02-12           learn         10
2020-02-14          2020-02-16           practice      20
2020-02-15          2020-02-21           exam          30
2020-02-20          2020-02-23           test          10
  

Исходя из вышесказанного, я хотел бы добавить plan столбец в df1 на основе значения From и to даты в df2 и Date значения в df1.

Ожидаемый результат:

output_df

 Date                t_factor        plan
2020-02-01             5            NaN
2020-02-02             23           NaN   
2020-02-03             14           start          
2020-02-04             23           start
2020-02-05             23           start  
2020-02-06             23           NaN
2020-02-07             30           foundation               
2020-02-08             29           foundation        
2020-02-09             100          NaN
2020-02-10             38           learn
2020-02-11             38           learn              
2020-02-12             38           learn                   
2020-02-13             70           NaN
2020-02-14             70           practice
2020-02-15             38           NaN              
2020-02-16             38           NaN                    
2020-02-17             70           exam      
2020-02-18             70           exam
2020-02-19             38           exam   
2020-02-20             38           NaN                 
2020-02-21             70           NaN         
2020-02-22             70           test
2020-02-23             38           test             
2020-02-24             38           NaN        
2020-02-25             70           NaN
2020-02-26             70           NaN
2020-02-27             70           NaN
  

Примечание:

Если есть какая-либо перекрывающаяся дата, то сохраните plan как NaN для этой даты.

Пример:

2020-02-14 to 2020-02-16 plan — это practice .

А 2020-02-15 to 2020-02-21 plan — это exam .

Таким образом, происходит перекрытие на 2020-02-15 и 2020-02-16 .

Следовательно, plan должно быть NaN для этого диапазона дат.

Я хотел бы реализовать функцию, показанную ниже.

 def (df1, df2)
    return output_df
  

Ответ №1:

Используйте: (Это решение, если From и to даты в dataframe df2 перекрываются, и нам нужно выбрать значения из столбца plan относительно самой ранней возможной даты)

 d1 = df1.sort_values('Date')
d2 = df2.sort_values('From')


df = pd.mer&e_asof(d1, d2[['From', 'plan']], left_on='Date', ri&ht_on='From')
df = pd.mer&e_asof(df, d2[['to', 'plan']],   left_on='Date', ri&ht_on='to',
                   direction='forward', suffixes=['', '_r']).drop(['From', 'to'], 1)

df['plan'] = df['plan'].mask(df['plan'].ne(df.pop('plan_r')))
  

Подробные сведения:

Используйте pd.mer&e_asof для выполнения asof mer&e для фреймов данных d1 и d2 для соответствующих столбцов Date и From с default direction='backward' для создания нового объединенного фрейма данных df , снова используйте pd.mer&e_asof для asof mer&e фреймов данных df и d2 для соответствующих столбцов Date и to с direction='forward' .

 print(df)

         Date  t_factor        plan      plan_r
0  2020-02-01         5         NaN       start
1  2020-02-02        23         NaN       start
2  2020-02-03        14       start       start
3  2020-02-04        23       start       start
4  2020-02-05        23       start       start
5  2020-02-06        23       start  foundation
6  2020-02-07        30  foundation  foundation
7  2020-02-08        29  foundation  foundation
8  2020-02-09       100  foundation       learn
9  2020-02-10        38       learn       learn
10 2020-02-11        38       learn       learn
11 2020-02-12        38       learn       learn
12 2020-02-13        70       learn    practice
13 2020-02-14        70    practice    practice
14 2020-02-15        38        exam    practice
15 2020-02-16        38        exam    practice
16 2020-02-17        70        exam        exam
17 2020-02-18        70        exam        exam
18 2020-02-19        38        exam        exam
19 2020-02-20        38        test        exam
20 2020-02-21        70        test        exam
21 2020-02-22        70        test        test
22 2020-02-23        38        test        test
23 2020-02-24        38        test         NaN
24 2020-02-25        70        test         NaN
25 2020-02-26        70        test         NaN
26 2020-02-27        70        test         NaN
  

Используйте Series.ne Series.mask для маскировки значений в столбце, plan где plan не равно plan_r .

 print(df)

         Date  t_factor        plan
0  2020-02-01         5         NaN
1  2020-02-02        23         NaN
2  2020-02-03        14       start
3  2020-02-04        23       start
4  2020-02-05        23       start
5  2020-02-06        23         NaN
6  2020-02-07        30  foundation
7  2020-02-08        29  foundation
8  2020-02-09       100         NaN
9  2020-02-10        38       learn
10 2020-02-11        38       learn
11 2020-02-12        38       learn
12 2020-02-13        70         NaN
13 2020-02-14        70    practice
14 2020-02-15        38         NaN
15 2020-02-16        38         NaN
16 2020-02-17        70        exam
17 2020-02-18        70        exam
18 2020-02-19        38        exam
19 2020-02-20        38         NaN
20 2020-02-21        70         NaN
21 2020-02-22        70        test
22 2020-02-23        38        test
23 2020-02-24        38         NaN
24 2020-02-25        70         NaN
25 2020-02-26        70         NaN
26 2020-02-27        70         NaN
  

Ответ №2:

С помощью pd.to_datetime преобразовать столбцы, подобные дате, в ряды дат и времени pandas:

 df1['Date'] = pd.to_datetime(df1['Date'])
df2[['From', 'to']] = df2[['From', 'to']].apply(pd.to_datetime)
  

Создайте a pd.IntervalIndex из столбцов From и to of df2 , затем используйте Series.map в столбце Date of df1 , чтобы сопоставить его со столбцом plan from df2 (после установки idx ):

 idx = pd.IntervalIndex.from_arrays(df2['From'], df2['to'], closed='both')
df1['plan'] = df1['Date'].map(df2.set_index(idx)['plan'])
  

Результат:

          Date  t_factor        plan
0  2020-02-01         5         NaN
1  2020-02-02        23         NaN
2  2020-02-03        14       start
3  2020-02-04        23       start
4  2020-02-05        23       start
5  2020-02-06        23         NaN
6  2020-02-07        30  foundation
7  2020-02-08        29  foundation
8  2020-02-09       100         NaN
9  2020-02-10        38       learn
10 2020-02-11        38       learn
11 2020-02-12        38       learn
12 2020-02-13        70         NaN
13 2020-02-14        70    practice
14 2020-02-15        38    practice
15 2020-02-16        38    practice
16 2020-02-17        70        exam
17 2020-02-18        70        exam
18 2020-02-19        38         NaN
19 2020-02-20        38        test
20 2020-02-21        70        test
21 2020-02-22        70        test
22 2020-02-23        38        test
23 2020-02-24        38         NaN
24 2020-02-25        70         NaN
25 2020-02-26        70         NaN
26 2020-02-27        70         NaN