Datenvorbereitung und explorative Analyse

Nachdem wir unseren Ansatz skizziert haben, werfen wir einen Blick auf unsere Daten und mit welchen Funktionen, mit denen wir arbeiten.

Aus diesem Grund sehen wir unsere Daten ~ 197.000 Lieferungen mit einer Vielzahl von numerischen und nicht numerischen Funktionen. In keinem der Merkmale fehlt ein großer Prozentsatz von Werten (niedrigste Nicht-Null-Anzahl ~ 181.000). Daher müssen wir uns wahrscheinlich nicht Sorgen machen, dass wir die Funktionen vollständig fallen lassen.

Überprüfen Sie, ob unsere Daten doppelte Lieferungen enthalten und ob Beobachtungen, für die wir die Lieferzeit nicht berechnen können.

print(f"Variety of duplicates: {df.duplicated().sum()} n")

print(pd.DataFrame({'Lacking Rely': df(('created_at', 'actual_delivery_time')).isna().sum()}))

Wir sehen, dass alle Lieferungen einzigartig sind. Es gibt jedoch 7 Lieferungen, die einen Wert für echtes_delivery_time fehlen, was bedeutet, dass wir die Lieferdauer für diese Bestellungen nicht berechnen können. Da es nur eine Handvoll davon gibt, werden wir diese Beobachtungen aus unseren Daten entfernen.

Lassen Sie uns nun unser Vorhersageziel erstellen. Wir möchten die Lieferdauer (in Sekunden) vorhersagen, dh die verstrichene Zeit zwischen dem, als der Kunde die Bestellung (‚created_at‘) und wann er die Bestellung erhielt (‚true_delivery_time‘).

# convert columns to datetime 
df('created_at') = pd.to_datetime(df('created_at'), utc=True)
df('actual_delivery_time') = pd.to_datetime(df('actual_delivery_time'), utc=True)

# create prediction goal
df('seconds_to_delivery') = (df('actual_delivery_time') - df('created_at')).dt.total_seconds()

Das Letzte, was wir tun werden, bevor wir unsere Daten in Zug/Check aufteilen, ist die Überprüfung fehlender Werte. Wir haben die Nicht-Null-Zählungen für jede Funktion bereits angezeigt, aber lassen Sie uns die Proportionen anzeigen, um ein besseres Bild zu erhalten.

Wir sehen, dass die Marktmerkmale (‚onShift_dashers‘, ‚tousy_dashers‘, ‚outstanding_orders‘) den höchsten Prozentsatz an fehlenden Werten (~ 8% fehlen) aufweisen. Die Funktion mit der zweithöchsten fehlenden Datenrate ist ’store_primary_category‘ (~ 2%). Alle anderen Funktionen fehlen <1%.

Da keine der Funktionen eine hohe Anzahl fehlt, werden wir keine davon entfernen. Später werden wir uns mit den Characteristic -Verteilungen befassen, um zu entscheiden, wie wir mit fehlenden Beobachtungen für jede Funktion angemessen umgehen können.

Aber zuerst teilen wir unsere Daten in Zug/Check auf. Wir werden mit einem 80/20 -Break up fortfahren und diese Testdaten in eine separate Datei schreiben, die wir erst berühren, wenn wir unser endgültiges Modell bewerten.

from sklearn.model_selection import train_test_split
import os

# shuffle
df = df.pattern(frac=1, random_state=42)
df = df.reset_index(drop=True)

# break up
train_df, test_df = train_test_split(df, test_size=0.2, random_state=42)

# write check information to separate file
listing = 'datasets'
file_name = 'test_data.csv'
file_path = os.path.be part of(listing, file_name)
os.makedirs(listing, exist_ok=True)
test_df.to_csv(file_path, index=False)

Lassen Sie uns nun in die Einzelheiten unserer Zugdaten eintauchen. Wir werden unsere numerischen und kategorialen Merkmale festlegen, um klarzustellen, auf welche Spalten in späteren Erkundungsschritten verwiesen werden.

categorical_feats = (
'market_id',
'store_id',
'store_primary_category',
'order_protocol'
)

numeric_feats = (
'total_items',
'subtotal',
'num_distinct_items',
'min_item_price',
'max_item_price',
'total_onshift_dashers',
'total_busy_dashers',
'total_outstanding_orders',
'estimated_order_place_duration',
'estimated_store_to_consumer_driving_duration'
)

Lassen Sie uns die kategorialen Merkmale mit fehlenden Werten (‚markt_id‘, ’store_primary_category‘, ‚order_protocol‘) erneut besuchen. Da bei diesen Merkmalen nur wenig Daten fehlten (<3%), werden wir diese fehlenden Werte einfach mit einer „unbekannten“ Kategorie unterstellen.

  • Auf diese Weise müssen wir keine Daten aus anderen Funktionen entfernen.
  • Möglicherweise enthält das Fehlen von Merkmalswerten eine gewisse Vorhersageleistung für die Lieferdauer, dh diese Funktionen sind nicht zufällig fehlen.
  • Darüber hinaus werden wir diesen Imputationsschritt zu unserer Vorverarbeitungspipeline während der Modellierung hinzufügen, damit wir diese Arbeit nicht manuell an unserem Testsatz duplizieren müssen.
missing_cols_categorical = ('market_id', 'store_primary_category', 'order_protocol')

train_df(missing_cols_categorical) = train_df(missing_cols_categorical).fillna("unknown")

Schauen wir uns unsere kategorialen Merkmale an.

pd.DataFrame({'Cardinality': train_df(categorical_feats).nunique()}).rename_axis('Characteristic')

Da ‚markt_id‘ & ‚order_protocol‘ eine niedrige Kardinalität aufweist, können wir ihre Verteilungen leicht visualisieren. Auf der anderen Seite sind ‚Store_id‘ & ‚Store_Primary_Category‘ hohe Kardinalitätsfunktionen. Wir werden uns diese später tiefer ansehen.

import seaborn as sns
import matplotlib.pyplot as plt

categorical_feats_subset = (
'market_id',
'order_protocol'
)

# Arrange the grid
fig, axes = plt.subplots(1, len(categorical_feats_subset), figsize=(13, 5), sharey=True)

# Create barplots for every variable
for i, col in enumerate(categorical_feats_subset):
sns.countplot(x=col, information=train_df, ax=axes(i))
axes(i).set_title(f"Frequencies: {col}")

# Regulate structure
plt.tight_layout()
plt.present()

Einige wichtige Dinge zu beachten:

  • ~ 70% der aufgegebenen Bestellungen haben ‚markt_id‘ von 1, 2, 4
  • <1% der Bestellungen haben 'order_protocol' von 6 oder 7

Leider haben wir keine zusätzlichen Informationen zu diesen Variablen, z. Zu diesem Zeitpunkt kann es eine gute Idee sein, nach zusätzlichen Daten zu diesen Informationen zu bitten, da dies zur Untersuchung der Tendencies bei der Lieferdauer in den Kategorisierungen der Area/Standort in Bezug auf die Area/Standort -Kategorisierungen beiträgt.

Schauen wir uns unsere kategorischen Merkmale mit höherer Kardinalität an. Vielleicht hat jede ’store_primary_category‘ einen zugehörigen ’store_id‘ reichweite? In diesem Fall benötigen wir möglicherweise nicht ’store_id‘, da ’store_primary_category‘ bereits viele Informationen über den in bestellten Geschäft zusammenfassen würde.

store_info = train_df(('store_id', 'store_primary_category'))

store_info.groupby('store_primary_category')('store_id').agg(('min', 'max'))

Offensichtlich nicht der Fall: Wir sehen, dass ‚Store_id‘ die Bereiche über die Ebenen von ’store_primary_category‘ überlappen.

Ein kurzer Blick auf die unterschiedlichen Werte und zugehörigen Frequenzen für ’store_id‘ & ’store_primary_category‘ zeigt, dass diese Funktionen haben hohe Kardinalität und sind spärlich verteilt. Im Allgemeinen können kategorische Merkmale mit hoher Kardinalität bei Regressionsaufgaben problematisch sein, insbesondere für Regressionsalgorithmen, die nur numerische Daten erfordern. Wenn diese hohen Kardinalitätsfunktionen codiert werden, können sie den Characteristic -Raum drastisch vergrößern, wodurch die verfügbaren Daten spärlich und die Fähigkeit des Modells verringert werden, auf neue Beobachtungen in diesem Merkmalsraum zu verallgemeinern. Für eine bessere und professionellere Erklärung der Phänomene können Sie mehr darüber lesen Hier.

Lassen Sie uns ein Gefühl dafür erhalten, wie spärlich diese Funktionen verteilt sind.

store_id_values = train_df('store_id').value_counts()

# Plot the histogram
plt.determine(figsize=(8, 5))
plt.bar(store_id_values.index, store_id_values.values, shade='skyblue')

# Add titles and labels
plt.title('Worth Counts: store_id', fontsize=14)
plt.xlabel('store_id', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.xticks(rotation=45) # Rotate x-axis labels for higher readability
plt.tight_layout()
plt.present()

Wir sehen, dass es eine Handvoll Geschäfte gibt, die Hunderte von Bestellungen haben, aber die meisten von ihnen haben viel weniger als 100.

Um die hohe Kardinalität von ’store_id‘ zu verarbeiten, erstellen wir eine andere Funktion, „store_id_freq“, die die Werte „store_id“ nach Frequenz gruppiert.

  • Wir gruppieren die Werte ’store_id‘ in fünf verschiedene Perzentilbehälter, die unten gezeigt sind.
  • ’store_id_freq‘ hat eine viel niedrigere Kardinalität als ’store_id‘, behält jedoch relevante Informationen zur Beliebtheit des Geschäfts, aus dem die Lieferung bestellt wurde.
  • Weitere Inspirationen hinter dieser Logik finden Sie in diesem Faden.
def encode_frequency(freq, percentiles) -> str:
if freq < percentiles(0):
return '(0-50)'
elif freq < percentiles(1):
return '(50-75)'
elif freq < percentiles(2):
return '(75-90)'
elif freq < percentiles(3):
return '(90-99)'
else:
return '99+'

value_counts = train_df('store_id').value_counts()
percentiles = np.percentile(value_counts, (50, 75, 90, 99))

# apply encode_frequency to every store_id based mostly on their variety of orders
train_df('store_id_freq') = train_df('store_id').apply(lambda x: encode_frequency(value_counts(x), percentiles))

pd.DataFrame({'Rely':train_df('store_id_freq').value_counts()}).rename_axis('Frequency Bin')

Unsere Codierung zeigt uns, dass ~ 60.000 Lieferungen in den im 90. bis 99. Perzentil katgorisierten Geschäften in Bezug auf die Popularität bestellt wurden, während ~ 12.000 Lieferungen aus Geschäften bestellt wurden, die im 0–50 -jährigen Perzentil in Beliebtheit waren.

Nachdem wir versucht haben, relevante „store_id“ in einer niedrigeren Dimension zu erfassen, versuchen wir, mit ’store_primary_category‘ etwas Ähnliches zu tun.

Schauen wir uns die beliebtesten „store_primary_category“ -Spegel an.

Ein kurzer Blick zeigt uns, dass viele dieser „store_primary_category“ -Egle nicht ausschließlich zueinander sind (z. B. „American“ & „Burger“). Weitere Untersuchungen zeigen viele weitere Beispiele für diese Artwork von Überlappung.

Lassen Sie uns additionally versuchen, diese unterschiedlichen Geschäftskategorien in einige grundlegende allumfassende Gruppen abzubilden.

store_category_map = {
'american': ('american', 'burger', 'sandwich', 'barbeque'),
'asian': ('asian', 'chinese language', 'japanese', 'indian', 'thai', 'vietnamese', 'dim-sum', 'korean',
'sushi', 'bubble-tea', 'malaysian', 'singaporean', 'indonesian', 'russian'),
'mexican': ('mexican'),
'italian': ('italian', 'pizza'),
}

def map_to_category_type(class: str) -> str:
for category_type, classes in store_category_map.gadgets():
if class in classes:
return category_type
return "different"

train_df('store_category_type') = train_df('store_primary_category').apply(lambda x: map_to_category_type(x))

value_counts = train_df('store_category_type').value_counts()

# Plot pie chart
plt.determine(figsize=(6, 6))
value_counts.plot.pie(autopct='%1.1f%%', startangle=90, cmap='viridis', labels=value_counts.index)
plt.title('Class Distribution')
plt.ylabel('') # Cover y-axis label for aesthetics
plt.present()

Diese Gruppierung ist wahrscheinlich brutal einfach, und es kann eine bessere Möglichkeit geben, diese Geschäftskategorien zu gruppieren. Machen wir uns für den Einfachheit halber für den Second fort.

Wir haben eine Menge Untersuchung unserer kategorialen Merkmale durchgeführt. Schauen wir uns die Verteilungen für unsere numerischen Funktionen an.

# Create grid for boxplots
fig, axes = plt.subplots(nrows=5, ncols=2, figsize=(12, 15)) # Regulate determine measurement
axes = axes.flatten() # Flatten the 5x2 axes right into a 1D array for simpler iteration

# Generate boxplots for every numeric characteristic
for i, column in enumerate(numeric_feats):
sns.boxplot(y=train_df(column), ax=axes(i))
axes(i).set_title(f"Boxplot for {column}")
axes(i).set_ylabel(column)

# Take away any unused subplots (if any)
for i in vary(len(numeric_feats), len(axes)):
fig.delaxes(axes(i))

# Regulate structure for higher spacing
plt.tight_layout()
plt.present()

Boxplots für eine Teilmenge unserer numerischen Funktionen

Viele der Verteilungen scheinen richtig verzerrt zu sein, dann sind sie auf das Vorhandensein von Ausreißern zurückzuführen.

Insbesondere scheint es eine Bestellung mit über 400 Artikeln zu geben. Dies scheint seltsam zu sein, da die nächstgrößte Bestellung weniger als 100 Artikel beträgt.

Schauen wir uns mehr in diese Bestellung von 400 Artikel an.

train_df(train_df('total_items')==train_df('total_items').max())

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert