2015-07-20 2 views
2

Предположим, что у меня есть следующая таблицаСложные слияния на основе даты начала и окончания панд

import pandas as pd, datetime 

table = [[datetime.datetime(2015, 1, 1), 1], 
     [datetime.datetime(2015, 1, 27), 1], 
     [datetime.datetime(2015, 1, 31), 1], 
     [datetime.datetime(2015, 2, 1), 1], 
     [datetime.datetime(2015, 2, 3), 1], 
     [datetime.datetime(2015, 2, 15), 1], 
     [datetime.datetime(2015, 2, 28), 1], 
     [datetime.datetime(2015, 3, 1), 1], 
     [datetime.datetime(2015, 3, 17), 1], 
     [datetime.datetime(2015, 3, 28), 1], 
     [datetime.datetime(2015, 4, 12), 1], 
     [datetime.datetime(2015, 4, 28), 1]] 

df1 = pd.DataFrame(table, columns=['Date', 'Id']) 
df2 = df1.copy() 
df2['Id'] = 2 
df = df1.append(df2) 

table2 = [[1, datetime.datetime(1900, 1, 1), datetime.datetime(2015, 2, 28), 2, 20], 
      [1, datetime.datetime(2015, 3, 1), datetime.datetime(3000, 1, 1), 4, 25], 
      [2, datetime.datetime(1900, 1, 1), datetime.datetime(3000, 1, 1), 2, 20]] 

df3 = pd.DataFrame(table2, columns=['Id', 'Start', 'End', 'Fix', 'Performance']) 

редактировать В df3, таблица будет сгруппирована по Id. То есть первые две строки действительны для Id = 1, а последние для Id = 2.

Вопрос сейчас; есть способ добавить Fix и Performance в виде столбцов к df таким образом, чтобы элементы соответствующих столбцов находились в строках, где Start и End действительны, как определено в Date? Это означает, что моя таблица будет выглядеть

  Date Id Fix Performance 
0 2015-01-01 1 2  20 
1 2015-01-27 1 2  20 
2 2015-01-31 1 2  20 
3 2015-02-01 1 2  20 
4 2015-02-03 1 2  20 
5 2015-02-15 1 2  20 
6 2015-02-28 1 2  20 
7 2015-03-01 1 4  25 
8 2015-03-17 1 4  25 
9 2015-03-28 1 4  25 
10 2015-04-12 1 4  25 
11 2015-04-28 1 4  25 
0 2015-01-01 2 2  20 
1 2015-01-27 2 2  20 
2 2015-01-31 2 2  20 
3 2015-02-01 2 2  20 
4 2015-02-03 2 2  20 
5 2015-02-15 2 2  20 
6 2015-02-28 2 2  20 
7 2015-03-01 2 2  20 
8 2015-03-17 2 2  20 
9 2015-03-28 2 2  20 
10 2015-04-12 2 2  20 
11 2015-04-28 2 2  20 

Спасибо, Tingis

+0

Как вы выбираете Fix и Performance, когда дата падает через два интервала, определенные рядами в df3? (например, взять данные 2001-01-01, соответствие линии 1 и 3) –

+0

Хм, не совсем уверен, что я следую вашему вопросу. Но 'Start' в' df3' отмечает, что дата, когда 'Fix' и' Performance' действительны, а 'End' в' df3' отмечает последнюю дату, когда они действительны. Надеюсь, что это ответ на ваш вопрос! – Tingiskhan

+1

@Tingiskhan три '[Start, End]' интервала в 'df3' перекрываются, есть двусмысленность. – galath

ответ

1

Вот один подход, при котором вы apply функции по строкам для создания двух разыскиваемых столбцов:

import pandas as pd 
import numpy as np 

def search(x): 
    df_ = df3[df3.Id==x['Id']] 
    mask = np.logical_and(df_.Start<=x['Date'], df_.End>=x['Date']) 
    return pd.Series([df_.loc[mask].Fix.tolist()[0], df_.loc[mask].Performance.tolist()[0]]) 

df[['Fix','Performance']] = df.apply(search, axis=1) 

In [423]: df 
Out[423]: 
     Date Id Fix Performance 
0 2015-01-01 1 2   20 
1 2015-01-27 1 2   20 
2 2015-01-31 1 2   20 
3 2015-02-01 1 2   20 
4 2015-02-03 1 2   20 
5 2015-02-15 1 2   20 
6 2015-02-28 1 2   20 
7 2015-03-01 1 4   25 
8 2015-03-17 1 4   25 
9 2015-03-28 1 4   25 
10 2015-04-12 1 4   25 
11 2015-04-28 1 4   25 
0 2015-01-01 2 2   20 
1 2015-01-27 2 2   20 
2 2015-01-31 2 2   20 
3 2015-02-01 2 2   20 
4 2015-02-03 2 2   20 
5 2015-02-15 2 2   20 
6 2015-02-28 2 2   20 
7 2015-03-01 2 2   20 
8 2015-03-17 2 2   20 
9 2015-03-28 2 2   20 
10 2015-04-12 2 2   20 
11 2015-04-28 2 2   20 
+0

Спасибо @ColonelBeauvel! Это было то, что я искал в начале! – Tingiskhan

1

Вы можете сначала сделайте SQL-стиль outer merge, а затем удалите эти несогласованные записи с Date выпадая из Start-to-End Интервал.

import pandas as pd 
import numpy as np 
import datetime 

# your data 
# ======================================================== 
table = [[datetime.datetime(2015, 1, 1), 1], 
     [datetime.datetime(2015, 1, 27), 1], 
     [datetime.datetime(2015, 1, 31), 1], 
     [datetime.datetime(2015, 2, 1), 1], 
     [datetime.datetime(2015, 2, 3), 1], 
     [datetime.datetime(2015, 2, 15), 1], 
     [datetime.datetime(2015, 2, 28), 1], 
     [datetime.datetime(2015, 3, 1), 1], 
     [datetime.datetime(2015, 3, 17), 1], 
     [datetime.datetime(2015, 3, 28), 1], 
     [datetime.datetime(2015, 4, 12), 1], 
     [datetime.datetime(2015, 4, 28), 1]] 

df1 = pd.DataFrame(table, columns=['Date', 'Id']) 
df2 = df1.copy() 
df2['Id'] = 2 
df = df1.append(df2) 

print(df) 


     Date Id 
0 2015-01-01 1 
1 2015-01-27 1 
2 2015-01-31 1 
3 2015-02-01 1 
4 2015-02-03 1 
5 2015-02-15 1 
6 2015-02-28 1 
7 2015-03-01 1 
..  ... .. 
4 2015-02-03 2 
5 2015-02-15 2 
6 2015-02-28 2 
7 2015-03-01 2 
8 2015-03-17 2 
9 2015-03-28 2 
10 2015-04-12 2 
11 2015-04-28 2 



table2 = [[1, datetime.datetime(1900, 1, 1), datetime.datetime(2015, 2, 28), 2, 20], 
      [1, datetime.datetime(2015, 3, 1), datetime.datetime(2030, 1, 1), 4, 25], 
      [2, datetime.datetime(1900, 1, 1), datetime.datetime(2030, 1, 1), 2, 20]] 
df3 = pd.DataFrame(table2, columns=['Id', 'Start', 'End', 'Fix', 'Performance']) 

print(df3) 

    Id  Start  End Fix Performance 
0 1 1900-01-01 2015-02-28 2   20 
1 1 2015-03-01 2030-01-01 4   25 
2 2 1900-01-01 2030-01-01 2   20 


# processing 
# ============================================= 
df_temp = pd.merge(df, df3, on='Id', how='outer') 
result = df_temp[(df_temp.Date >= df_temp.Start) & (df_temp.Date <= df_temp.End)].reset_index(drop=True) 

     Date Id  Start  End Fix Performance 
0 2015-01-01 1 1900-01-01 2015-02-28 2   20 
1 2015-01-27 1 1900-01-01 2015-02-28 2   20 
2 2015-01-31 1 1900-01-01 2015-02-28 2   20 
3 2015-02-01 1 1900-01-01 2015-02-28 2   20 
4 2015-02-03 1 1900-01-01 2015-02-28 2   20 
5 2015-02-15 1 1900-01-01 2015-02-28 2   20 
6 2015-02-28 1 1900-01-01 2015-02-28 2   20 
7 2015-03-01 1 2015-03-01 2030-01-01 4   25 
8 2015-03-17 1 2015-03-01 2030-01-01 4   25 
9 2015-03-28 1 2015-03-01 2030-01-01 4   25 
10 2015-04-12 1 2015-03-01 2030-01-01 4   25 
11 2015-04-28 1 2015-03-01 2030-01-01 4   25 
12 2015-01-01 2 1900-01-01 2030-01-01 2   20 
13 2015-01-27 2 1900-01-01 2030-01-01 2   20 
14 2015-01-31 2 1900-01-01 2030-01-01 2   20 
15 2015-02-01 2 1900-01-01 2030-01-01 2   20 
16 2015-02-03 2 1900-01-01 2030-01-01 2   20 
17 2015-02-15 2 1900-01-01 2030-01-01 2   20 
18 2015-02-28 2 1900-01-01 2030-01-01 2   20 
19 2015-03-01 2 1900-01-01 2030-01-01 2   20 
20 2015-03-17 2 1900-01-01 2030-01-01 2   20 
21 2015-03-28 2 1900-01-01 2030-01-01 2   20 
22 2015-04-12 2 1900-01-01 2030-01-01 2   20 
23 2015-04-28 2 1900-01-01 2030-01-01 2   20 

# if you don't like Start and End columns in the final table, just drop them 
result.drop(['Start', 'End'], axis=1, inplace=True) 
+0

Спасибо @JianxunLi! Мне никогда не приходилось использовать внешнее слияние. – Tingiskhan

+0

Однако кажется, что я не могу использовать годы, превышающие 3000. Итак, я пойду с ответом полковника на этот раз! Но большое вам спасибо за помощь! – Tingiskhan

+0

@Tingiskhan Нет проблем. :-) Проблема с годом более 3000 - это то, что pandas конвертирует datetime в 'numpy.datetime64' на частоте наносекунды, а при использовании 64-битной' int' для хранения этой информации ее диапазон находится между 1970 и 2262. См. Http: //docs.scipy.org/doc/numpy/reference/arrays.datetime.html#datetime-units для получения дополнительной информации. Чтобы исправить это, вы можете попробовать преобразовать тип в 'datetime64' со второй частотой. –

Смежные вопросы