Projekt obejmuje analizę rozmieszczenia przestrzennego restauracji z platformy pyszne.pl w Warszawie. Celem analizy było zrozumienie, w jaki sposób restauracje z tej platformy są rozlokowane oraz gdzie znajdują się ich największe skupiska. Dodatkowo zestawiono informacje o rozmieszczeniu tych restauracji z rozmieszeniem ludności i mieszkań na mapie Warszawy.
Informacje o restauracjach zostały pozyskane metodą web-scrapingu ze strony pyszne.pl. Dane scrapowano dla wszystkich kodów pocztowych w Warszawie, a następnie na potrzeby tego projektu, dla każdej restauracji zagregowano informacje o jej położeniu geograficznym, średniej ocenie i ich ilości oraz średnich kosztach transportu. Na początku wczytano je i ograniczono do obserwacji z pełnymi danymi o położeniu geograficznym oraz tylko tych położonych w Warszawie. Ostatecznie baza składała się z 2442 restauracji. Dodatkowo akodowano też dane w klasach sf i sp.
r.pyszne <- read.csv("rest_pyszne_full.csv")
head(r.pyszne)
## restaurant
## 1 https://www.pyszne.pl/zakupy/alkohole-24h-warszawa-powstancow-slaskich?sortBy=name&menuItemId=true
## 2 https://www.pyszne.pl/menu/barracuda-sushi-bar
## 3 https://www.pyszne.pl/menu/ciekawa-cafe
## 4 https://www.pyszne.pl/zakupy/cofesto-ciastko-i-kawa?sortBy=name&menuItemId=true
## 5 https://www.pyszne.pl/menu/indian-summers
## 6 https://www.pyszne.pl/menu/kawiarnia-w-pasazu-powstancow-slaskich
## longitude latitude street.address district postal.code
## 1 20.92838 52.26222 Powstańców Śląskich 124 Bemowo 01-466
## 2 20.91331 52.24265 Kazubów 1 Bemowo 01-466
## 3 20.91337 52.24199 Powstańców Śląskich 80d Bemowo 01-466
## 4 20.91502 52.23929 Stacja Metro Bemowo lokal 1011 C-04 Bemowo 01-466
## 5 20.91520 52.24019 Bolkowska 2C/H4 Bemowo 01-466
## 6 20.91962 52.25517 Powstańców Śląskich 106D/210 Bemowo 01-466
## address.region rating_score rating_votes delivery_to_postals
## 1 Warszawa NA NA 776
## 2 Warszawa 4.9 5 629
## 3 Warszawa 4.2 124 217
## 4 Warszawa NA NA 213
## 5 Warszawa 4.8 1835 3435
## 6 Warszawa 4.9 7 782
## delivery_to_districts transport_min_cost transport_mean_cost
## 1 9 0 11.020558
## 2 8 10 14.651163
## 3 5 0 4.535337
## 4 4 0 5.550000
## 5 18 3 17.430398
## 6 7 0 11.340150
## transport_max_cost
## 1 11.99
## 2 20.00
## 3 9.99
## 4 9.99
## 5 25.00
## 6 11.99
# Usunięcie restauracji poza Warszawą
r.pyszne <- r.pyszne[r.pyszne$address.region == "Warszawa",]
r.pyszne <- r.pyszne[!r.pyszne$district == "",]
unique(r.pyszne$district)
## [1] "Bemowo" "Włochy" "Wola" "Ursus"
## [5] "Praga-Południe" "Bielany" "Targówek" "Śródmieście"
## [9] "Praga-Północ" "Ursynów" "Mokotów" "Żoliborz"
## [13] "Wilanów" "Ochota" "Białołęka" "Wesoła"
## [17] "Wawer" "Rembertów"
# Usunięcie restauracji z brakiem lokalizacji
r.pyszne <- r.pyszne[complete.cases(r.pyszne[,c(2:3)]),]
dim(r.pyszne)
## [1] 2442 14
library(sp)
library(spdep)
library(sf)
# Restauracje klasa SP
r.pyszne.sp <- r.pyszne
coordinates(r.pyszne.sp) <-c ("longitude", "latitude")
proj4string(r.pyszne.sp) <- as(st_crs("+proj=longlat +ellps=WGS84"), "CRS")
summary(r.pyszne.sp)
# Restauracje klasa SF
r.pyszne.sf <- st_as_sf(r.pyszne.sp)
r.pyszne.sf
r.pyszne.sf <- st_transform(r.pyszne.sf, 4326)
Następnie wczytano pliki shapefile z informacjami o konturach Warszawy i jej dzielnic oraz zapisono je klasach sf i sp.
########## KONTURY DZIELNIC ##########
dzielnice.sf <- st_read("dzielnice_Warszawy.shp", stringsAsFactors = FALSE)
dzielnice.sf <- st_transform(dzielnice.sf, 4326)
dzielnice.sp <- as_Spatial(dzielnice.sf, cast = TRUE, IDs ="nazwa_dzie")
########## KONTURY WARSZAWY ##########
powiaty <- st_read("powiaty.shp", stringsAsFactors = FALSE)
powiaty <- st_transform(powiaty, 4326)
warszawa.sf <- powiaty[powiaty$jpt_nazwa == "powiat Warszawa",]
warszawa.sp <- as_Spatial(warszawa.sf, cast = TRUE, IDs ="jpt_kod_je")
W pierwszej kolejności zwizualizowano jak rozmieszczone są restauracje na mapie Warszawy.
library(ggplot2)
ggplot() + geom_sf(data = dzielnice.sf, fill = "white") +
theme_minimal() +
geom_point(data = r.pyszne, aes(x = longitude, y = latitude), color = "#66295F", size = 2, alpha = 0.3) +
ggtitle("Restauracje pyszne.pl") +
theme(plot.title = element_text(hjust = 0.5, size = 20),
axis.title.x = element_blank(),
axis.title.y = element_blank())
Patrząc na mapę, zauważyć można iż restauracje nie są do końca równomiernie rozłożone - najwięcej jest ich w centralnych dzielnicach. Zliczmy ile jest restauracji dla każdej z dzielnic i zwizualizujmy to.
library(dplyr)
# Liczba restauracji w każdej dzielnicy
liczba.restauracji <- r.pyszne %>%
group_by(district) %>% summarise(number.of.restaurants = n()) %>% ungroup
dzielnice.sf <- merge(dzielnice.sf, liczba.restauracji,
by.x = "nazwa_dzie", by.y = "district", all.x = TRUE, sort = FALSE)
centroids <- st_centroid(dzielnice.sf)
ggplot() +
geom_sf(data = dzielnice.sf,
aes(fill = number.of.restaurants), color = "gray5") +
scale_fill_gradient(low = "#EED3EA", high = "#66295F", name = "") +
theme_minimal() +
ggtitle("Restauracje w poszczególnych dzielnicach") +
theme(plot.title = element_text(hjust = 0.5, size = 15),
axis.title.x = element_blank(),
axis.title.y = element_blank()) +
geom_sf_text(data = centroids, aes(label = nazwa_dzie), color = "black", size = 2)
Widoczne jest iż najwięcej jest ich na Śródmieściu, Woli i Mokotowie. Spójrzmy też na to jak kształtuje się liczba kodów do których dowozi restauracja.
ggplot() +
geom_sf(data = dzielnice.sf, fill = "white") +
theme_minimal() +
geom_point(data = r.pyszne,
aes(x = longitude, y = latitude, color = delivery_to_postals), size = 2, alpha = 0.5) +
ggtitle("Liczba adresów pocztowych do których dowozi restauracja")+
theme(plot.title = element_text(hjust = 0.5, size = 15),
axis.title.x = element_blank(),
axis.title.y = element_blank()) +
scale_color_viridis_c(option = "A", name = "")
W centrum Warszawy kropki mają jaśniejszy kolor - tutaj oznacza to, że restauracje dowożą do większej liczby adresów pocztowych, porównując z ciemniejszymi kropkami - te restauracje dowożą do mniejszej liczby adresów pocztowych.
Połączmy informacje o restauracjach z danymi dotyczącymi liczby populacji jak i budynków z NSP 2021, pochodzących z GUS1. W pierwszej kolejności ładujemy dane, wycinamy grid jedynie dla Warszawy i łączymy dane o budynkach i populacji w jeden df. Dla Warszawy finalnie mamy grid składający się z 601 kratek wielkości 1km x 1km. Dla każdej kratki mamy informację o liczbie rezydentów ogółem, w podziale na płeć oraz na wiek jak i liczbę budynków oraz mieszkań.
########## Grid populacja ##########
POP.2021 <- st_read("trkx19307.shp", stringsAsFactors=FALSE)
## Reading layer `trkx19307' from data source
## `/Users/monikakot/Desktop/Spatial/Projekt_spatial/trkx19307.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 315856 features and 12 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 14.11547 ymin: 48.99754 xmax: 24.1529 ymax: 54.84028
## Geodetic CRS: WGS 84
# wycięcie Warszawy
mer.pop <- st_join(POP.2021, warszawa.sf, join = st_intersects)
a <- which(mer.pop$jpt_nazwa_ == "powiat Warszawa")
POP.2021.waw <- POP.2021[a,]
POP.2021.waw
## Simple feature collection with 601 features and 12 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 20.83728 ymin: 52.09162 xmax: 21.2805 ymax: 52.378
## Geodetic CRS: WGS 84
## First 10 features:
## shape_leng shape_area code
## 63632 3997.988 998991.0 CRS3035RES1000mN3286000E5059000
## 63660 3998.006 999000.1 CRS3035RES1000mN3298000E5059000
## 63672 3997.989 998991.6 CRS3035RES1000mN3287000E5059000
## 63688 3998.008 999000.9 CRS3035RES1000mN3299000E5059000
## 63701 3998.005 998999.3 CRS3035RES1000mN3297000E5059000
## 63798 3998.009 999001.7 CRS3035RES1000mN3300000E5059000
## 64015 3997.998 998996.2 CRS3035RES1000mN3293000E5059000
## 64197 3997.991 998992.4 CRS3035RES1000mN3288000E5059000
## 64223 3998.016 999004.8 CRS3035RES1000mN3296000E5060000
## 64235 3998.005 998999.4 CRS3035RES1000mN3289000E5060000
## pl_code res res_male res_fem res_0_14
## 63632 PL_CRS3035RES1000mN3286000E5059000 4691 2251 2440 719
## 63660 PL_CRS3035RES1000mN3298000E5059000 156 75 81 13
## 63672 PL_CRS3035RES1000mN3287000E5059000 296 142 154 48
## 63688 PL_CRS3035RES1000mN3299000E5059000 1 NA NA 0
## 63701 PL_CRS3035RES1000mN3297000E5059000 423 203 220 64
## 63798 PL_CRS3035RES1000mN3300000E5059000 134 56 78 18
## 64015 PL_CRS3035RES1000mN3293000E5059000 1367 647 720 226
## 64197 PL_CRS3035RES1000mN3288000E5059000 121 51 70 14
## 64223 PL_CRS3035RES1000mN3296000E5060000 0 0 0 0
## 64235 PL_CRS3035RES1000mN3289000E5060000 113 53 60 21
## res_15_64 res_65_ multipolyg multipoly1 geometry
## 63632 2873 1099 2656647 6519.691 MULTIPOLYGON (((20.85177 52...
## 63660 114 29 2669361 6535.273 MULTIPOLYGON (((20.87799 52...
## 63672 196 52 2657702 6520.985 MULTIPOLYGON (((20.85395 52...
## 63688 NA NA 2670426 6536.576 MULTIPOLYGON (((20.88018 52...
## 63701 274 85 2668298 6533.971 MULTIPOLYGON (((20.8758 52....
## 63798 74 42 2671491 6537.879 MULTIPOLYGON (((20.88238 52...
## 64015 875 266 2664050 6528.768 MULTIPOLYGON (((20.86705 52...
## 64197 89 18 2658759 6522.281 MULTIPOLYGON (((20.85613 52...
## 64223 0 0 2667074 6532.473 MULTIPOLYGON (((20.88812 52...
## 64235 68 24 2659656 6523.382 MULTIPOLYGON (((20.8728 52....
########## Grid budynki ##########
BUD.2021 <- st_read("twmn18483.shp", stringsAsFactors=FALSE)
## Reading layer `twmn18483' from data source
## `/Users/monikakot/Desktop/Spatial/Projekt_spatial/twmn18483.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 315856 features and 8 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 14.11547 ymin: 48.99754 xmax: 24.1529 ymax: 54.84028
## Geodetic CRS: WGS 84
# wycięcie Warszawy
mer.bud <- st_join(BUD.2021, warszawa.sf, join = st_intersects)
a <- which(mer.bud$jpt_nazwa_ == "powiat Warszawa")
BUD.2021.waw <- BUD.2021[a,]
BUD.2021.waw
## Simple feature collection with 601 features and 8 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 20.83728 ymin: 52.09162 xmax: 21.2805 ymax: 52.378
## Geodetic CRS: WGS 84
## First 10 features:
## shape_leng shape_area code
## 221862 3997.988 998991.0 CRS3035RES1000mN3286000E5059000
## 221890 3998.006 999000.1 CRS3035RES1000mN3298000E5059000
## 221902 3997.989 998991.6 CRS3035RES1000mN3287000E5059000
## 221918 3998.008 999000.9 CRS3035RES1000mN3299000E5059000
## 221931 3998.005 998999.3 CRS3035RES1000mN3297000E5059000
## 222028 3998.009 999001.7 CRS3035RES1000mN3300000E5059000
## 222245 3997.998 998996.2 CRS3035RES1000mN3293000E5059000
## 222428 3997.991 998992.4 CRS3035RES1000mN3288000E5059000
## 222454 3998.016 999004.8 CRS3035RES1000mN3296000E5060000
## 222466 3998.005 998999.4 CRS3035RES1000mN3289000E5060000
## pl_code bud dwel multipolyg multipoly1
## 221862 PL_CRS3035RES1000mN3286000E5059000 709 1924 2656647 6519.691
## 221890 PL_CRS3035RES1000mN3298000E5059000 40 43 2669361 6535.273
## 221902 PL_CRS3035RES1000mN3287000E5059000 95 101 2657702 6520.985
## 221918 PL_CRS3035RES1000mN3299000E5059000 1 1 2670426 6536.576
## 221931 PL_CRS3035RES1000mN3297000E5059000 130 144 2668298 6533.971
## 222028 PL_CRS3035RES1000mN3300000E5059000 51 50 2671491 6537.879
## 222245 PL_CRS3035RES1000mN3293000E5059000 307 620 2664050 6528.768
## 222428 PL_CRS3035RES1000mN3288000E5059000 32 46 2658759 6522.281
## 222454 PL_CRS3035RES1000mN3296000E5060000 0 0 2667074 6532.473
## 222466 PL_CRS3035RES1000mN3289000E5060000 39 48 2659656 6523.382
## geometry
## 221862 MULTIPOLYGON (((20.85177 52...
## 221890 MULTIPOLYGON (((20.87799 52...
## 221902 MULTIPOLYGON (((20.85395 52...
## 221918 MULTIPOLYGON (((20.88018 52...
## 221931 MULTIPOLYGON (((20.8758 52....
## 222028 MULTIPOLYGON (((20.88238 52...
## 222245 MULTIPOLYGON (((20.86705 52...
## 222428 MULTIPOLYGON (((20.85613 52...
## 222454 MULTIPOLYGON (((20.88812 52...
## 222466 MULTIPOLYGON (((20.8728 52....
########## Grid populacja + budynki ##########
ALL.2021.waw <- st_join(POP.2021.waw, BUD.2021.waw, join = st_equals)
Następnie łączymy dane grid z informacjami o restauracjach. Najpierw do każdej restauracji przyporządkowujemy kratkę 1km x 1km w której leży, a następnie zliczmy ile jest restauracji w każdej z nich - dzięki temu mamy jednolite dane.
########## Połączenie gridu + restauracji ##########
ALL.2021.waw$myID <- 1:dim(ALL.2021.waw )[1]
mer.res <- st_join(r.pyszne.sf, ALL.2021.waw, join = st_intersects)
dim(mer.res) # do każdej restauracji przyporządkowana kratka
## [1] 2442 34
########## Zliczenie restauracji w każdej kratce ##########
restaurants.agg <- mer.res %>% group_by(myID) %>% summarise(number.of.restaurants = n())
restaurants.agg <- as.data.frame(restaurants.agg)
restaurants.agg <- restaurants.agg[, 1:2]
ALL.2021.waw <- merge(ALL.2021.waw, restaurants.agg,
by.x = "myID", by.y = "myID", all.x = TRUE, sort = FALSE)
# Dla kratek z brakiem przyporządkowanych restauracji zapisujemy 0
ALL.2021.waw$number.of.restaurants[is.na(ALL.2021.waw$number.of.restaurants)] <- 0
Zwizualizujmy teraz jak rozmieszczone są restauracje, populacja i mieszkania w Warszawie.
Patrząc na te trzy mapy, widać iż kształt rozmieszczenia resturacji jest podobny do rozmieszczenia ludności i mieszkań w Warszawie. Prosta regresja liniowa potwierdza iż liczba restauracji jest zależna od liczby populacji i mieszkań.
mod <- lm(number.of.restaurants~res+dwel, data = ALL.2021.waw)
summary(mod)
##
## Call:
## lm(formula = number.of.restaurants ~ res + dwel, data = ALL.2021.waw)
##
## Residuals:
## Min 1Q Median 3Q Max
## -23.355 -0.740 -0.344 0.382 44.024
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.3695006 0.2740887 1.348 0.178
## res -0.0031591 0.0002513 -12.571 <2e-16 ***
## dwel 0.0078284 0.0004215 18.573 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 5.281 on 598 degrees of freedom
## Multiple R-squared: 0.6543, Adjusted R-squared: 0.6531
## F-statistic: 565.8 on 2 and 598 DF, p-value: < 2.2e-16
W dalszej części projektu sprawdzimy czy w rozmieszczeniu restauracji z platformy pyszne.pl można stwierdzić aglomeracje oraz jakie klastry tworzą restauracje.
Entropia, czyli tak zwana miara nieporządku, jest jedną z metod statystycznych pozwalających uchwycić zjawisko aglomeracji. Dostarcza ona poniekąd informacji na temat prawdopodobieństwa znalezienia się w danej lokalizacji - gdy entropia jest wysoka to punkty są bardzo regularnie rozłożone, więc ciężko jest oszacować gdzie znajdzie się kolejna lokalizacja - prawdopodobieństwo dla każdego z nich jest podobne. Z kolei gdy entropia jest niska, to rozłożenie punktów jest nieregularne i z większym pradopodobieństwem można stwierdzić położenie nowych punktów. W tej metodzie entropia zostala połączona z teselacją, która pozwala na przejście z danych punktowych na obszary podzielone na tzw. kafelki, których różne rozmiary, w zależności od stopnia koncentracji danych, pełnią rolę prawodpodobieństw. W przypadku równomiernego rozkładu, rozmiar poszczególnych kafelków jest praktycznie identyczny, a w przypadku aglomeracji zróżnicowany - w obszarze gdzie punktów jest dużo to kafelki będą małe.
Aglomerację można zmierzyć za pomocą porównania maksymalnej, hipotetycznej entropii z tą zaobserwowaną, uzyskując tak zwaną relatywną entropię. Uzyskana przez nas wartość relatywna entropii mniejsza niż jeden oznacza, iż badany rozkład nie jest jednostajny i można wyróżnic aglomarację, co jest widoczne w centralnych częściach Warszawy.
Ta metoda wykorzystuje dwie zmienne przestrzenne - całkowity dystans do k najbliższych sąsiadów oraz liczbę sąsiadów w stałym promieniu. Znormalizowane zmienne są następnie poddawane klasteryzjacji metodą k-średnich. Parametry zostały ustalone na liczenie odległości do 10 sąsiadów i zliczanie sąsiadów w promieniu 0.05.
## [1] 1.970513
## [1] 0.1010681
Na wykresie widzimy podział restauracji na trzy grupy: o małym, średnim
i dużym zagęszczeniu. Te w grupie o najmniejszej gęstości charakteryzują
się najwyższymi odleglościami do k najbliższych sąsiadów oraz najniższą
liczbą sąsiadów w promieniu. Z kolei te o największej gęstości mają
najmniejsze odległości do k najbliższych sąsiadów oraz największą liczbę
sąsiadów w promieniu. Zwizualizujmy te grupy na mapie Warszawy.
Grupa restauracji o największej gęstości znajduje się na Śródmieściu, Woli, Ochocie, Żoliborzu i części Mokotowa. Z kolei grupa o najmniejszej gęstości znajduje się na dzielnicach bardziej obrzeżnych - na Białolęce, Rembertowie czy Wawrze.
Metoda ta wykorzystuje dwa parametry: promień koła oraz minimalną liczbę punktów oczekiwanych w tym kole. Spełnienie tego warunku decyduje o przynależności danego punktu do klastra o wysokiej gęstości. W tej metodzie przy grupowaniu danych nie zakłada się oczekiwanej liczby klastrów, tylko ustala poziom parametrów. W tym przypadku wyznaczono promień na 0.01, a oczkiwaną liczbę punktów (restauracji) w danym promieniu na 50.
Algorytm wyznaczył 7 klastrów - największy z nich obejmuje większą część Śródmieścia oraz część Woli, Ochoty oraz Mokotowa. Wyodrębniono też oddzielne klastry, m.in. na południowej części Mokotowa, na Pradze Południe czy Bemowie.
Metoda ta analizuje różnorodność wartości w sąsiedztwie każdego punktu w przestrzeni. Wysokie wartości lokalnej entropii świadczą o występowaniu bardziej zróżnicowanych danych, a niskie o ich podobieństwie. Poniższe mapy prezentują wartości lokalnej entropii dla rozkładu przestrzennego restauracji po przekształceniu danych punktowych w rastry składające się z 10 000 komórek (100 wierszy x 100 kolumn), dla różnych wartości ruchomego okna.
Dla mniejszych wartości okna efekty koncentracji przestrzennej wydają się być mniej wyraźne, co wskazuje na lepsze dopasowanie dla wyższych jego wartości. W obszarze centrum Warszawy oraz jego okolicach wartości lokalnej entropii są relatywnie wysokie, co sugeruje, że obszar pod względem restauracji jest bardziej zrożnicowany. Czym dalej od centrum, tym wartości lokalnej entropii są niższe, co wskazuje na coraz mniejsze zróżnicowanie pod względem restauracji wraz z oddalaniem się od centralnych dzielnic.
Przeprowadzona analiza eklsporacyjna pozwoliła na wyciągnięcie kilku wniosków dotyczących przestrzennego rozmieszczenia restauracji z platformy pyszne.pl w Warszawie. Po pierwsze rozmieszczenie to nie jest równomierne - resturacja najgęściej skupiają się w centralnych dzielnicach miasta. Taki rozkład przestrzenny koreluje z ogólnym rozkładem ludności oraz mieszkań w mieście,co zostało potwierdzone za pomocą prostej regresji liniowej. Po drugie, za pomocą kilku różnych metod, udało się wyróżnić aglomeracje. Każda z nich dostarczyła podobnych wniosków - im dalej od centrum Warszawy, tym mniej gęsto skupione są restauracje.