Usługi logistyczne, a w szczególności automatyczne punkty odbioru przesyłek (paczkomaty) odgrywają kluczową rolę w dynamicznym rozwoju e-commerce. Ich rozmieszczenie w przestrzeni miejskiej może być znaczące zarówno z perspektywy mieszkańców, wpływając na wygodę i dostępność usług, jak i optymalizacji procesów dostaw przez firmy kurierskie. Celem tego projektu jest przeprowadzenie analizy przestrzennej paczkomatów InPost na terenie Warszawy oraz odpowiedzenie na następujące pytanie badawcze: Czy rozmieszczenie paczkomatów InPost w Warszawie wykazuje istotne skupiska, czy są one jednak równomiernie rozproszone?
Dane dotyczące paczkomatów w Warszawie zostały pobrane z internetu z wykorzystaniem biblioteki osmdata, która pozyskuje dane z OpenStreetMap za pośrednictwem narzędzia Overpass API. Umożliwiło to uzyskanie szczegółowych informacji o lokalizacji paczkomatów oraz o ich operatorach. Ze względu na to, że owe mapy są aktualizowane przez społeczność, to istnieje prawdopodobieństwo, że niektóre z paczkomatów zostały pominięte. Pliki w formacie .shp dotyczące konturów powiatów i dzielnic Warszawy, a także siatki kilometrowej z gęstością zaludnienia pochodzą z serwisu GIS.
# Wczytanie bibliotek
library("sf")
library("ggplot2")
library("osmdata")
library("dplyr")
library("tmap")
library("spatstat")
library("dbscan")
# Wczytanie konturów dla Warszawy
WAW <- st_read("dane/powiaty.shp", quiet = TRUE) %>%
filter(jpt_nazwa_ == "powiat Warszawa") %>%
st_transform(crs = 4326) # przekształcenie do formatu WGS84
# Wczytanie konturów dla dzielnic Warszawy
WAW_D <- st_read("dane/dzielnice_Warszawy.shp", quiet = TRUE) %>%
st_transform(crs = 4326)
# Wczytanie danych o gestości zaludnienia i ograniczenie do Warszawy
POP <- st_read("dane/GRID_NSP2021_RES.shp", quiet = TRUE) %>%
st_transform(crs = 4326) %>%
st_intersection(WAW)
# Pobranie danych o paczkomatach w Warszawie
parcel_lockers <- opq(bbox = st_bbox(WAW)) %>%
add_osm_feature(key = "amenity", value = "parcel_locker") %>%
osmdata_sf()
# Obróbka danych o paczkomatach oraz nadanie im spójnych nazw
WAW_parcel_lockers <- parcel_lockers$osm_points %>%
st_transform(crs = 4326) %>%
filter(!is.na(amenity)) %>%
filter(!is.na(geometry)) %>%
select(osm_id, amenity, brand, operator, geometry) %>%
st_filter(WAW) %>%
mutate(operator = case_when(
grepl("InPost", brand, ignore.case = TRUE) ~ "InPost",
grepl("DPD", brand, ignore.case = TRUE) ~ "DPD",
grepl("DHL", brand, ignore.case = TRUE) ~ "DHL",
grepl("Orlen", brand, ignore.case = TRUE) ~ "Orlen",
grepl("Allegro", brand, ignore.case = TRUE) ~ "Allegro",
TRUE ~ "Inny"
)) %>%
select(osm_id, operator, geometry)
# Wykres z liczbą paczkomatów różnych firm w Warszawie
WAW_parcel_lockers %>%
st_drop_geometry() %>%
group_by(operator) %>%
summarise(liczba = n()) %>%
ggplot(aes(x = reorder(operator, -liczba), y = liczba, fill = operator)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = liczba),
vjust = -0.8,
color = "darkgrey",
size = 3) +
scale_fill_manual(values = c(
"InPost" = "moccasin",
"Allegro" = "orange",
"DPD" = "firebrick",
"Orlen" = "indianred",
"DHL" = "khaki",
"Inny" = "gray"
)) +
labs(title = "Liczba paczkomatów w Warszawie wg operatora",
x = "Operator",
y = "Liczba paczkomatów") +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(hjust = 0.5)
)
W dalszej części projektu skupimy się na paczkomatach firmy InPost, ponieważ paczkomaty tego operatora są najliczniejsze w stolicy. Skupienie się na jednym operatorze pozwoli na bardziej szczegółowe wnioski dotyczące rozmieszczenia paczkomatów i przedstawi analizę z perspektywy głównego gracza na tym rynku.
InPost <- WAW_parcel_lockers %>%
filter(operator == "InPost")
tmap_mode("plot")
tm_shape(WAW_D) +
tm_polygons(col = NA, alpha = 0.1, border.col = "gray50") +
tm_shape(WAW) +
tm_borders(lwd = 2, col = "black") +
tm_shape(InPost) +
tm_symbols(size = 0.3, col = "darkblue") +
tm_layout(title = "Paczkomaty InPost w Warszawie (dzielnice)",
title.size = 1.5,
title.position = c("center", "top"),
inner.margins = c(0.1, 0.1, 0.15, 0.1),
legend.outside = TRUE)
InPost posiada swoje paczkomaty we wszystkich dzielnicach Warszawy, zapewniając mieszkańcom łatwy dostęp do usług. Po zachodniej stronie Wisły zlokalizowano 624 paczkomaty, natomiast po wschodniej – 387. Taka różnica w rozmieszczeniu może wynikać z większej gęstości zaludnienia w zachodniej części miasta, gdzie znajduje się również centrum Warszawy.
# Zliczenie liczby paczkomatów w każdej dzielnicy
InPost_count <- st_join(InPost, WAW_D, left = FALSE) %>%
st_drop_geometry() %>%
group_by(nazwa_dzie) %>%
summarise(liczba = n())
dzielnice_bubbles <- st_centroid(WAW_D) %>%
left_join(InPost_count, by = "nazwa_dzie")
tmap_mode("plot")
tm_shape(WAW_D) +
tm_polygons(col = NA, alpha = 0.1, border.col = "gray40", lwd=0.5) +
tm_shape(WAW) +
tm_borders(lwd = 2, col = "black") +
tm_shape(dzielnice_bubbles) +
tm_text("liczba",
size = 1.2,
col = "blue",
fontface = "bold") +
tm_text("nazwa_dzie",
size = 0.5,
col = "black",
fontface = "bold",
ymod = 1.5) +
tm_layout(title = "Liczba paczkomatów w dzielnicach Warszawy",
title.size = 1.5,
title.position = c("center", "top"),
inner.margins = c(0.1, 0.1, 0.15, 0.1),
legend.outside = TRUE)
Najwięcej paczkomatów InPostu jest w dzielnicach położonych w okolicach centrum: Mokotów, Praga-Południe oraz Wola. Można również zauważyć, że sporo paczkomatów znajduje się na obrzeżach Warszawy tzn. na Białołęce, w Ursynowie, Wawerze, Bemowie czy Targówku.
WAW_D_p <- WAW_D %>%
st_transform(crs = 2178) %>%
mutate(area_km2 = as.numeric(st_area(geometry)) / 1e6)
# Wykorzystujemy crs = 2178 - https://spatialreference.org/ref/epsg/2178/
InPost_density <- InPost_count %>%
left_join(WAW_D_p %>% st_drop_geometry() %>% select(nazwa_dzie, area_km2), by = "nazwa_dzie") %>%
mutate(paczkomaty_na_km2 = liczba / area_km2)
ggplot(InPost_density, aes(x = reorder(nazwa_dzie, paczkomaty_na_km2), y = paczkomaty_na_km2, fill = paczkomaty_na_km2)) +
geom_bar(stat = "identity") +
geom_text(aes(label = round(paczkomaty_na_km2, 2)),
hjust = -0.2,
size = 3,
col = "darkgrey") +
coord_flip() +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
labs(title = "Średnia liczba paczkomatów InPost na 1 km² w dzielnicach Warszawy",
x = "Dzielnica",
y = "Paczkomaty na 1 km²") +
theme_minimal() +
theme(legend.position = "none")
Największe zagęszczenie paczkomatów odnotowano w dzielnicy Ursus, co można przypisać jej niewielkiej powierzchni wynoszącej zaledwie około 9,36 km² (jest to druga najmniejsza dzielnica Warszawy, zaraz po Żoliborzu). Wysoka gęstość paczkomatów występuje również w dzielnicach położonych w pobliżu centrum miasta, takich jak Wola, Praga-Południe, Mokotów, Żoliborz i Śródmieście, które są intensywnie zurbanizowane.
Z kolei dzielnice na obrzeżach Warszawy – Wilanów, Wesoła, Wawer i Rembertów – wyróżniają się najniższym zagęszczeniem paczkomatów, z wynikiem poniżej 1 paczkomatu na km². Najprawdopodobniej wynika to głównie z ich oddalenia od centrum oraz znacznie większej powierzchni, jak w przypadku Wawra czy Wilanowa.
Wizualizacja rozmieszczenia paczkomatów firmy InPost na tle siatki o rozmiarach 1 km x 1 km uwzględniającej gęstość zaludnienia.
tmap_mode("plot")
tm_shape(POP) +
tm_polygons(col = "RES",
style = "jenks",
palette = "Reds",
alpha = 0.7,
border.alpha = 0,
title = "Liczba mieszkańców") +
tm_shape(WAW_D) +
tm_polygons(col = NA, alpha = 0.1, border.col = "gray50") +
tm_shape(WAW) +
tm_borders(lwd = 2, col = "black") +
tm_shape(InPost) +
tm_symbols(size = 0.3, col = "darkblue") +
tm_layout(title = "Rozmieszczenie paczkomatów InPost na tle gęstości zaludnienia",
title.size = 1.5,
title.position = c("center", "top"),
inner.margins = c(0.1, 0.1, 0.15, 0.1),
legend.outside = TRUE)
Najwięcej paczkomatów znajduje się w obszarach o największej gęstości zaludnienia (ciemnoczerwone obszary), takich jak Śródmieście, Wola, Mokotów i Praga-Południe. Wysoka koncentracja mieszkańców w tych rejonach generuje duże zapotrzebowanie na paczkomaty. W obszarach o umiarkowanej gęstości zaludnienia (jasnoczerwone i pomarańczowe strefy), paczkomaty są rozmieszczone równomiernie, co może świadczyć o strategii dostosowania infrastruktury do lokalnych potrzeb. Na obrzeżach Warszawy, gdzie zaludnienie jest niskie (jaśniejsze kolory), liczba paczkomatów jest zauważalnie mniejsza. Przykładem są dzielnice takie jak Wawer, Wilanów czy Rembertów.
Odpowiednik wcześniejszego wykresu w wersji interaktywnej.
tmap_mode("view")
tm_shape(POP) +
tm_polygons(col = "RES",
style = "jenks",
palette = "Reds",
alpha = 0.7,
border.alpha = 0,
title = "Liczba mieszkańców") +
tm_shape(WAW_D) +
tm_polygons(col = NA, alpha = 0.1, border.col = "gray50") +
tm_shape(WAW) +
tm_borders(lwd = 2, col = "black") +
tm_shape(InPost) +
tm_symbols(size = 0.4, col = "darkblue") +
tm_layout(title = "Rozmieszczenie paczkomatów InPost na tle gęstości zaludnienia",
title.size = 1.5,
title.position = c("center", "top"),
inner.margins = c(0.1, 0.1, 0.15, 0.1),
legend.outside = TRUE)
Wykonano test Clarka-Evansa w celu oceny rozmieszczenia paczkomatów. W swojej hipotezie zerowej test ten zakłada całkowitą losowość przestrzenną (Complete Spatial Randomness – CSR). Hipoteza alternatywna wskazuje natomiast, czy wzorzec odbiega od losowości w kierunku uporządkowania (równomiernego rozłożenia punktów) lub skupienia (aglomeracji). Test Clarka-Evansa opiera się na porównaniu średnich odległości od najbliższego sąsiada w badanym wzorcu i wzorcu losowym. Wynik testu to wskaźnik agregacji, wyrażony jako stosunek średniej odległości w badanym wzorcu do średniej odległości w rozkładzie losowym.
WAW_merc <- WAW %>%
st_transform(crs = 2178)
WAW_D_merc <- WAW_D %>%
st_transform(crs = 2178)
InPost_merc <- InPost %>%
st_transform(crs = 2178)
WAW_owin <- as.owin(WAW_merc)
InPost_coords <- st_coordinates(InPost_merc)
InPost_ppp <- ppp(
x = InPost_coords[,1],
y = InPost_coords[,2],
window = WAW_owin)
CE_InPost <- clarkevans.test(InPost_ppp, correction = "none")
CE_InPost
##
## Clark-Evans test
## No edge correction
## Z-test
##
## data: InPost_ppp
## R = 0.73401, p-value < 2.2e-16
## alternative hypothesis: two-sided
Dane charakteryzują się statystycznie istotną tendencją do klasteryzacji, co potwierdza p-value < 2.2e-16 oraz wartość R mniejsza niż 1.
Poniżej zastosowano algorytm DBSCAN do identyfikacji skupisk paczkomatów w miejscach o dużej gęstości oraz do wychwycenia obszarów charakteryzujących się rzadkim rozmieszczeniem paczkomatów. Jego działanie polega na analizie okręgów wokół każdego punktu w przestrzeni. Algorytm dzieli punkty na: klastry wysokiej gęstości oraz szum (pojedyncze, rozproszone punkty, które nie tworzą klastrów). Opiera się na dwóch parametrach: promieniu okręgu (maksymalna odległość między punktami aby punkty te należały do jednego klastra) oraz minimalnej liczbie punktów (aby uznać obszar za klaster wysokiej gęstości).
# eps = 600 ze względu na to, że wynik wyglądał najlepiej spośród innych wartości tego parametru
InPost_dbscan <- dbscan(InPost_coords, eps = 600, minPts = 5)
InPost_dbscan
## DBSCAN clustering for 1011 objects.
## Parameters: eps = 600, minPts = 5
## Using euclidean distances and borderpoints = TRUE
## The clustering contains 41 cluster(s) and 212 noise points.
##
## 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
## 212 79 83 17 43 10 18 16 18 10 12 7 5 6 71 62 8 20 56 5
## 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
## 7 63 5 9 43 8 9 7 8 10 9 10 5 10 7 6 7 6 6 6
## 40 41
## 7 5
##
## Available fields: cluster, eps, minPts, metric, borderPoints
InPost_merc$cluster_db <- as.factor(InPost_dbscan$cluster)
hull_db <- InPost_merc %>%
filter(cluster_db != 0) %>%
group_by(cluster_db) %>%
summarize(geometry = st_union(geometry)) %>%
st_convex_hull()
ggplot() +
geom_sf(data = WAW_merc, fill = NA, color = "black", linewidth = 1) +
geom_sf(data = WAW_D_merc, fill = NA, color = "black", linewidth = 0.2) +
geom_sf(data = hull_db, aes(fill = cluster_db), alpha = 0.8, color = NA) +
geom_sf(data = InPost_merc, color = "black", size = 1, alpha = 0.5) +
geom_sf(data = InPost_merc %>% filter(cluster_db != 0), aes(color = cluster_db), size = 1) +
scale_fill_viridis_d(option = "turbo", guide = "none") +
scale_color_viridis_d(option = "turbo", guide = "none") +
labs(title = "DBSCAN dla paczkomatów InPost w Warszawie",
color = "Klastry") +
theme_minimal() +
theme(
legend.position = "none",
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
) +
coord_sf(crs = 2178, datum = NA)
Dane zostały podzielone na 42 klastry. Największe koncentracje paczkomatów występują w dzielnicach Praga-Południe, Śródmieście/Wola, Mokotów oraz Bielany/Żoliborz. Klastry bliżej centrum są większe i bardziej zwarte, podczas gdy na peryferiach są mniejsze i bardziej rozproszone. Obserwacje zaklasyfikowane jako szum, czyli niepasujące do żadnego z klastrów, zostały przypisane do klastra zerowego
Zastosowano algorytm HDBSCAN, który eliminuje problem doboru parametru dotyczącego długości promienia okręgów (eps). Algorytm ten działa na hierarchicznej strukturze klastrów i lepiej radzi sobie z niejednorodnymi danymi. Jednak jest on bardziej złożony obliczeniowo.
InPost_hdbscan <- hdbscan(InPost_coords, minPts = 5)
InPost_hdbscan
## HDBSCAN clustering for 1011 objects.
## Parameters: minPts = 5
## The clustering contains 52 cluster(s) and 308 noise points.
##
## 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
## 308 12 57 16 6 7 7 22 8 15 6 7 6 82 10 6 6 14 17 7
## 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
## 9 13 7 13 6 21 6 5 20 5 5 15 5 9 6 10 11 11 5 9
## 40 41 42 43 44 45 46 47 48 49 50 51 52
## 19 29 5 15 8 36 7 7 5 7 6 39 18
##
## Available fields: cluster, minPts, coredist, cluster_scores,
## membership_prob, outlier_scores, hc
InPost_merc$cluster_hdb <- as.factor(InPost_hdbscan$cluster)
hull_hdb <- InPost_merc %>%
filter(cluster_hdb != 0) %>%
group_by(cluster_hdb) %>%
summarize(geometry = st_union(geometry)) %>%
st_convex_hull()
ggplot() +
geom_sf(data = WAW_merc, fill = NA, color = "black", linewidth = 1) +
geom_sf(data = WAW_D_merc, fill = NA, color = "black", linewidth = 0.2) +
geom_sf(data = hull_hdb, aes(fill = cluster_hdb), alpha = 0.8, color = NA) +
geom_sf(data = InPost_merc, color = "black", size = 1, alpha = 0.5) +
geom_sf(data = InPost_merc %>% filter(cluster_hdb != 0), aes(color = cluster_hdb), size = 1) +
scale_fill_viridis_d(option = "turbo", guide = "none") +
scale_color_viridis_d(option = "turbo", guide = "none") +
labs(title = "HDBSCAN dla paczkomatów InPost w Warszawie",
color = "Klastry") +
theme_minimal() +
theme(
legend.position = "none",
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
) +
coord_sf(crs = 2178, datum = NA)
Algorytm HDBSCAN podzielił dane na 53 różne klastry. Największe skupiska paczkomatów można zaobserwować w dzielnicach Praga-Południe, Mokotów oraz Białołęka. W porównaniu do DBSCAN, HDBSCAN wygenerował więcej mniejszych klastrów w centralnych obszarach miasta. Dodatkowo lepiej identyfikował punkty szumowe (308 wobec 212 w DBSCAN). Algorytm ten okazał się skuteczniejszy w tworzeniu klastrów w obszarach o zmiennej gęstości paczkomatów.
Zastosowano algorytm KDE w celu zobrazowania obszarów o wyższej i niższej intenstywności występowania paczkomatów. Dzięki temu można łatwo wskazać miejsca, w których są one skupione bliżej siebie.
library("MASS")
library("terra")
library("viridis")
coords <- st_coordinates(InPost_merc)
bb <- st_bbox(WAW_merc)
lims <- c(bb["xmin"], bb["xmax"], bb["ymin"], bb["ymax"])
kde <- kde2d(coords[,1], coords[,2], n = 200, lims = lims)
r <- rast(kde)
crs(r) <- st_crs(WAW_merc)$wkt
WAW_vect <- vect(WAW_merc)
r_mask <- mask(r, WAW_vect)
df_r <- as.data.frame(r_mask, xy = TRUE)
colnames(df_r) <- c("x", "y", "density")
df_r$density <- df_r$density * nrow(InPost_merc) * 1e6
ggplot() +
geom_raster(data = df_r, aes(x = x, y = y, fill = density), interpolate = TRUE) +
scale_fill_viridis_c(option = "magma", name = "Gęstość", labels = scales::comma) +
geom_contour(data = df_r, aes(x = x, y = y, z = density), color = "white", alpha = 0.5) +
geom_sf(data = WAW_D_merc, fill = NA, color = "green", linewidth = 0.4) +
geom_sf(data = WAW_merc, fill = NA, color = "black", linewidth = 0.5) +
geom_sf(data = InPost_merc, color = "dodgerblue", size = 0.8) +
labs(title = "Zagęszczenie paczkomatów InPost w Warszawie") +
theme_minimal() +
theme(
axis.text = element_blank(),
axis.ticks = element_blank(),
axis.title = element_blank(),
panel.grid = element_blank()
)
Centralne dzielnice Warszawy mają najwięcej paczkomatów w stosunku do powierzchni, co może być związane z dużą populacją zamieszkującą te rejony oraz będącą tam infrastrukturą, która przyciąga ludzi zamieszkałych poza centrum (do pracy, uczelni, czy restauracji). Obrzeża miasta charakteryzują się znacznie mniejszym zagęszczeniem paczkomatów, co może wynikać z rzadszej zabudowy i mniejszego popytu ze strony konsumentów. Izolinie wskazują na stopniowe przejście od stref o wysokiej gęstości paczkomatów do stref o niskiej gęstości.
Z analizy paczkomatów InPost w Warszawie wynika, że nie są one rozmieszczone równomiernie, a zamiast tego tworzą wyraźne skupiska. Widać to szczególnie w przypadku dzielnic o wysokiej gęstości zaludnienia, takich jak przykładowo Mokotów i Praga-Południe. Potwierdził to test Clarka-Evansa, który wskazał na występowanie klastrów (R mniejsze od 1 oraz p-value < 0.05). W przypadku algorytmów DBSCAN oraz HDBSCAN, umożliwiły one dodatkowo zobrazowanie tego, gdzie te skupiska mogą się znajdować. W centrum miasta są one wyraźnie większe i gęstsze, podczas gdy na obrzeżach są one mniejsze i bardziej rozproszone. Dodatkowo zastosowanie KDE umożliwiło pokazanie miejsc, w których paczkomaty InPostu występują najliczniej. Podsumowując, wyniki wskazują na to, że paczkomaty InPost (w Warszawie) są głównie zlokalizowane w okolicach, które są bardziej zamieszkałe przez ludzi, przez co istnieje tam większe zapotrzebowanie na takowe usługi.
W kolejnych badaniach na ten temat warto byłoby uwzględnić czynniki, które mogą mieć wpływ na rozmieszczenie paczkomatów, takie jak przykładowo dochód mieszkańców, czy lokalizacja centrów handlowych. Dodatkowo, możnaby również rozszerzyć analizę o poszczególne dzielnice, inne miasta lub operatorów (np. Allegro), dzięki czemu możliwe byłoby porównanie podobieństw i różnic w ich rozmieszczeniu. Ciekawym byłoby również wykorzystanie innych technik analizy przestrzennej, np. QDC lub ETA.