Объединение таблиц с SQL-подобным соединением с фильтром (Между) в левом соединении - PullRequest
0 голосов
/ 08 февраля 2019

Итак, у меня есть две таблицы, в которых я хочу сделать left join и отфильтровать строки, где мой столбец date из df1 находится между столбцами from и to из df2.

Обратите внимание на row 6, у которого нет ClockInDate, что в конечном итоге вызовет проблему.

df1 :

  Company Resource ClockInDate
0       A     ResA  2019-02-09
1       A     ResB  2019-02-09
2       A     ResC  2019-02-09
3       B     ResD  2019-02-09
4       B     ResE  2019-02-09
5       B     ResF  2019-02-09
6       B     ResG         NaT

df2 :

  Company Resource EffectiveFrom EffectiveTo
0       A     ResA    2018-01-01  2018-12-31
1       A     ResA    2019-01-01  2099-12-31
2       A     ResB    2018-01-01  2018-12-31
3       A     ResB    2019-01-01  2099-12-31
4       B     ResE    2018-01-01  2018-12-31
5       B     ResE    2019-01-01  2099-12-31
6       B     ResF    2018-01-01  2018-12-31
7       B     ResF    2019-01-01  2099-12-31
8       B     ResG    2018-01-01  2018-12-31
9       B     ResG    2019-01-01  2099-12-31

Я думал, что смогу сделать это с left merge в пандах и после этого применить фильтр.
Но это дает другой вывод.

Таким образом, в SQL вы можете включить этот фильтр в предложение ON следующим образом, но это не то же самое, что включение после фильтра в предложении WHERE:

       SELECT t1.company,
              t1.resource,
              t2.company,
              t2.resource,
              t1.ClockInDate,
              t2.EffectiveFrom,
              t2.EffectiveTo
       FROM table1 t1
       LEFT JOIN table2 t2 ON t1.resource = t2.resource
                            AND t1.company = t2.company
                            AND t1.ClockInDate BETWEEN t2.EffectiveFrom AND t2.EffectiveTo

Notice часть: AND t1.ClockInDate BETWEEN t2.EffectiveFrom AND t2.EffectiveTo
Примечание: в коде SQL df1 равно t1 и df2 равно t2

Вывод SQL (Это мой ожидаемый вывод):

    t1.Company  t1.Resource t1.ClockInDate  t2.EffectiveFrom    t2.EffectiveTo
0   A           ResA        2019-02-09      2019-01-01          2099-12-31
1   A           ResB        2019-02-09      2019-01-01          2099-12-31
2   A           ResC        NaT             NaT                 NaT
3   B           ResD        NaT             NaT                 NaT
4   B           ResE        2019-02-09      2019-01-01          2099-12-31
5   B           ResF        2019-02-09      2019-01-01          2099-12-31
6   B           ResG        NaT             NaT                 NaT

Так что я это мой код в Python:

Выход Python

df_merge = pd.merge(df1, df2, on=['Company', 'Resource'], how='left')
df_final = df_merge[df_merge.ClockInDate.between(df_merge.EffectiveFrom, df_merge.EffectiveTo) | df_merge.EffectiveFrom.isnull()]

#Output:

    Company Resource    ClockInDate EffectiveFrom   EffectiveTo
1   A       ResA        2019-02-09  2019-01-01      2099-12-31
3   A       ResB        2019-02-09  2019-01-01      2099-12-31
4   A       ResC        2019-02-09  NaT             NaT
5   B       ResD        2019-02-09  NaT             NaT
7   B       ResE        2019-02-09  2019-01-01      2099-12-31
9   B       ResF        2019-02-09  2019-01-01      2099-12-31

Обратите внимание, что последняя строка с ресурсом ResG не включена в вывод Python.

Код для копирования и вставки для воспроизведения DataFrames

df1 = pd.DataFrame({'Company':['A', 'A', 'A', 'B', 'B', 'B', 'B'],
                    'Resource':['ResA', 'ResB','ResC', 'ResD', 'ResE', 'ResF', 'ResG'],
                    'ClockInDate':['2019-02-09', '2019-02-09', '2019-02-09', '2019-02-09', '2019-02-09', '2019-02-09', '']})

df1['ClockInDate'] = pd.to_datetime(df1.ClockInDate)

df2 = pd.DataFrame({'Company':['A','A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B'],
                    'Resource':['ResA', 'ResA', 'ResB', 'ResB', 'ResE', 'ResE', 'ResF', 'ResF', 'ResG', 'ResG'],
                    'EffectiveFrom':['2018-01-01', '2019-01-01', '2018-01-01', '2019-01-01', '2018-01-01', '2019-01-01', '2018-01-01', '2019-01-01', '2018-01-01', '2019-01-01'],
                    'EffectiveTo':['2018-12-31', '2099-12-31', '2018-12-31', '2099-12-31', '2018-12-31', '2099-12-31', '2018-12-31', '2099-12-31', '2018-12-31', '2099-12-31']})

df2['EffectiveFrom'] = pd.to_datetime(df2.EffectiveFrom)
df2['EffectiveTo'] = pd.to_datetime(df2.EffectiveTo)

Ответы [ 2 ]

0 голосов
/ 03 апреля 2019

Итак, после работы над этим проектом я получил больше понимания.Я нашел решение, но надеялся на cleaner.Но это работает: мы можем объединить строки из исходного кадра данных, которые имеют ClockIndate.isnull:

df_merge = pd.merge(df1, df2, on=['Company', 'Resource'], how='left')

df_filter = df_merge[df_merge.ClockInDate.between(df_merge.EffectiveFrom, df_merge.EffectiveTo) | df_merge.EffectiveFrom.isnull()]

df_final = pd.concat([df_filter, df1[df1.ClockInDate.isnull()]], sort=True)

print(df_final)
  ClockInDate Company EffectiveFrom EffectiveTo Resource
1  2019-02-09       A    2019-01-01  2099-12-31     ResA
3  2019-02-09       A    2019-01-01  2099-12-31     ResB
4  2019-02-09       A           NaT         NaT     ResC
5  2019-02-09       B           NaT         NaT     ResD
7  2019-02-09       B    2019-01-01  2099-12-31     ResE
9  2019-02-09       B    2019-01-01  2099-12-31     ResF
6         NaT       B           NaT         NaT     ResG
0 голосов
/ 08 февраля 2019

sql эквивалент с где:

SELECT t1.company,
        t1.resource,
        t2.company,
        t2.resource,
        t1.ClockInDate,
        t2.EffectiveFrom,
        t2.EffectiveTo
FROM table1 t1
LEFT JOIN table2 t2 ON t1.resource = t2.resource
                    AND t1.company = t2.company
WHERE t1.ClockInDate IS NULL --no ClockInDate to check
    OR t2.company IS NULL AND t2.resource IS NULL --not rows in t2 for t1
    OR t1.ClockInDate BETWEEN t2.EffectiveFrom AND t2.EffectiveTo --ClockInDate exists, rows in t2 exist, we can now check ClockInDate to be between t2.EffectiveFrom AND t2.EffectiveTo

, что будет переводиться на Python как:

df_merge = pd.merge(df1, df2, on=['Company', 'Resource'], how='left')
df_final = df_merge[df_merge.ClockInDate.isnull() | df_merge.ClockInDate.between(df_merge.EffectiveFrom, df_merge.EffectiveTo) | df_merge.EffectiveFrom.isnull()]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...