Сравнение двух DataFrames и L oop через них (чтобы проверить условие) - PullRequest
1 голос
/ 05 марта 2020

Я пытаюсь объединить два DataFrames в зависимости от условия.

Условие

if df1.Year == df2.Year & 
df1.Date >= df2.BeginDate or df1.Date <= df2.EndDate &
df1.ID == df2.ID 
#if the condition is True, I would love to add an extra column (binary) to df1, something like
#df1.condition = Yes or No.

Мои данные выглядят так:

df1: 

Year     Week     ID   Date
2020      1      123   2020-01-01 00:00:00
2020      1      345   2020-01-01 00:00:00
2020      2      123   2020-01-07 00:00:00
2020      1      123   2020-01-01 00:00:00


df2: 

Year    BeginDate               EndDate                 ID
2020    2020-01-01  00:00:00    2020-01-02  00:00:00    123
2020    2020-01-01  00:00:00    2020-01-02  00:00:00    123
2020    2020-01-01  00:00:00    2020-01-02  00:00:00    978
2020    2020-09-21  00:00:00    2020-01-02  00:00:00    978


end_df: #Expected output 

Year     Week     ID   Condition
2020      1      123     True  #Year is matching, week1 is between the dates, ID is matching too 
2019      1      345     False #Year is not matching
2020      2      187     False # ID is not matching 
2020      1      123     True  # Same as first row. 

Я решил решить эту проблему, зациклившись на двух кадрах данных:

for row in df1.iterrrows(): 
    for row2 in df2.iterrows(): 
         if row['Year'] == row2['Year2']:
              if row['ID] == row2['ID']: 
                  .....
                  .....
                   row['Condition'] = True 
         else: 
            row['Condition'] = False 

Однако ... это приводит к ошибке после ошибки.

Очень надеюсь, что вы, ребята, будете решать эту проблему. Спасибо заранее!

ОБНОВЛЕНИЕ 1

Я создал oop. Однако это l oop занимает много времени (и я не уверен, как добавить значение в новый столбец).

Обратите внимание, что в df1 я создал столбец «Дата» (в том же формате, что и начало и конец из df2).

Введите ключ: Как я могу добавить значение True (в конце l oop ..) к моему df1 (в дополнительном столбце)?

for index, row in df1.interrows(): 
      row['Year'] = str(row['Year'])

      for index1, row1 in df2.iterrows():
          row1['Year'] = str(row1['Year'])


          if row['Year'] == row1['Year']:
                  row['ID'] = str(row['ID']) 
                  row1['ID'] = str(row1['ID']) 


                  if row['ID] == row1['ID']: 

                        if row['Date'] >= row1['BeginDate'] and row['Date'] <= row1['Enddate']:
                              print("I would like to add this YES to df1 in an extra column")

Редактировать 2

Попытка решения @davidbilla: похоже, что столбец 'condition' работает не очень хорошо. Как видите, он совпадает даже при df1.Year! = Df2.Year. Обратите внимание, что df2 сортируется на основе идентификатора (поэтому все одинаковые уникальные номера должны быть там

It looks like the 'condition' column is not doing well. As you can see, it match even while df1.Year != df2.Year. Note that df2 is sorted based on ID (so all the same unique numbers should be there

Ответы [ 2 ]

4 голосов
/ 05 марта 2020

Полагаю, вы ожидаете чего-то подобного - если вы пытаетесь сопоставить строку данных по строке (т. Е. Сравнить строку 1 из df1 с строкой из 1 из df2):

df1['condition'] = np.where((df1['Year']==df2['Year'])&(df1['ID']==df2['ID'])&((df1['Date']>=df2['BeginDate'])or(df1['Date']<=df2['EndDate'])), True, False)

np.where принимает условия как первый параметр, второй параметр будет значением, если условие выполнено, 3-й параметр является значением, если условие не выполнено.

РЕДАКТИРОВАТЬ 1: На основе вашего образца набора данных

df1 = pd.DataFrame([[2020,1,123],[2020,1,345],[2020,2,123],[2020,1,123]],
                   columns=['Year','Week','ID'])
df2 = pd.DataFrame([[2020,'2020-01-01  00:00:00','2020-01-02  00:00:00',123],
                    [2020,'2020-01-01  00:00:00','2020-01-02  00:00:00',123],
                    [2020,'2020-01-01  00:00:00','2020-01-02  00:00:00',978],
                    [2020,'2020-09-21  00:00:00','2020-01-02  00:00:00',978]],
                   columns=['Year','BeginDate','EndDate','ID'])
df2['BeginDate'] = pd.to_datetime(df2['BeginDate'])
df2['EndDate'] = pd.to_datetime(df2['EndDate'])
df1['condition'] = np.where((df1['Year']==df2['Year'])&(df1['ID']==df2['ID']),True, False)
# &((df1['Date']>=df2['BeginDate'])or(df1['Date']<=df2['EndDate'])) - removed this condition as the df has no Date field
print(df1)

Вывод:

   Year  Date   ID  condition
0  2020     1  123       True
1  2020     1  345      False
2  2020     2  123      False
3  2020     1  123      False

РЕДАКТИРОВАНИЕ 2: Чтобы сравнить одну строку в df1 со всеми строками в df2

df1['condition'] = (df1['Year'].isin(df2['Year']))&(df1['ID'].isin(df2['ID']))

Это займет df1['Year'] и сравнивает его со всеми значениями df2['Year'].

На основе образца набора данных:

df1:

   Year       Date   ID  
0  2020 2020-01-01  123  
1  2020 2020-01-01  345  
2  2020 2020-10-01  123  
3  2020 2020-11-13  123  

df2:

   Year  BeginDate    EndDate   ID
0  2020 2020-01-01 2020-02-01  123
1  2020 2020-01-01 2020-01-02  123
2  2020 2020-03-01 2020-05-01  978
3  2020 2020-09-21 2020-10-01  978

Изменение кода:

date_range = list(zip(df2['BeginDate'],df2['EndDate']))

def check_date(date):
    for (s,e) in date_range:
        if date>=s and date<=e:
            return True
    return False

df1['condition'] = (df1['Year'].isin(df2['Year']))&(df1['ID'].isin(df2['ID']))
df1['date_compare'] = df1['Date'].apply(lambda x: check_date(x)) # you can directly store this in df1['condition']. I just wanted to print the values so have used a new field
df1['condition'] = (df1['condition']==True)&(df1['date_compare']==True)

Вывод:

   Year       Date   ID  condition  date_compare
0  2020 2020-01-01  123       True          True    # Year match, ID match and Date is within the range of df2 row 1
1  2020 2020-01-01  345      False          True    # Year match, ID no match
2  2020 2020-10-01  123       True          True    # Year match, ID match, Date is within range of df2 row 4
3  2020 2020-11-13  123      False         False    # Year match, ID match, but Date is not in range of any row in df2

РЕДАКТИРОВАТЬ 3: На основе обновленного вопроса (Ранее я думал, что все в порядке, если 3 значения год, идентификатор и дата соответствуют df2 в любой из строк, которые не находятся в одной строке). Я думаю, что теперь я лучше понял ваше требование.

df2['BeginDate'] = pd.to_datetime(df2['BeginDate'])
df2['EndDate'] = pd.to_datetime(df2['EndDate'])
df1['Date'] = pd.to_datetime(df1['Date'])

df1['condition'] = False
for idx1, row1 in df1.iterrows():
    match = False
    for idx2, row2 in df2.iterrows():
        if (row1['Year']==row2['Year']) & \
                (row1['ID']==row2['ID']) & \
                (row1['Date']>=row2['BeginDate']) & \
                (row1['Date']<=row2['EndDate']):
            match = True
    df1.at[idx1, 'condition'] = match

Вывод - Установите 1:

DF1:

   Year       Date   ID
0  2020 2020-01-01  123
1  2020 2020-01-01  123
2  2020 2020-01-01  345
3  2020 2020-01-10  123
4  2020 2020-11-13  123

DF2:

   Year  BeginDate    EndDate   ID
0  2020 2020-01-15 2020-02-01  123
1  2020 2020-01-01 2020-01-02  123
2  2020 2020-03-01 2020-05-01  978
3  2020 2020-09-21 2020-10-01  978

Результат DF1:

   Year       Date   ID  condition
0  2020 2020-01-01  123       True
1  2020 2020-01-01  123       True
2  2020 2020-01-01  345      False
3  2020 2020-01-10  123      False
4  2020 2020-11-13  123      False

Выход - Набор 2: DF1:

   Year       Date        ID
0  2019 2019-01-01   s904112
1  2019 2019-01-01   s911243
2  2019 2019-01-01   s917131
3  2019 2019-01-01  sp986214
4  2019 2019-01-01   s510006
5  2020 2020-01-10   s540006

DF2:

   Year  BeginDate    EndDate       ID
0  2020 2020-01-27 2020-09-02  s904112
1  2020 2020-01-27 2020-09-02  s904112
2  2020 2020-01-03 2020-03-15  s904112
3  2020 2020-04-15 2020-01-05  s904112
4  2020 2020-01-05 2020-05-15  s540006
5  2019 2019-01-05 2019-05-15  s904112

DF1 Результат:

   Year       Date        ID  condition
0  2019 2019-01-01   s904112      False
1  2019 2019-01-01   s911243      False
2  2019 2019-01-01   s917131      False
3  2019 2019-01-01  sp986214      False
4  2019 2019-01-01   s510006      False
5  2020 2020-01-10   s540006       True
2 голосов
/ 10 марта 2020

2-я строка нужного выхода имеет Year как 2019, поэтому я предполагаю, что 2-я строка df1.Year также 2019 вместо 2020

Если я правильно понимаю, вам нужно объединить и отфильтровать Date вне диапазона BeginDate и EndDate. Во-первых, в df2 есть дубликаты и недопустимые диапазоны дат. Нам нужно удалить дубликаты и недопустимые диапазоны перед объединением. Недопустимые диапазоны дат - это диапазоны, в которых BeginDate> = EndDate, что является индексом 3 из df2.

#convert all date columns of both `df1` and `df2` to datetime dtype
df1['Date'] = pd.to_datetime(df1['Date'])
df2[['BeginDate', 'EndDate']] = df2[['BeginDate', 'EndDate']].apply(pd.to_datetime)

#left-merge on `Year`, `ID` and using `eval` to compute 
#columns `Condition` where `Date` is between `BeginDate` and `EndDate`. 
#Finally assign back to `df1`
df1['Condition'] = (df1.merge(df2.loc[df2.BeginDate < df2.EndDate].drop_duplicates(), 
                                on=['Year','ID'], how='left')
                       .eval('Condition= BeginDate <= Date <= EndDate')['Condition'])

Out[614]:
   Year  Week   ID       Date  Condition
0  2020     1  123 2020-01-01       True
1  2019     1  345 2020-01-01      False
2  2020     2  123 2020-01-07      False
3  2020     1  123 2020-01-01       True
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...