Znacie to nagranie?
- No, ja mam M1 do wynajęcia…
- To już nie jest aktualne, wie pan?
Pytanie brzmi za ile wynająć takie M1? Albo ile zapłacić? W zależności od lokalizacji.
Skąd wziąć dane?
Sprawdzimy jakie są rynkowe ceny najmu mieszkań, korzystając jak zwykle z dostępnych źródeł. Popatrzymy globalnie, na cały kraj. Popatrzymy też statystycznie (bez dużej ingerencji w dane)., a skoro statystycznie to potrzebujemy dużo danych, żeby statystyka miała sens. Tym bardziej że będziemy głównie uśredniać (albo precyzyjniej – opierać się na medianach, czyli wartościach środkowych).
Skąd weźmiemy dane? Z serwisów ogłoszeniowych. Jest kilka specjalizujących się w nieruchomościach (np. otodom.pl), ale są też inne gdzie przewija się znacznie więcej ogłoszeń – OLX.pl, gumtree.pl czy gratka.pl. Ta ostatnia okazała się najbardziej przydatna. Z prostego powodu – na liście wyników wyszukiwania znajdujemy potrzebne dane (dzięki temu wystarczy przeskanować kolejne strony indeksu zamiast wchodzić na każde ogłoszenie – jest szybciej), ułożone dodatkowo w sensownym HTMLu.
Jeśli chcesz pominąć technikalia – skroluj dalej, albo kliknij sobie tutaj i przejdź do analizy, a jeśli interesują Cię ceny mieszkań w Warszawie – to też jest, a nieco dalej w innych miastach.
Już możecie się domyślić, że metodą “czołgową” przejedziemy po kolei wszystkie strony i sobie pobierzemy to co nam potrzebne.
Potrzebujemy kilku bibliotek, przy okazji zdefiniujemy “standardowy” wygląd wszystkich wykresów (żeby nie dodawać za każdym razem moje ulubione “+ theme_minimal()”).
1 2 3 4 5 6 7 8 |
library(rvest) library(dplyr) library(tidyr) library(ggplot2) library(stringr) # wygląd wykresów dla wszystkich theme_set(theme_minimal()) |
Początek dość oczywisty, pobranie danych w sumie też. Ale to jest najbardziej żmudna część pracy.
Pobranie danych
Jak zrobić to metodycznie?
- Wchodzimy na wybrany serwis, szukamy tego, co będziemy pobierać (w tym przypadku mieszkania do wynajęcia, dodatkowo z kilkoma parametrami, które sprawią że w wynikach dostaniemy stosunkowo kompletne dane).
- Patrzymy jak wyglądają linki do wygenerowanych stron
- Sprawdzamy ile jest tych stron (w tym przypadku wyszło mi 1043)
- Sprawdzamy kilka kolejnych stron – jak zmienia się link?
- Oglądamy dokładnie HTMLa, najlepiej i najwygodniej przy użyciu jakiejś wtyczki typu WebDeveloper czy po prostu “Zbadaj” w Chrome
Jak to wszystko wiemy – krok o kroku próbujemy wyłuskać poszczególne pola ze strony. Próbujemy z jednym polem (np. cena mieszkania) – sprawdzamy na stronie czy otrzymane wyniki są zgodne z tym co “wyłuskaliśmy”. Potem z drugim polem. I tak do końca.
Dla Gratki na obecną chwilę (do HTML może się zmienić ze zmianami w serwisie) działający kod wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
## pobranie danych url_pre <- "https://dom.gratka.pl/mieszkania-do-wynajecia/lista/,,1,40," url_post <- ",1,1,co,li,s,mo,lpo.html" oferty <- data.frame(stringsAsFactors = F) for(i in 1:1043) { url <- paste0(url_pre, i, url_post) page <- read_html(url) %>% html_node("div.wrapper") %>% html_node("div#ogloszenia") %>% html_node("div.small-12") %>% html_node("ul#list-ads") %>% html_nodes("li.ogloszenie") # ceny cena <- page %>% html_node("div.detailedPrice") %>% html_node("p") %>% html_node("b") %>% html_text() %>% gsub(" ", "", .) %>% as.numeric() # lokalizacje lokalizacja <- page %>% html_nodes("div.ogloszenieInfo") %>% html_node("h2") %>% html_text() tmp <- page %>% html_nodes("div.ogloszenieInfo") %>% html_node("div.infoLista") %>% html_node("p.infoDane") liczba_pokoi <- tmp %>% html_text() %>% strsplit(split = ",") %>% do.call(rbind, .) %>% .[,1] powierzchnia <- tmp %>% html_nodes("span") %>% html_nodes("b") %>% html_text() %>% gsub(",", ".", .) %>% as.numeric() pietro <- tmp %>% html_text() %>% strsplit(split = ",") %>% do.call(rbind, .) %>% .[,2] oferty <- rbind(oferty, data.frame(lokalizacja, cena, powierzchnia, liczba_pokoi, pietro, stringsAsFactors = F)) } |
Po pobraniu danych czas na drugą kwestię typu “pain in the ass”, czyli
czyszczenie danych
i ewentualne dodanie wartości wyliczonych na potrzeby dalszych analiz.
Rozdzielimy na początek pole z lokalizacją na miasto, dzielnicę i ulicę (z której akurat tutaj nie będziemy korzystać – ale można użyć kombinacji miasto-ulica do zaznaczenia punktów (ogłoszeń) na mapie, korzystając z GoogleMaps).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
oferty <- separate(oferty, lokalizacja, into=c("miasto", "ulica"), sep =",") oferty$miasto <- gsub("Mieszkanie ", "", oferty$miasto) oferty$dzielnica <- substr(oferty$miasto, lapply(oferty$miasto, function(x) as.numeric(str_locate(x, " ")[2])+1), nchar(oferty$miasto)) oferty$miasto <- substr(oferty$miasto, 1, lapply(oferty$miasto, function(x) as.numeric(str_locate(x, " ")[1])-1)) # wybieramy tylko potrzebne kolumny oferty <- oferty[,c(1,7,2,3,4,5,6)] # dodajemy średnią cenę za metr kwadratowy oferty$cena_za_metr <- oferty$cena / oferty$powierzchnia # usuwamy braki oferty <- na.omit(oferty) # tylko do 6 pokoi oferty$liczba_pokoi <- gsub("\\W", " ", oferty$liczba_pokoi) oferty <- filter(oferty, liczba_pokoi %in% c("1 pokój", "2 pokoje", "3 pokoje", "4 pokoje", "5 pokoi", "6 pokoi")) oferty$liczba_pokoi <- factor(oferty$liczba_pokoi, levels = c("1 pokój", "2 pokoje", "3 pokoje", "4 pokoje", "5 pokoi", "6 pokoi")) |
Uff… tyle na temat pobierania. Na dzień dobry mamy 28023 oferty. Liczba ofert w poszczególnych miastach wygląda następująco (długa tabela):
miasto | n |
---|---|
Warszawa | 14422 |
Kraków | 4998 |
Katowice | 1528 |
Poznań | 1364 |
Łódź | 1134 |
Wrocław | 1104 |
Gdańsk | 807 |
Szczecin | 440 |
Lublin | 352 |
Gdynia | 298 |
Toruń | 278 |
Gliwice | 256 |
Rzeszów | 158 |
Sopot | 155 |
Kielce | 98 |
Olsztyn | 83 |
Dąbrowa | 80 |
Sosnowiec | 67 |
Bydgoszcz | 46 |
Białystok | 38 |
Zabrze | 30 |
Chorzów | 26 |
Będzin | 23 |
Tychy | 22 |
Częstochowa | 19 |
Opole | 18 |
Wałbrzych | 16 |
Bielsko-Biała | 14 |
Słupsk | 14 |
Mińsk | 12 |
Nowy | 12 |
Tarnowskie | 11 |
Bytom | 10 |
Mysłowice | 10 |
Zielona | 10 |
Bielany | 8 |
Pruszków | 8 |
Tarnów | 8 |
Gniezno | 7 |
Piotrków | 7 |
Tczew | 7 |
Jaworzno | 6 |
Pruszcz | 6 |
Ruda | 6 |
Siemianowice | 4 |
Świętochłowice | 3 |
Trochę za dużo, ostatnie miasta na liście mają po kilka ogłoszeń – może zepsuć nam to statystykę.
Ograniczenie ilości danych
Zostawimy więc tylko miasta z dużą ilością ofert (więcej niż trzeci kwartyl).
Dodatkowo odrzucamy skrajne 5% cen za metr (za dużo i za mało) – po przeglądzie danych w tabeli oferty widać tam ewidentne błędy użytkowników, które na szybko można podzielić na dwa typy:
- mieszkania w cenie kilkuset tysięcy – to raczej sprzedaż niż wynajem
- jakieś minimalne ceny – podejrzewam że może chodzić o wynajęcie jednego pokoju za kilkaset złotych w na przykład trzypokojowym mieszkaniu; wówczas mamy podaną wielkość mieszkania i cenę pokoju.
1 2 3 4 5 6 7 8 9 |
# tylko miasta z dużą ilością ofert lista_miast <- count(oferty, miasto) %>% filter(n >= quantile(n, 0.75)) # odrzucamy skrajne 5% cen za metr (za dużo i za mało) # i zostawiamy tylko miasta z 75% ofert oferty <- filter(oferty, miasto %in% lista_miast$miasto, cena_za_metr >= quantile(oferty$cena_za_metr, 0.05), cena_za_metr <= quantile(oferty$cena_za_metr, 0.95)) |
Po odrzuceniu zostaje nam 24445 ofert, w podziale na miasta wygląda to tak:
miasto | n |
---|---|
Warszawa | 13173 |
Kraków | 4656 |
Katowice | 1460 |
Poznań | 1172 |
Wrocław | 1011 |
Łódź | 912 |
Gdańsk | 755 |
Szczecin | 368 |
Lublin | 312 |
Gdynia | 247 |
Gliwice | 199 |
Toruń | 180 |
Dane mamy w pełni przygotowane. Teraz mięsko, czyli
Analiza
Na początek szybki przegląd rozkładów. Na kilku poniższych wykresach pionowa czerwona linia to średnia, a czarna – mediana. Na podstawie tych rozkładów widać, dlaczego wybieramy w późniejszych analizach medianę – średnie są większe od mediany dla wszystkich danych, co trochę może wprowadzać w błąd przy wnioskowaniu.
Zobaczmy wielkości mieszkań:
1 2 3 4 |
ggplot(oferty) + geom_density(aes(powierzchnia), fill = "lightgreen") + geom_vline(xintercept = median(oferty$powierzchnia)) + geom_vline(xintercept = mean(oferty$powierzchnia), color="red") |
Połowa mieszkań wystawionych na wynajem ma powierzchnię mniejszą niż 57 m2 (oczywiście druga połowa ma większą). Ciekawe jak to ma się do rozkładu wielkości wszystkich mieszkań? Trzeba by sprawdzić w danych GUSu, pewnie ze spisów powszechnych. Proszę sprawdzić i napisać w komentarzu – w końcu chcę Was czegoś nauczyć, poszukiwanie informacji to jedna z najważniejszych umiejętności.
Znamy wielkość, a jak z ceną za całe mieszkanie?
1 2 3 4 |
ggplot(oferty) + geom_density(aes(cena), fill = "lightgreen") + geom_vline(xintercept = median(oferty$cena)) + geom_vline(xintercept = mean(oferty$cena), color="red") |
Mediana to 2600 złotych. Jeszcze tylko cena za metr kwadratowy – bo taka jednostka wydaje się najbardziej miarodajna przy porównywaniu ofert w poszczególnych miastach.
1 2 3 4 |
ggplot(oferty) + geom_density(aes(cena_za_metr), fill = "lightgreen") + geom_vline(xintercept = median(oferty$cena_za_metr)) + geom_vline(xintercept = mean(oferty$cena_za_metr), color="red") |
Na pierwszy rzut oka rozkład wygląda sensownie – po równo po obu stronach 46.56 zł/m2. Prawą stronę górki robi Warszawa – proszę sprawdzić ten sam rozkład dla ofert tylko z Warszawy (filtr na miasto).
Mamy jeszcze dane o ilości pokoi oraz o piętrze, na którym znajduje się mieszkanie. Te ostatnie jednak są dość kiepskiej jakości (w 13% to błędy, to wynik pobierania danych), ale z tego co mamy – 10 najbardziej popularnych to:
Piętro | Procent ofert |
---|---|
2 piętro z 4 | 6.44 |
1 piętro z 4 | 5.95 |
3 piętro z 4 | 4.99 |
1 piętro z 3 | 4.54 |
4 piętro z 4 | 3.73 |
2 piętro z 3 | 3.68 |
3 piętro z 3 | 3.57 |
3 piętro z 5 | 2.93 |
2 piętro z 5 | 2.81 |
1 piętro z 2 | 2.76 |
To jak z tymi pokojami?
Liczba pokoi | Procent |
---|---|
1 pokój | 8.7 |
2 pokoje | 48.7 |
3 pokoje | 30.9 |
4 pokoje | 9.0 |
5 pokoi | 2.2 |
6 pokoi | 0.4 |
1 2 3 4 5 6 7 8 9 10 |
oferty %>% group_by(liczba_pokoi) %>% summarise(n=n()) %>% mutate(p=round(100*n/sum(n), 1)) %>% ungroup() %>% ggplot() + geom_bar(aes(1, p, fill=liczba_pokoi), stat="identity") + coord_polar(theta="y") + labs(fill="Liczba pokoi") + theme_void() |
Najwięcej do wynajęcia jest mieszkań 2- (prawie połowa ofert!) i 3- pokojowych. Takich też zapewne najwięcej się buduje (i “po babci” zostało) – znowu do sprawdzenia w GUSie.
Zobaczmy jak to wygląda w poszczególnych miastach?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# rozkład procentowy po ilości pokoi oferty %>% group_by(miasto, liczba_pokoi) %>% summarise(n=n()) %>% mutate(p=100*n/sum(n)) %>% ungroup() %>% mutate(liczba_pokoi = factor(liczba_pokoi, levels=rev(c("2 pokoje", "3 pokoje", "1 pokój", "4 pokoje", "5 pokoi", "6 pokoi")))) %>% ggplot() + geom_bar(aes(miasto, p, fill=liczba_pokoi), stat="identity", color="black") + coord_flip() + labs(fill="Liczba pokoi", y="") + theme(legend.position = "bottom") + geom_hline(yintercept = c(25, 50, 75, 90, 95, 100)) + scale_y_continuous(breaks = c(25, 50, 75, 90, 95, 100)) |
Zgodnie z oczekiwaniami – we wszystkich miastach 75% (lub więcej) to mieszkania dwu- i trzypokojowe. Ponadprzeciętne wartości to:
- w Warszawie czteropokojowe
- w Gliwicach, delikatnie mniej Szczecin i Poznań – jednopokojowe
Wiemy o liczbie pokoi w zależności od miasta, a czy z powierzchnią jest podobnie? Najpierw podzielimy powierzchnię na grupy: poniżej 38 m2, 38-60 m2, 60-90 m2 oraz ponad 90 m2:
1 2 |
oferty$powierzchnia_kat <- cut(oferty$powierzchnia, breaks = c(0, 38, 60, 90, 1000)) |
I od razu zobaczmy jak to wygląda w poszczególnych miastach:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
oferty %>% group_by(miasto, powierzchnia_kat) %>% summarise(n=n()) %>% mutate(p=100*n/sum(n)) %>% ungroup() %>% mutate(powierzchnia_kat = factor(powierzchnia_kat, levels=rev(levels(powierzchnia_kat)))) %>% ggplot() + geom_bar(aes(miasto, p, fill=powierzchnia_kat), stat="identity") + coord_flip() + labs(y="", fill="Powierzchnia:") + scale_fill_manual(labels=c("ponad 90 m2", "60-90 m2", "38-60 m2", "poniżej 38 m2"), values=c("#44ff44", "#44aa44", "#448844", "#444444")) + geom_hline(yintercept = c(25, 50, 75, 90, 100)) + scale_y_continuous(breaks = c(25, 50, 75, 90, 100)) |
Mamy potwierdzenie (nie wprost i wcale nie muszą to być zależne od siebie wartości – to statystyczna pułapka, w którą łatwo wpaść; przykład do znane wykresy pokazujące liczbę filmów z Cage’em versus liczba samobójstw – zbieżność nie musi oznaczać przyczynowości) poprzedniej obserwacji – w Warszawie jest więcej większych mieszkań (tych 4-pokojowych) – 25% to mieszkania ponad 90m2.
Teraz sprawdźmy zależności – czy większe mieszkanie jest droższe? Czy liczba pokoi ma jakieś znaczenie dla ceny? Czy liczba pokoi zależy od powierzchni mieszkania?
Na wszystkie te pytania można intuicyjnie odpowiedzieć tak, ale zobaczmy potwierdzenie w danych:
1 2 3 4 5 6 |
# cena zależna od ilości pokoi ggplot(oferty) + geom_boxplot(aes(liczba_pokoi, cena, group=liczba_pokoi), color="black", fill="lightgreen") + labs(title="Cena w zależności od liczby pokoi", x="Liczba pokoi", y="Cena") |
1 2 3 4 5 6 |
# wielkość w zależności od ilości pokoi ggplot(oferty) + geom_boxplot(aes(liczba_pokoi, powierzchnia, group=liczba_pokoi), color="black", fill="lightgreen") + labs(title="Powierzchnia w zależności od liczby pokoi", x="Liczba pokoi", y="Powierzchnia mieszkania") |
Jeszcze tylko rzucimy okiem na cenę mieszkania w zależności od ilości pomieszczeń, ale w rozbiciu na poszczególne miasta:
1 2 3 4 5 6 7 |
# j/w w rozbiciu na miasta ggplot(oferty) + geom_boxplot(aes(liczba_pokoi, cena/1000, group=liczba_pokoi), color="black", fill="lightgreen") + facet_wrap(~miasto, scales = "free_x", ncol = 3) + labs(x="Liczba pokoi", y="Cena w tys.") + coord_flip() |
Tutaj widać interesujące rzeczy. Patrząc tylko na wykresy, bez zaglądania w dane i przede wszystkim ilość ofert w każdej z kategorii (przekrój miasto i liczba pokoi) widać:
- w Gdyni różnica w cenie pomiędzy 5 a 6 pokoi właściwie nie istnieje
- podobnie jest w Lublinie dla 4- i 5-pokojowych
- w Katowicach pięciopokojowe mogą okazać się tańsze niż czteropokojowe
- jeszcze bardziej widoczne jest to w Poznaniu dla 5- i 6-pokojowych
Bez dokładnej analizy danych, chociażby o standardzie i przede wszystkim lokalizacji w ramach miasta, powyższe wnioski mogą okazać się nieprawdziwe.
Gdzieś tam na górze napisałem, że najbardziej sensowną i dającą najlepsze porównanie jest cena za metr kwadratowy. Sprawdźmy to, razem z całą drogą do potwierdzenia tej tezy.
1 2 3 4 5 |
# cena zależna od wielkości mieszkania ggplot(oferty) + geom_point(aes(powierzchnia, cena), alpha=0.1) + geom_smooth(aes(powierzchnia, cena)) + labs(x="Powierzchnia", y="Cena") |
Widać prawie liniową zależność (współczynnik korelacji to 0.89), która jest intuicyjna: im większa chata, tym więcej trzeba płacić. To prowadzi do wniosku, że jednostkowa cena metra kwadratowego powinna być w miarę stała. Zobaczmy:
1 2 3 4 |
ggplot(oferty) + geom_point(aes(powierzchnia, cena_za_metr), alpha=0.1) + geom_smooth(aes(powierzchnia, cena_za_metr)) + labs(x="Powierzchnia", y="Cena za m2") |
Okazuje się jak widać, że niekoniecznie metr kwadratowy kosztuje zawsze tyle samo. To ma sens – najwięcej jest mieszkań o powierzchni w okolicy 45-55 m2 (rozkład z pierwszego wykresu), a więc i konkurencja w tym przedziale największa. Najprostszą metodą wygrania na rynku z konkurencją jest cena.
Spróbujmy przybliżyć fragment z minimum i coś z niego odczytać:
1 2 3 4 5 6 |
oferty %>% ggplot() + geom_point(aes(powierzchnia, cena_za_metr), alpha=0.1) + geom_smooth(aes(powierzchnia, cena_za_metr)) + coord_cartesian(xlim=c(40, 70), ylim=c(40, 50)) + labs(x="Powierzchnia", y="Cena za m2") |
Z wykresu odczytać można minimum dla 55 m2 przy cenie 45 zł/m2. A z obliczeń
1 2 3 4 5 6 7 8 9 |
# średnia powierzchnia - globalnie sr_pow_global <- mean(oferty$powierzchnia) # średnia cena za metr - globalnie sr_cena_za_metr_global <- mean(oferty$cena_za_metr) # mediany med_pow_global <- median(oferty$powierzchnia) med_cena_za_metr_global <- median(oferty$cena_za_metr) |
wychodzi:
średnie:
- powierzchnia: 67.28 m2
- cena za metr: 47.21 zł/m2
mediany:
- powierzchnia: 57 m2
- cena za metr: 46.56 zł/m2
Jak wygląda rozkład tego parametru dla wszystkich (no, tych 12 które zostały po czyszczeniu danych) miast?
Liczba pokoi | średnia | mediana | minimum | maksimum |
---|---|---|---|---|
1 pokój | 46.77 | 45.71 | 26.47 | 72.22 |
2 pokoje | 46.89 | 46.21 | 26.47 | 72.31 |
3 pokoje | 47.02 | 46.40 | 26.47 | 72.29 |
4 pokoje | 48.91 | 48.00 | 26.50 | 72.10 |
5 pokoi | 51.24 | 52.67 | 26.68 | 72.22 |
6 pokoi | 50.29 | 50.00 | 28.00 | 71.43 |
1 2 3 4 5 6 7 |
ggplot(oferty) + geom_boxplot(aes(liczba_pokoi, cena_za_metr, group=liczba_pokoi), color="black", fill="lightgreen") + labs(x="Liczba pokoi", y="Cena za m2") + scale_y_continuous(breaks = seq(20, 80, 5)) + geom_hline(yintercept = sr_cena_za_metr_global, color="red") + geom_hline(yintercept = med_cena_za_metr_global) |
Jak widać zarówno wartości leżą wokół średniej (47.21 zł/m2 – linia czerwona na wykresie) jak i mediany (46.56 zł/m2) dla mieszkań do trzech pokoi. Trochę drożej “za jednostkę” robi się dla mieszkań większych, co było już widać na wykresie, który powiększaliśmy :)
Pierwszy poważny wniosek z tej całej analizy jest taki, że jeśli masz do wynajęcia (albo chcesz wynająć) najpopularniejze mieszkanie dwupokojowe około 50 m2 to powinno ono kosztować jakieś 47 zł/m2 czyli około 2350 zł. Czy więcej czy mniej – zależy od lokalizacji.
No to ile w Warszawie, ile w Poznaniu, a ile w Gliwicach? No ile?
Przygotujmy średnie (i mediany) dla ofert według miast i zobaczmy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
miasta <- oferty %>% group_by(miasto) %>% mutate(l_ofert = n()) %>% mutate(sr_pow = mean(powierzchnia)) %>% mutate(sr_cena = mean(cena)) %>% mutate(sr_cena_za_metr = mean(cena_za_metr)) %>% mutate(med_pow = median(powierzchnia)) %>% mutate(med_cena = median(cena)) %>% mutate(med_cena_za_metr = median(cena_za_metr)) %>% ungroup() %>% select(miasto, l_ofert, sr_pow, sr_cena, sr_cena_za_metr, med_pow, med_cena, med_cena_za_metr) %>% unique() |
Miasto | Liczba ofert | Średnia pow. | Średnia cena | Średnia cena m2 | Mediana pow. | Mediana cena | Mediana cena m2 |
---|---|---|---|---|---|---|---|
Warszawa | 13173 | 77.00 | 4024.71 | 52.72 | 67.0 | 3400 | 52.73 |
Gdańsk | 755 | 57.41 | 2619.10 | 45.97 | 51.0 | 2300 | 45.45 |
Wrocław | 1011 | 58.18 | 2457.93 | 42.67 | 53.0 | 2200 | 41.67 |
Kraków | 4656 | 56.96 | 2392.83 | 42.16 | 50.0 | 2000 | 40.32 |
Gdynia | 247 | 64.93 | 2661.94 | 40.85 | 55.0 | 2200 | 37.00 |
Katowice | 1460 | 53.52 | 2084.01 | 39.99 | 50.0 | 1950 | 39.27 |
Poznań | 1172 | 51.69 | 1977.59 | 38.48 | 48.0 | 1700 | 36.76 |
Lublin | 312 | 57.16 | 2129.42 | 38.07 | 54.0 | 1950 | 36.08 |
Łódź | 912 | 57.48 | 2072.10 | 36.81 | 52.0 | 1850 | 35.00 |
Szczecin | 368 | 54.22 | 1957.79 | 36.70 | 47.6 | 1850 | 34.76 |
Gliwice | 199 | 49.21 | 1711.50 | 35.46 | 45.0 | 1500 | 33.77 |
Toruń | 180 | 45.78 | 1485.99 | 32.75 | 41.3 | 1350 | 31.58 |
Jest pytanie – jest odpowiedź:
Nasze 50 m2 w Warszawie powinno kosztować 2600 zł, w Poznaniu 1800 zł, a w Gliwicach 1700 zł.
Zobaczmy tabelkę na wykresie. Na wykresie wszystko wygląda ładniej, chyba że wykres jest spierniczony. Wówczas można się do niego poprzypierniczać.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# mediana cena za metr wg miasta miasta %>% arrange(med_cena_za_metr) %>% mutate(miasto=factor(miasto, levels=miasto)) %>% ggplot() + geom_bar(aes(miasto, med_cena_za_metr, fill=miasto), stat="identity", show.legend = FALSE) + geom_text(aes(miasto, med_cena_za_metr, label=round(med_cena_za_metr, 1)), hjust=1.2, vjust=0.2) + geom_hline(yintercept = sr_cena_za_metr_global, color="red") + geom_hline(yintercept = med_cena_za_metr_global) + labs(x="", y="Cena za metr kwadratowy (mediana)") + coord_flip() |
Ameryki nie odkryliśmy – Warszawa jest najdroższa, potem są kolejne duże miasta. Sądziłem szczerze mówiąc, że Gdańsk będzie gdzieś w okolicach 4-5 miejsca, a nie drugi. Znaczy: można się czegoś dowiedzieć, zdziwić. Watro analizować. A że Toruń tańszy niż Łódź to totalne zaskoczenie.
Wiemy już o cenach, a gdzie są największe mieszkania do wynajęcia? Teoretycznie wszędzie powinno być w miarę podobnie, prawda?
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# srednia powierzchnia wg miasta miasta %>% arrange(med_pow) %>% mutate(miasto=factor(miasto, levels=miasto)) %>% ggplot() + geom_bar(aes(miasto, med_pow, fill=miasto), stat="identity", show.legend = FALSE) + geom_text(aes(miasto, med_pow, label=round(med_pow, 1)), hjust=1.2, vjust=0.2) + geom_hline(yintercept = sr_pow_global, color="red") + geom_hline(yintercept = med_pow_global) + labs(x="", y="Powierzchnia (mediana)") + coord_flip() |
I tu kolejne zaskoczenie – w Warszawie to mają wielkie mieszkania, po 70 metrów panie! Się powodzi w tej stolicy. Ale tak jest – mediana ma prawo być tak (stosunkowo) wysoka, bo sporo jest mieszkań powyżej 80 m2, kilkanaście nawet ponad 200 m2. Procentowo (w przedziałach co 25 m2) wygląda to tak:
Skoro wiemy jak różnią się miasta pomiędzy sobą, to czy w samych miastach są różnice i jak one są duże?
Trzeba pogrupować dane analogicznie jak wcześniej, tyle że schodzimy na poziom miast i dodatkowo dzielnic, zostawiając tylko cenę za metr kwadratowy oraz liczbę ofert z danej dzielnicy.
1 2 3 4 5 6 7 8 |
# średnie parametry wg dzielnic dzielnice <- oferty %>% group_by(miasto, dzielnica) %>% mutate(l_ofert = n()) %>% mutate(med_cena_za_metr = median(cena_za_metr)) %>% ungroup() %>% select(miasto, dzielnica, l_ofert, med_cena_za_metr) %>% unique() |
Ceny mieszkań w Warszawie
Jaki jest wynik? Najpierw Warszawa. Bo tutaj mieszkam i mogę skomentować wynik :)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
dzielnice %>% filter(miasto=="Warszawa") %>% filter(l_ofert > quantile(l_ofert, 0.75)) %>% arrange(med_cena_za_metr) %>% mutate(dzielnica=factor(dzielnica, levels=dzielnica)) %>% mutate(mediana = mean(med_cena_za_metr)) %>% ggplot() + geom_bar(aes(dzielnica, med_cena_za_metr, fill=dzielnica), stat="identity", show.legend = FALSE) + geom_text(aes(dzielnica, med_cena_za_metr, label=round(med_cena_za_metr, 1)), hjust=1.2, vjust=0.2, size=3) + geom_text(aes(dzielnica, 1, label=l_ofert), hjust=0, vjust=0.2, size=3) + geom_hline(aes(yintercept = mediana)) + coord_flip() + labs(x="Dzielnica", y="Cena za metr kwadratowy (mediana dla dzielnicy)") + theme(legend.position = "none") |
Dziwnie i podejrzanie wygląda ta Praga Północ na początku. Bierze się to z różnych sposobów zapisania nazwy dzielnicy (widać to też przy innych dzielnicach – Śródmieście czy Mokotów w kilku wariacjach). Jeśli ręcznie poprawimy dane (albo: wybierzemy tylko te rekordy, gdzie dzielnica w jakiś sposób przypomina ciąg “Praga Północ”) dostaniemy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
oferty %>% filter(miasto=="Warszawa", dzielnica %in% c("Praga-Północ", "Praga Północ", "Praga-Północ Praga")) %>% select(cena_za_metr) %>% summary() ## cena_za_metr ## Min. :28.12 ## 1st Qu.:43.75 ## Median :49.12 ## Mean :51.54 ## 3rd Qu.:61.90 ## Max. :70.59 |
medianę ceny za metr kwadratowy równą nieco ponad 49 złotych, co jest o wiele bardziej prawdopobne niż prawie 61 zł/m2, które widać na wykresie.
Poza północną Pragą dane mają sens – zawsze Śródmieście czy też Centrum jest najdroższe (w każdym właściwie mieście). Mokotów to dobra lokalizacja – wszędzie blisko, metro pod nosem, mi osobiście nie podobają się budynki. Wola analogicznie do Mokotowa, a do tego metro już jest albo za chwilę będzie. Z drugiej strony są blokowiska – Ursynów, Bemowo, Targówek czy Białołęka. Wesoła i Wawer to już prawie poza miastem, plusem Wesołej jest SKM.
Za ile powinno być wynajmowane konkretne mieszkanie?
Za przykład weźmy warszawską Białołękę i od razu zróbmy stosowną tabelkę (można wydrukować, wyciąć i schować do portfela):
Liczba pokoi | Powierzchnia | Cena (średnia) | Cena (mediana) | Liczba ofert |
---|---|---|---|---|
1 pokój | (35,40] | 1677 | 1590 | 3 |
2 pokoje | (30,35] | 2250 | 2250 | 2 |
2 pokoje | (35,40] | 1772 | 1700 | 9 |
2 pokoje | (40,45] | 1750 | 1800 | 6 |
2 pokoje | (45,50] | 2019 | 2100 | 13 |
2 pokoje | (50,55] | 2055 | 2150 | 10 |
2 pokoje | (55,60] | 1975 | 1925 | 6 |
2 pokoje | (65,70] | 1900 | 1900 | 1 |
3 pokoje | (50,55] | 2500 | 2500 | 1 |
3 pokoje | (55,60] | 2750 | 2750 | 2 |
3 pokoje | (60,65] | 2750 | 2650 | 4 |
3 pokoje | (65,70] | 2520 | 2500 | 15 |
Mieszkanie 45m2 na Białołęce powinno być wynajmowane za 1750 zł. Jeśli ktoś chce więcej – nie warto. Jeśli mniej – okazja! ;) Oczywiście zależy od standardu i lokalizacji. Jest 6 takich mieszkań, warto zobaczyć wszystkie.
Patrząc na oferty – Tachomin i Aluzyjna w podobnej cenie, Odkryta nieco tańsze. I tego nie rozumiem (tej Aluzyjnej), ale to już mocno lokalna dygresja.
Ceny mieszkań w innych miastach
Ten sam kod wykonujemy dla pozostałych miast. I tutaj uwaga – też mamy błędnie wpisane nazwy dzielnic. Aby pozyć się tego typu wartości należałoby po wczytaniu danych porządnie je przeczyścić, niestety w dużym stopniu ręcznie (wyszukując i zamieniając odpowiednie ciągi znaków). Gratka mogłaby wprowadzić słowniki… nie jest to trudne i kosztowne.
Jeśli mieszkasz w którymś z miast – zapewne zobaczysz to, co ja widzę w Warszawie i Krakowie (znam topografię, wiem gdzie która dzielnica i co tam jest).
Następnym razem też zrobimy coś fajnego. Może nawet fajniejszego. Ale już tylko o Warszawie. I rowerach.
A na koniec nagranie, które jest tak stare jak internet. Do tego nagrania właśnie nawiązuje początek tego tekstu.
Extreme Parental Advisory.
Świetna analiza. Wygląda dużo lepiej niż na niejedna wykonana przez „specjalistę” z branży.
Świetna analiza ale może być jeden błąd. Mianowicie w niektórych ogłoszeniach cena zawiera opłaty a w innych nie.
The Real Person!
The Real Person!
Zgadza się. Ale nie chciałem komplikować całości – w gruncie rzeczy w moich postach chodzi o pokazanie jak coś zrobić. Jednocześnie to coś (chciałbym, żeby tak było) powinno być na tyle interesujące, że nawet laik programowania czy analiz jest w stanie zrozumieć o co chodzi i zobaczyć ciekawostki w „świecie wokół nas”.
Amazing work ! Swietny material !
Pysznie się to czyta. Dzięki
a jak użyć tego kodu, jaki to język? mógłbyś wytłumaczyć, gdyby jakiś zielony chciałby sobie zrobić analizę, dla mniejszego miasta?
The Real Person!
The Real Person!
Trzeba pobrać dane. Później nie filtrować ich po miastach z największą ilością ofert tylko zostawić wszystko. Tabelkę oferty należy przejrzeć pod kątem owego mniejszego miasta. Być może Gratka nie ma żadnych ofert dla tej miejscowości? Tego nie wiem.
Jeśli jesteś totalnym laikiem w R to najpierw poznaj ten język. Jak napisałem w jednym z wcześniejszych postów – „nie zamierzam prowadzić tutaj jakiegoś kursu, a publikowane kody źródłowe będą raczej dla zaznajomionych z R”. Mnie interesuje analiza danych i wynik tej analizy. Kody źródłowe są przy okazji (to wartość dodana) – blogów analitycznych jest sporo, z mechaniką przeprowadzenia analizy mało.
jestem totalnym laikiem :) dzięki
Niesamowite.
Czy analiza uwzględnia stronę popytu? Wziąłeś stronę podażową i wyciągasz wnioski odnośnie ceny po jakiej powinno być mieszkanie wynajmowane. Nie bez powodu te mieszkania stoją puste i są do wynajęcia. Prześledź jeszcze jak długo konkretne oferty są dostępne. Wypadałoby oddzielić oferty z mediami i bez.
The Real Person!
The Real Person!
I o takie komentarze mi najbardziej chodzi.
W 100% masz rację. Pokazuję tutaj mechanikę, jakiś wstęp do rzeczowej analizy. Nikt nie płaci mi za poświęcony czas, a wszystkie wpisy związane z analizami jakie pojawiły (i pojawią) się na blogu są formą uczenia się (learning by doing).
Rzetelna analiza rynku wynajmowanych mieszkań powinna zawierać dodatkowo przede wszystkim standard tych mieszkań, kwestie czy media są wliczone w koszt czy nie. Cena „za ile powinno być” jest ceną transakcyjną – tego w ogłoszeniach nie znajdę. Ile czasu wisi ogłoszenie? To pytanie czy serwis ogłoszeniowy (albo ogłoszeniodawca) zdejmuje oferty nieaktualne. Tego nie wiem, ale też nie sprawdzałem – biorę listę ofert „na moment”, nie badam ich historii itp. To kosztuje. Ja pokazuję jak to robić i jak do tego się zabrać.
Przy tych cenach za wynajem np. W Warszawie – to raczej się opłaca lepiej kupić już mieszkanie na raty (po paru latach będzie nasze), a nie ładujemy te 2000 ziko komuś obcemu w kieszeń.
Mógłbyś zrobić analize jak to wygląda w przypadku kupna, a nie wynajmu?
Analiza rynku sprzedaży zamiast wynajmu jest do zrobienia dokładnie w ten sam sposób. Jedyna różnica to adres strony z listą ofert. Kod potrzebny jest powyżej, R i odpowiednie biblioteki są dostępne i darmowe. Zapraszam do zabawy.
Ja celuję już w inne tematy :)
Pingback: CSI: Lemur - znajdź mój dom | Łukasz Prokulski
Pingback: Wybór odpowiedniego algorytmu. Część 3 – algorytmy regresyjne | Mateusz Grzyb
Interesowałeś się kwestiami prawnymi związanymi z takim pozyskiwaniem danych? Np. w regulaminie otodom jest wyraźnie napisane, że nie wolno bez pisemnej zgody pobierać, przetwarzać i agregować danych. Tak mnie ciekawi temat, bo sam chciałem podobnego coś zrobić, ale mam lekkie opory czytając regulamin.
The Real Person!
The Real Person!
Szczerze mówiąc nie wiem. Jak na razie nikt nie reagował, z tego co wiem to nawet nie banują adresów IP crawlujących strony.
Panie Łukaszu, czy jest szansa na dorzucenie tego kodu do publicznego repo na Githubie? Podobna prośba w odniesieniu do kodu z danymi z Otomoto.pl :)