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

Чтобы этот проект был эффективным, вы должны иметь следующие функции для работы:

  • адрес
  • product_type (элемент)

Остальное заберет скрипт Python. (Предполагается, что у вас есть набор данных заказов для вашего бизнеса)

df.info()

Ниже у нас есть цикл, который собирает точки широты и долготы, используемые для кластеризации в скрипте. Чтобы избежать сбоя цикла (из-за выпадения некоторых адресов), мы добавили блок try-except. Результаты добавляются в словарь. Обратите внимание, что этот процесс может занять много времени, поэтому я рекомендую настроить хранилище для ваших данных, чтобы избежать повторения процесса. По соображениям конфиденциальности мы не можем делиться своими результатами.

import pandas as pd
from geopy.geocoders import Nominatim

# create an instance of the geocoder
geolocator = Nominatim(user_agent="geoapiExercises")

# create an empty dataframe to store the results
result = pd.DataFrame(columns=["Address", "Latitude", "Longitude"])

# loop over the addresses
for i, address in enumerate(df.address):
    try:
        # get the location of the address
        location = geolocator.geocode(address)

        # store the address, latitude, and longitude in the dataframe
        result = result.append({"Address": address, "Latitude": location.latitude, "Longitude": location.longitude}, ignore_index=True)
    except:
        pass

# print the results
print(result)

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

def lat_long():    
    query = """select distinct(item) from OCF.distances"""
    items = pd.read_sql(query, con = engine_ocf)
    center = []
    for i in range(0,len(items)):    
        query = """SELECT  address_1, 
        gender, suspected_income, zip, 
        zestimate, city, livingArea, 
        item, price, quantity, fulfilled, 
        purchase, Date,  
        Latitude, Longitude
        FROM OCF.orderss
        join OCF.coordinates on OCF.orderss.address = OCF.coordinates.address
        where item = '{}'""".format(items.item[i])
        center.append(pd.read_sql(query, con = engine_ocf))
    query = pd.concat(center)
    return query

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

import math

def haversine_distance(lat1, lon1, lat2, lon2):
    # convert decimal degrees to radians 
    lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
    # haversine formula 
    dlat = lat2 - lat1 
    dlon = lon2 - lon1 
    a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
    c = 2 * math.asin(math.sqrt(a)) 
    # Earth radius in kilometers
    km = 6371 * c
    return km

def calculate_distance_between_lists(latitudes1, longitudes1, latitudes2, longitudes2):
    if len(latitudes1) != len(longitudes1) or len(latitudes2) != len(longitudes2):
        raise ValueError("The length of the latitude and longitude lists must be the same.")
    if len(latitudes1) != len(latitudes2):
        raise ValueError("The length of the two latitude lists must be the same.")
    
    distances = []
    for lat1, lon1, lat2, lon2 in zip(latitudes1, longitudes1, latitudes2, longitudes2):
        distances.append(haversine_distance(lat1, lon1, lat2, lon2))
    return distances

Следующим шагом является создание алгоритма, который находит центроиды с помощью кластеризации K-средних, метода неконтролируемого машинного обучения. Для выполнения этого шага в вашей среде Python должен быть установлен Scikit-learn (sklearn). Под функцией find_k_means_centroids мы создали цикл, который перебирает фрейм данных, хранящийся в функции lat_long(). Затем результаты сохраняются в словаре centroids_1, который мы будем использовать для построения и определения оптимальных складов в этом районе.

import numpy as np
from sklearn.cluster import KMeans

def find_k_means_centroids(data, k):
    # Fit the KMeans model to the data
    kmeans = KMeans(n_clusters=k, random_state=0).fit(data)
    # Get the cluster centroids
    centroids = kmeans.cluster_centers_
    return centroids
centriods_1 = { "item" : [],
            "Latitude": [],
            "Longitude": []}

for i in lat_long().item.unique():
    df = lat_long()[lat_long()['item'] == i]
    try:
        for k in range(2, 3):
            centroids = find_k_means_centroids(df[['Latitude', 'Longitude']].values, k)
            centriods_1["item"].append(i)
            centriods_1["Latitude"].append(centroids[0][0])
            centriods_1["Longitude"].append(centroids[0][1])
            centriods_1["item"].append(i)
            centriods_1["Latitude"].append(centroids[1][0])
            centriods_1["Longitude"].append(centroids[1][1])
            print("Centroids for k =", df.item.iloc[k])
            print(centroids)
            print("\n")
    except Exception as e:
                centriods_1['Latitude'].append(float('nan'))
                centriods_1['Longitude'].append(float('nan'))
                centriods_1['item'].append(float('nan'))

Далее мы хотим визуализировать и увидеть, где центроиды встают на свои места.

## import plotly.express as px
import pandas as pd
import plotly.express as px

# Create the scatter plot
fig = px.scatter_mapbox(centroids_df, lat="Latitude", lon="Longitude", color="item",
                        color_discrete_sequence=["red", "green", "blue", "yellow", "purple",
                                                 "cyan", "orange", "pink","Magenta","Maroon","Violet","Purple", "Olive"],
                        mapbox_style="carto-positron", size_max=45,zoom=50)

# Add the map layout
fig.update_layout(mapbox_center = {"lat": 37.0902, "lon": -95.7129},
                  mapbox=dict(center=dict(lat=37.0902, lon=-95.7129),
                              zoom=3))
legend=dict(x=0, y=1, traceorder="normal", font=dict(size=4))

# Show the plot
fig.show()

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

Затем нам нужно собрать склады в Соединенных Штатах, для этого я использовал центры выполнения Amazon в качестве наших выбранных складов.

Для центров выполнения вы можете найти их здесь и собрать данные с помощью простого pd.read_html(ссылка)[0]:



def distances_to_fba():
    from math import radians, sin, cos, sqrt, atan2
    data = centroids_df

    # Convert latitude and longitude columns to radians
    data['latitude'] = centroids_df['Latitude'].apply(radians)
    data['longitude'] = centroids_df['Longitude'].apply(radians)
    fba['latitude'] = fba['Latitude'].apply(radians)
    fba['longitude'] = fba['Longitude'].apply(radians)

    # Define the Earth's radius (mean radius = 6,371km)
    R = 6371

    # Initialize an empty list to store the distances
    distances = {'distances' : [],
                 'Amazon Address': [],
                'product': [],
                 'Longitude' : [],
                 'Latitude' : []
                }

    # Loop through each row in df1
    for index1, row1 in data.iterrows():
        lat1 = row1['latitude']
        lon1 = row1['longitude']
        product = row1['item']
      # Loop through each row in df2
        for index2, row2 in fba.iterrows():
            lat2 = row2['latitude']
            lon2 = row2['longitude']

             # Calculate the Haversine distance between two points
            a = sin((lat2-lat1)/2)**2 + cos(lat1) * cos(lat2) * sin((lon2-lon1)/2)**2
            c = 2 * atan2(sqrt(a), sqrt(1-a))
            d = R * c

                # Append the distance to the list
            distances['distances'].append(d)
            distances['Amazon Address'].append(row2['Address'])
            distances['product'].append(product)
            distances['Latitude'].append(row2['Latitude'])
            distances['Longitude'].append(row2['Longitude'])



    df = pd.DataFrame(distances)
    return df
    # Convert the distances list to a numpy array
    #distances = np.array(distances)

Последний шаг в этом проекте — выяснить, где разместить ваши продукты. Для этого я создал цикл, который перебирает расстояния в порядке возрастания и получает для каждого продукта голову кадра.

Best_locations = {'product': [],
                 'Amazon Address': [],
                 'distances' : [],
                 'Longitude' : [],
                 'Latitude' : []}
for i in list(items.item):
    try:
        Best_locations['product'].append(distances_to_fba()[distances_to_fba()['product'] ==str(i)].sort_values(by="distances", ascending=True).head(1)['product'].unique()[0] )
        Best_locations['Amazon Address'].append(distances_to_fba()[distances_to_fba()['product'] ==str(i)].sort_values(by="distances", ascending=True).head(1)['Amazon Address'].unique()[0])
        Best_locations['distances'].append(distances_to_fba()[distances_to_fba()['product'] ==str(i)].sort_values(by="distances", ascending=True).head(1)['distances'].unique()[0])
        Best_locations['Latitude'].append(distances_to_fba()[distances_to_fba()['product'] ==str(i)].sort_values(by="distances", ascending=True).head(1)['Latitude'].unique()[0])    
        Best_locations['Longitude'].append(distances_to_fba()[distances_to_fba()['product'] ==str(i)].sort_values(by="distances", ascending=True).head(1)['Longitude'].unique()[0])
    except:
            pass
pd.DataFrame(Best_locations)

Конечным результатом являются рекомендуемые места для хранения продуктов, как показано ниже.

Если вам понравилось читать об этом проекте или вы даже использовали часть кода в своей работе, свяжитесь со мной в Linkedin и ознакомьтесь с некоторыми другими моими работами на Github. В настоящее время ищу свою первую роль в науке о данных, так как я недавно закончила магистратуру. в аналитике данных. Ссылки ниже!

https://www.linkedin.com/in/tylerbrownpsu/