Числовое нечеткое сравнение Присоединение к Python / pandas dataframes

1

У меня есть четыре таблицы производства графика авиакомпании. На самом деле у меня нет колонки PK, но я помещаю ее туда, чтобы легче справляться с полетами. Сейчас я их в pd.DataFrames.

Таблица 1. Проект №1

MarketID  Origin Dest FltNum  DepCentral  ArrCentral  PK
1DFWSJC   DFW    SJC  111     0700        1035        A
2DFWSJC   DFW    SJC  222     2035        2410        B

Таблица 2. Проект №2

MarketID  Origin Dest FltNum  DepCentral  ArrCentral  PK
1DFWSJC   DFW    SJC  111     0700        1030        A
2DFWSJC   DFW    SJC  222     2040        2410        B

Таблица 3. Проект № 3

MarketID  Origin Dest FltNum  DepCentral  ArrCentral  PK
1DFWSJC   DFW    SJC  444     0645        1015        A
2DFWSJC   DFW    SJC  555     1300        1630        X
3DFWSJC   DFW    SJC  666     2040        2410        B

Таблица 4. Окончательный график

MarketID  Origin Dest FltNum  DepCentral  ArrCentral  PK
1DFWSJC   DFW    SJC  444     0645        1015        A
2DFWSJC   DFW    SJC  666     2040        2415        B

Я в основном хочу следить за каждым рейсом, который был в проекте 1, который также находится в конечном графике, и посмотреть, как они были изменены в процессе производства. Но я также пытаюсь отслеживать изменения, внесенные в каждый полет через каждый стол. По существу это было бы внутреннее соединение, и у меня были бы столбцы для DepCentral_Draft1, DepCentral_Draft2 и т.д.

С этим я мог видеть, что между проектами 1 и 2 полет A имел время своего прибытия, перемещенное на 5 минут раньше. Между осадками 2 и 3 полет A имел время вылета и прибытия, перемещенное на 15 минут раньше. и т.п.

MarketID приближается, но между черновиками № 2 и № 3 они вставили рейс и повторно обозначенный MarketID (он был заказан по времени отправления). Похоже, что они добавили и вычитали полеты довольно бесцеремонно между Draft # 2 и # 3, а также между № 3 и Final, поэтому я не могу полагаться на MarketID.

FltNum также очень непоследователен. Между Draft # 1 и # 2 они совпадают, но # 3 и # 4, они кажутся случайным образом заменяют номер рейса после Draft # 3.

Как человек я могу сказать, что 2DFWSJC в таблице 3 не является ни полетом А, ни В из-за того, насколько отличается его время вылета/прибытия. Когда я присоединился к MarketID, он думал, что перелет B перемещается с 2040 до 1300 (именно так я заметил эти вставки).

Я не могу объединить Origin, Dest, и время отправления или прибытия, и матч, потому что они немного смещены, поэтому они не являются точными совпадениями. Я видел нечеткие сравнения со строками, но я не видел ничего подобного с числами.

Что-то вроде определения полета B как

Origin     = DFW  
Dest       = SJC   
DepCentral = ~2035 
ArrCentral = ~2410    

Где я мог бы определить, насколько близко ~, или автоматически понимает, что 2040 намного ближе к 2035 году, чем 1300, и присоединяется, если это возможно.

Это оригинальная функция, которую я написал, чтобы найти разницу между исходным расписанием и новым.

def schdChanges(base, new):
merged = pd.merge(base, new, on = ['MktSegID'])

diffDf = pd.DataFrame()

diffDf['MktSegID'] = merged['MktSegID']
diffDf['Cdep'] = merged['Cdep_y'] - merged['Cdep_x']
diffDf['Carr'] = merged['Carr_y'] - merged['Carr_x']
diffDf['Block'] = merged['Block_y'] - merged['Block_x']
diffDf['Turn'] = merged['Turn_y'] - merged['Turn_x']

return diffDf 

Редактировать. Альтернативные решения, которые у меня были, определяют, где изменилась емкость рынка (например, DFW-SJC), и сосредоточена только на них.

DirMkt - Origin + Dest

Draft1Cap = Draft1.groupby('DirMkt')['MktSegID'].nunique()
FinalCap  = Final.groupby( 'DirMkt')['MktSegID'].nunique()

FinalCap.subtract(Draft1Cap, fill_value = 0)[FinalCap.subtract(Draft1Cap, fill_value = 0) != 0]  

Итак, теперь я определил, какие из них изменились. Моя старая функция работает на все, кроме этих.

И используя это, я определил, какие рынки нужно удалить из графика, потому что они не существуют ни в черновике, ни в финале

def returnNotMatches(a, b):
    return [[x for x in a if x not in b], [x for x in b if x not in a]]  

returnNotMatches(set(Draft1['DirMkt'].unique()), set(Final['DirMkt'].unique())) 

Итак, в основном, минимальный минимум, который мне нужен, - это выяснить, когда добавляется полет, где он был добавлен по отношению к другим рейсам на его рынке?

Теги:
pandas
fuzzy-comparison

1 ответ

0
Лучший ответ

У вас 6 столбцов. Позвольте разбить их на то, насколько они полезны в этой ситуации.

MarketID и FltNum могут меняться произвольно, так что это нам не поможет. Origin and Dest Я предполагаю, что он должен быть одним и тем же и не может измениться, поэтому мы проверим, что DepCentral и ArrCentral являются наиболее важными, и каждый из них может изменить до 50 минут.

У вас есть сложная бизнес-логика, поэтому, вероятно, это не простое решение. Итак, здесь, где приходит удовольствие, кодируя что-то, чтобы справиться с этой логикой!

Эта программа находит совпадения, я оставлю это до вас, чтобы получить результат, как вам это понравится

import pandas as pd

Если у вас есть даты или вы точно знаете, что у вас не будет чего-то в 23:55, которое должно совпадать с 00:04, вы можете упростить или заменить эту логику

def time_change(old_time, new_time):
    old_hrs, new_hrs = int(old_time[0:2]), int(new_time[0:2])
    old_mins, new_mins = int(old_time[2:]), int(new_time[2:])

    old_total = 60 * old_hrs + old_mins
    new_total = 60 * new_hrs + new_mins

    # note, this may make incorrect assumptions since we don't have the day. 
    # If you have the day in your actual data, there are better ways of comparing the times
    return abs((new_total - old_total)) % (24 * 60)

теперь, к сути дела, проверяя матч. Вы изложили то, что ищете, так что это всего лишь логика его реализации. Эта функция принимает любые две строки для сравнения.

def check_match(old, new):
    #["MarketID", "Origin", "Dest", "FltNum", "DepCentral", "ArrCentral"]
    if old['Origin'] != new['Origin']:
        return False, "", ""
    if old['Dest'] != new['Dest']:
        return False, "", ""
    total_time_change = time_change(old["DepCentral"], new["DepCentral"]) + \
        time_change(old["ArrCentral"], new["ArrCentral"])
    if abs(total_time_change) <= 50:
        return True, total_time_change, "other fields that changed"
    else:
        return False, "", ""

Прокрутите все строки в старой и новой таблице и сравните их. С размером данных, о которых вы упомянули, смотреть все должно быть хорошо.

def compare_tables(old, new_df):
    if 'PK' not in old.columns:
        # use the current index as the starting PK
        old['PK'] = old.index
    for _, old_row in old.iterrows():
        print("Looking at row:")
        print(old_row.T)
        pk = old_row['PK']
        best_match = None
        best_match_time_change = float('inf')
        for _, new_row in new_df.iterrows():
            is_match, time_change, old_changes = check_match(old_row, new_row)
            if is_match and (time_change < best_match_time_change):
                best_match_time_change = time_change
                best_match = new_row
        print("The best match is:")
        if best_match is not None:
            print(best_match.T, best_match_time_change)
        else:
            print("no matches found")
        print()
        print()

петля через все пары таблиц:

all_tables = [table_1, table_2, table_3, table_4]
for old, new_df in zip(all_tables, all_tables[1:]):
    compare_tables(old, new_df)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню