Të gjithë kodin për modelin e parashikimit të kohës së dorëzimit të ushqimit mund ta gjeni këtu.

Në industrinë e shpërndarjes së ushqimit, ku dorëzimi i shpejtë dhe i saktë është vendimtar, modeli i parashikimit të kohës së dorëzimit të ushqimit luan një rol jetësor. Koha e dorëzimit ndikon drejtpërdrejt në kënaqësinë e klientit dhe përvojën e tyre të përgjithshme. Modeli ndihmon në optimizimin e operacioneve, thjeshtimin e logjistikës dhe përmbushjen e pritjeve të klientëve duke ofruar vlerësime të besueshme se kur do të dorëzohet ushqimi.

Ne do të ndërtojmë një model të parashikimit të kohës së dorëzimit të ushqimit, i cili do të prezantohet në dy artikuj. Në këtë artikull fillestar, ne do të fillojmë duke ekzaminuar të dhënat dhe më pas do t'i pastrojmë ato për të siguruar saktësinë dhe qëndrueshmërinë. Ne do të përdorim gjithashtu teknika inxhinierike të veçorive për të nxjerrë njohuri të vlefshme që do të përmirësojnë performancën e modelit. Në fund të këtij artikulli, ne do të kemi fituar një kuptim të mirë të të dhënave dhe do të kemi mësuar se si t'i përmirësojmë ato për parashikime më të sakta.

artikullin tjetër, fokusi ynë do të jetë në përgatitjen e të dhënave për ndërtimin e modelit. Kjo do të përfshijë hapa të mëtejshëm të parapërpunimit si kodimi i etiketës dhe standardizimi për të optimizuar të dhënat. Më pas, ne do të gërmojmë në trajnimin dhe vlerësimin e modeleve të regresionit, duke përfshirë algoritme të njohura si regresioni linear, pemët e vendimeve, pyjet e rastësishme dhe XGBoost. Ne do të përdorim Cross Validation për të vlerësuar performancën e modelit.

Parakushtet

  • Njohuri me fletoret e Google Colab, Notebooks Jupyter ose ndonjë mjet ekuivalent për të punuar me kodin dhe analizën e të dhënave.
  • Kuptimi bazë i Pandas Dataframe, si dhe konceptet e të mësuarit të mbikëqyrur, veçanërisht modelet e regresionit.

Grupi i të dhënave

Të dhënat e të dhënave që do të përdor ka rreth 45,593 regjistrime të shpërndarjes së ushqimit me informacionin përkatës si vendndodhjen e restorantit, vendndodhjen e dorëzimit, kushtet e motit, kohën e dorëzimit të densitetit të trafikut etj. Mund ta shkarkoni grupin e të dhënave nga Kaggle.

Importoni bibliotekat

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as stats
import statistics
from geopy.distance import geodesic

from sklearn.model_selection import train_test_split,cross_val_score, GridSearchCV
from sklearn.preprocessing import LabelEncoder,StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
import xgboost as XGBRegressor
from sklearn.metrics import mean_squared_error, r2_score,mean_absolute_error

import warnings
warnings.filterwarnings('ignore')

Ngarko grupin e të dhënave

df_train = pd.read_csv('food-delivery-dataset/train.csv')

Përmbledhje e grupit të të dhënave

df_train.info()

  • Të dhënat përbëhet nga 45,593 regjistrime të dorëzimit me 20 kolona, ​​duke përfshirë një përzierje të llojeve të të dhënave duke përfshirë int64, float64 dhe objekt.
  • Variabla e daljes në grupin e të dhënave është Koha_marrë(min), e cila përfaqëson kohën e dorëzimit në minuta.
#Summary Statistics for numerical columns
df_train.describe().T

#Summary Statistics for non-numerical columns
df_train.describe(exclude=np.number).T

Vëzhgimet kryesore

  • Kolona Time_Ordered përmban vlera NaN që kërkojnë trajtim.
  • Ne duhet të eksplorojmë kolona të tjera për vlerat e mundshme null.
  • Kolona Time_taken(min) duhet të konvertohet në një vlerë numerike në vend të një objekti.
  • Ne duhet të adresojmë llojet e të dhënave objekt në grupin e të dhënave.
  • Veçoritë Data dhe ora kërkojnë gjithashtu trajtim dhe përpunim paraprak.

Pastrimi i të dhënave

Tani që kemi një kuptim bazë të grupit të të dhënave, le të vazhdojmë me pastrimin e të dhënave. Pastrimi i të dhënave është një proces thelbësor që përfshin identifikimin dhe korrigjimin e gabimeve, mospërputhjeve dhe vlerave që mungojnë brenda një grupi të dhënash.

Gjatë këtij hapi, ne do të fokusohemi në formatimin e emrave të kolonave, rregullimin e vlerave të kolonave, përditësimin e llojeve të të dhënave, heqjen e kolonave të panevojshme, trajtimin e vlerave që mungojnë dhe kontrollimin për çdo të dhënë të kopjuar. Duke trajtuar këto aspekte, ne mund të përmirësojmë cilësinë dhe besueshmërinë e përgjithshme të grupit të të dhënave.

Formatoni emrat e kolonave

Le të fillojmë me formatimin e emrave të kolonave, pasi kjo do të rrisë qartësinë dhe lexueshmërinë e të dhënave.

#Change name Weatherconditions to Weather_conditions
df_train.rename(columns={'Weatherconditions': 'Weather_conditions'},inplace=True)

Nxjerrja e të dhënave

Më pas, do të vazhdojmë me nxjerrjen e vlerave nga disa veçori. Ky hap do të na mundësojë të marrim informacion specifik dhe përkatës nga grupi i të dhënave, duke u përafruar me objektivat tona të analizës dhe modelimit.

Për shembull, duke nxjerrë emrin e qytetit nga kolona "Delivery_person_ID", ne mund të marrim një variabël të vlefshëm që do të na ndihmojë në zhvillimin e modelit tonë. Për më tepër, qartësia mund të arrihet duke hequr "kushtet" nga kolona "Kushtet_moti" dhe duke eliminuar "(min)" nga kolona "Koha_marrë". Këto rregullime do të përmirësojnë qartësinë dhe kuptimin e përgjithshëm të të dhënave.

def extract_column_value(df):
    #Extract time and convert to int
    df['Time_taken(min)'] = df['Time_taken(min)'].apply(lambda x: int(x.split(' ')[1].strip()))
    #Extract Weather conditions
    df['Weather_conditions'] = df['Weather_conditions'].apply(lambda x: x.split(' ')[1].strip())
    #Extract city code from Delivery person ID
    df['City_code']=df['Delivery_person_ID'].str.split("RES", expand=True)[0]
    
extract_column_value(df_train)
df_train[['Time_taken(min)','Weather_conditions','City_code']].head()

Përditëso llojet e të dhënave

Më pas, ne do të vazhdojmë të përditësojmë llojet e të dhënave të veçorive në formatet e tyre më të përshtatshme.

#Update datatypes
def update_datatype(df):
    #Update datatype from object to float
    df['Delivery_person_Age'] = df['Delivery_person_Age'].astype('float64')
    df['Delivery_person_Ratings'] = df['Delivery_person_Ratings'].astype('float64')
    df['multiple_deliveries'] = df['multiple_deliveries'].astype('float64')
    #Update datatype from object to datetime
    df['Order_Date']=pd.to_datetime(df['Order_Date'],format="%d-%m-%Y")
    
update_datatype(df_train)

Hiqni kolonat

Ne mund të eliminojmë kolonat e kolonave ID dhe Delivery_person_ID pasi ato shërbejnë vetëm si identifikues unik dhe nuk ofrojnë ndonjë informacion të vlefshëm për qëllimet tona të analizës ose ndërtimit të modelit. Heqja e këtyre kolonave do të ndihmojë në përmirësimin e të dhënave dhe përmirësimin e qartësisë dhe efikasitetit të analizës sonë.

df_train.drop(['ID','Delivery_person_ID'],axis=1,inplace=True)

Kontrollo për vlera të dyfishta

Le të kontrollojmë për çdo vlerë të kopjuar në grupin e të dhënave për të siguruar saktësinë e të dhënave dhe për të eliminuar paragjykimet. Nëse gjenden dublikatë, do të na duhet t'i heqim ato për të ruajtur integritetin e të dhënave.

#Check for Duplicate Values
if (len(df_train[df_train.duplicated()])>0):
    print("There are Duplicate values present")
else:
    print("There is no duplicate value present")

Meqenëse nuk ka vlera të kopjuara, ne mund të vazhdojmë në hapin tjetër dhe të adresojmë vlerat që mungojnë në grupin e të dhënave.

Trajtoni vlerat që mungojnë

Trajtimi i vlerave që mungojnë është thelbësor për ruajtjen e integritetit të të dhënave, sigurimin e analizave të sakta dhe parandalimin e rezultateve të njëanshme ose njohurive jo të plota. Si pjesë e këtij procesi, ne do të konvertojmë vargjet NaNnp.nan dhe më pas do të llogarisim dhe printojmë numrin e vlerave që mungojnë për çdo veçori.

#Replace NaN to np.nan
df_train.replace('NaN', float(np.nan), regex=True,inplace=True)
#Show count of NaN values in data
df_train.isnull().sum().sort_values(ascending=False)

Duke pasur parasysh praninë e vlerave të NaN në kolona të shumta, vizualizimi i këtyre kolonave do të ishte i dobishëm për identifikimin e modeleve dhe përcaktimin e qasjes optimale për trajtimin e vlerave nule për çdo veçori.

#Data Visualisation
cols = ['Delivery_person_Age','Delivery_person_Ratings','Weather_conditions','Road_traffic_density','multiple_deliveries','Festival','City']
num_plots = len(cols)
num_rows = (num_plots // 2) + (num_plots % 2)

fig, axes = plt.subplots(num_rows, 2, figsize=(20,15))

for i, column_name in enumerate(cols):
    row = i // 2
    col = i % 2

    ax = axes[row, col]
    sns.countplot(data=df_train, x=column_name, order=df_train[column_name].value_counts().sort_index().index, ax=ax)

    ax.set_xlabel(column_name)
    ax.set_ylabel('No. of Orders')
    ax.set_title(column_name)
    ax.tick_params(axis='x', rotation=45)
    
if num_plots % 2 != 0:
    fig.delaxes(axes[-1, -1])

plt.tight_layout()
plt.show()

  • Për Delivery_person_age dhe Weather_conditions, të cilat shfaqin shpërndarje pothuajse uniforme, vlerat që mungojnë do të plotësohen rastësisht.
  • Për sa i përket Vlerësimeve_personit të dorëzimit, i cili shfaq një shpërndarje të anuar majtas, vlerat që mungojnë do të plotësohen me mesataren.
  • Për kolonat e mbetura kategorike, vlerat që mungojnë do të plotësohen me modalitetin, që përfaqëson vlerën më të shpeshtë në secilën kolonë përkatëse.

Këto qasje do të ndihmojnë për të siguruar që vlerat që mungojnë të trajtohen siç duhet bazuar në karakteristikat e secilës kolonë.

#Handle null values
def handle_null_values(df):
    df_train['Delivery_person_Age'].fillna(np.random.choice(df['Delivery_person_Age']), inplace=True)
    df_train['Weather_conditions'].fillna(np.random.choice(df['Weather_conditions']), inplace=True)
    df_train['City'].fillna(df['City'].mode()[0], inplace=True)
    df_train['Festival'].fillna(df['Festival'].mode()[0], inplace=True)
    df_train['multiple_deliveries'].fillna(df['multiple_deliveries'].mode()[0], inplace=True)
    df_train['Road_traffic_density'].fillna(df['Road_traffic_density'].mode()[0], inplace=True)
    df_train['Delivery_person_Ratings'].fillna(df['Delivery_person_Ratings'].median(), inplace=True)
    
handle_null_values(df_train)
df_train.isnull().sum()

SHËNIM — Aktualisht, ne nuk i kemi adresuar vlerat NaN në kolonën Time_Ordered. Megjithatë, ne do t'i trajtojmë ato në seksionin tjetër pasi të konvertojmë kolonën në një objekt datatime. Kjo qasje sekuenciale na lejon të sigurojmë trajtimin e saktë të vlerave që mungojnë pasi të përfundojë transformimi i nevojshëm i të dhënave.

Inxhinieri e Veçorisë

Tani le të vazhdojmë në hapin tjetër të Inxhinierisë së Veçorive. Në këtë hap, ne zgjedhim, transformojmë dhe gjenerojmë me kujdes veçori domethënëse nga të dhënat, duke synuar të përmirësojmë performancën e modeleve tona të mësimit të makinerive. Ky proces ofron përfitime të ndryshme, duke përfshirë saktësinë e përmirësuar të modelit, zbutjen e përshtatjes së tepërt dhe zbulimin e modeleve të fshehura.

Në këtë skenar specifik, ne kemi akses në veçori si data e porosisë, koha e porosisë dhe koha e zgjedhur, të cilat ofrojnë mundësi të shumta për krijimin e shumë funksioneve të reja që mund të na ndihmojnë në kapjen dhe përdorimin e aspekteve të ndryshme të informacionit të lidhur me kohën.

Për më tepër, ne kemi veçori të gjerësisë dhe gjatësisë gjeografike si për restorantin ashtu edhe për vendndodhjen e dorëzimit. Duke përdorur këtë informacion, ne mund të llogarisim distancën midis dy vendndodhjeve, duke shtuar një veçori tjetër të vlefshme në grupin tonë të të dhënave.

Le të fillojmë duke gjeneruar veçori të reja bazuar në Data e porosisë.

def extract_date_features(data):
    data["day"] = data.Order_Date.dt.day
    data["month"] = data.Order_Date.dt.month
    data["quarter"] = data.Order_Date.dt.quarter
    data["year"] = data.Order_Date.dt.year
    data['day_of_week'] = data.Order_Date.dt.day_of_week.astype(int)
    data["is_month_start"] = data.Order_Date.dt.is_month_start.astype(int)
    data["is_month_end"] = data.Order_Date.dt.is_month_end.astype(int)
    data["is_quarter_start"] = data.Order_Date.dt.is_quarter_start.astype(int)
    data["is_quarter_end"] = data.Order_Date.dt.is_quarter_end.astype(int)
    data["is_year_start"] = data.Order_Date.dt.is_year_start.astype(int)
    data["is_year_end"] = data.Order_Date.dt.is_year_end.astype(int)
    data['is_weekend'] = np.where(data['day_of_week'].isin([5,6]),1,0)

extract_date_features(df_train)

Më pas, ne do të krijojmë një veçori të re që nxjerr në pah ndryshimin midis kohës së porosisë dhe kohës së marrjes. Si pjesë e këtij hapi, ne do të heqim të gjitha veçoritë që lidhen me kohën dhe datën, pasi kemi nxjerrë tashmë informacionin e nevojshëm prej tyre, i cili mund të përdoret në modelin tonë të mësimit të makinerive.

#Calculate Time Differnce 
def calculate_time_diff(df):
    # Find the difference between ordered time & picked time
    df['Time_Orderd'] = pd.to_timedelta(df['Time_Orderd'])
    df['Time_Order_picked'] = pd.to_timedelta(df['Time_Order_picked'])
    df['Time_Order_picked_formatted'] = df['Order_Date'] + np.where(df['Time_Order_picked'] < df['Time_Orderd'], pd.DateOffset(days=1), pd.DateOffset(days=0)) + df['Time_Order_picked']
    df['Time_Ordered_formatted'] = df['Order_Date'] + df['Time_Orderd']
    df['order_prepare_time'] = (df['Time_Order_picked_formatted'] - df['Time_Ordered_formatted']).dt.total_seconds() / 60
    
    # Handle null values by filling with the median
    df['order_prepare_time'].fillna(df['order_prepare_time'].median(), inplace=True)
    
    # Drop all the time & date related columns
    df.drop(['Time_Orderd', 'Time_Order_picked', 'Time_Ordered_formatted', 'Time_Order_picked_formatted', 'Order_Date'], axis=1, inplace=True)
calculate_time_diff(df_train)

Më pas, ne do të llogarisim distancën midis vendndodhjes së restorantit dhe vendndodhjes së dorëzimit dhe do të krijojmë një veçori të re të quajtur distanca

#Calculate distance between restaurant location & delivery location
def calculate_distance(df):
    df['distance']=np.zeros(len(df))
    restaurant_coordinates=df[['Restaurant_latitude','Restaurant_longitude']].to_numpy()
    delivery_location_coordinates=df[['Delivery_location_latitude','Delivery_location_longitude']].to_numpy()
    df['distance'] = np.array([geodesic(restaurant, delivery) for restaurant, delivery in zip(restaurant_coordinates, delivery_location_coordinates)])
    df['distance']= df['distance'].astype("str").str.extract('(\d+)').astype("int64")
    
calculate_distance(df_train)

konkluzioni

Ne kemi kuptuar rëndësinë e parashikimit të kohës së dorëzimit të ushqimit, kemi fituar një kuptim gjithëpërfshirës të të dhënave të shpërndarjes së ushqimit dhe kemi përfunduar pastrimin e të dhënave dhe inxhinierinë e veçorive.

artikullin tjetër, ne do të avancojmë më tej duke kryer detyra shtesë të parapërpunimit dhe do të përqendrohemi në ndërtimin e modelit të parashikimit të kohës së dorëzimit të ushqimit.