O Airbnb já é considerado como sendo a maior empresa hoteleira da atualidade. O detalhe é que ele não possui nenhum hotel!
Conectando pessoas que querem viajar (e se hospedar) com anfitriões que querem alugar seus imóveis de maneira prática, o Airbnb fornece uma plataforma inovadora para tornar essa hospedagem alternativa.
No final de 2018, a Startup fundada 10 anos atrás, já havia hospedado mais de 300 milhões de pessoas ao redor de todo o mundo, desafiando as redes hoteleiras tradicionais.
Uma das iniciativas do Airbnb é disponibilizar dados do site, para algumas das principais cidades do mundo. Por meio do portal Inside Airbnb, é possível baixar uma grande quantidade de dados para desenvolver projetos e soluções de Data Science.
Neste notebook, iremos analisar os dados referentes ao Distrito de Lisboa, e ver quais insights podem ser extraídos a partir de dados brutos.
Lisboa é a capital de Portugal e é uma das cidades mais carismáticas e vibrantes da Europa. É uma cidade que mistura o patrimônio tradicional sem esforço, com um impressionante modernismo. Como um destino de férias, Lisboa oferece uma história rica e variada, uma agitada vida noturna e é abençoada com um clima glorioso durante o ano todo.
Lisboa é uma cidade movimentada e animada, possuindo uma grande variedade de atividades e atrações turísticas fascinantes. A cidade tem uma atmosfera acolhedora e liberal, enquanto ainda abraça a sua herança profundamente enraizada e história vasta. Lisboa irá atrair uma seleção diversificada de idades e turistas; pode formar uma viagem cultural, uma extravagância da vida noturna, férias em família, uma pausa relaxante na cidade ou até mesmo como base para umas férias na praia.
A capital portuguesa é reconhecida constantemente como uma das melhores cidades do mundo, uma afirmação confirmada por “Lonely Planet Guides”, que nomeou Lisboa como uma das 10 melhores cidades do mundo. Surpreendentemente, Lisboa ainda é uma das capitais menos visitadas da Europa, mas isso está mudando rapidamente à medida que novos visitantes descobrem o fascínio de Portugal.
Fonte: https://lisbonlisboaportugal.com/lisboa-portugal-guia-br.html
Os dados usados foram obtidos do site Inside Airbnb, com a data de compilação em 28 de maio de 2020.
# Iremos importar os pacotes necessários
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# importando o arquivo listings.csv para um DataFrame
df = pd.read_csv("http://data.insideairbnb.com/portugal/lisbon/lisbon/2020-04-29/visualisations/listings.csv")
Dicionário das variáveis
id
- número de id gerado para identificar o imóvelname
- nome da propriedade anunciadahost_id
- número de id do proprietário (anfitrião) da propriedadehost_name
- Nome do anfitriãoneighbourhood_group
- municípioneighbourhood
- bairrolatitude
- coordenada da latitude da propriedadelongitude
- coordenada da longitude da propriedaderoom_type
- informa o tipo de quarto que é oferecidoprice
- preço para alugar o imóvelminimum_nights
- quantidade mínima de noites para reservarnumber_of_reviews
- número de reviews que a propriedade possuilast_review
- data do último reviewreviews_per_month
- quantidade de reviews por mêscalculated_host_listings_count
- quantidade de imóveis do mesmo anfitriãoavailability_365
- número de dias de disponibilidade dentro de 365 diasAntes de iniciar qualquer análise, vamos verificar a cara do nosso dataset, analisando as 5 primeiras entradas.
# mostrar as 5 primeiras entradas
df.head()
id | name | host_id | host_name | neighbourhood_group | neighbourhood | latitude | longitude | room_type | price | minimum_nights | number_of_reviews | last_review | reviews_per_month | calculated_host_listings_count | availability_365 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 6499 | Belém 1 Bedroom Historical Apartment | 14455 | Bruno | Lisboa | Belm | 38.69750 | -9.19768 | Entire home/apt | 40 | 3 | 26 | 2020-01-03 | 0.38 | 1 | 307 |
1 | 25659 | Sunny, Alfama Sleeps 3 - Coeur d'Alfama | 107347 | Ellie | Lisboa | Santa Maria Maior | 38.71167 | -9.12696 | Entire home/apt | 60 | 3 | 113 | 2019-12-08 | 1.54 | 1 | 317 |
2 | 29248 | Apartamento Alfama com vista para o rio! | 125768 | Bárbara | Lisboa | Santa Maria Maior | 38.71272 | -9.12628 | Entire home/apt | 60 | 3 | 321 | 2020-03-11 | 2.82 | 1 | 351 |
3 | 29396 | Alfama Hill - Boutique apartment | 126415 | Mónica | Lisboa | Santa Maria Maior | 38.71239 | -9.12887 | Entire home/apt | 60 | 1 | 239 | 2020-03-15 | 2.47 | 2 | 318 |
4 | 29720 | TheHOUSE - Your luxury home | 128075 | Francisco | Lisboa | Estrela | 38.71195 | -9.15877 | Entire home/apt | 1100 | 2 | 55 | 2020-03-15 | 0.48 | 2 | 300 |
Vamos prosseguir e identificar a quantidade de entradas que nosso conjunto de dados possui e ver os tipos de cada coluna.
# identificar o volume de dados do DataFrame
print("Entradas:\t {}".format(df.shape[0]))
print("Variáveis:\t {}\n".format(df.shape[1]))
# verificar as 5 primeiras entradas do dataset
display(df.dtypes)
Entradas: 24640
Variáveis: 16
id int64
name object
host_id int64
host_name object
neighbourhood_group object
neighbourhood object
latitude float64
longitude float64
room_type object
price int64
minimum_nights int64
number_of_reviews int64
last_review object
reviews_per_month float64
calculated_host_listings_count int64
availability_365 int64
dtype: object
Este dataset que baixamos é a versão “resumida” do Airbnb, que contém 24640 entradas e 16 variáveis.
A qualidade de um dataset está diretamente relacionada à quantidade de valores ausentes. É importante entender logo no início se esses valores nulos são significativos comparados ao total de entradas.
# Verificar valores ausentes no dataset e colocar em ordem decrescente
(df.isnull().sum() / df.shape[0]).sort_values(ascending=False)
reviews_per_month 0.168547
last_review 0.168547
name 0.000771
host_name 0.000041
availability_365 0.000000
calculated_host_listings_count 0.000000
number_of_reviews 0.000000
minimum_nights 0.000000
price 0.000000
room_type 0.000000
longitude 0.000000
latitude 0.000000
neighbourhood 0.000000
neighbourhood_group 0.000000
host_id 0.000000
id 0.000000
dtype: float64
reviews_per_month
possui 16,8% dos seus valores faltantes.last_review
possui 16,8% dos seus valores faltantes.Para identificar a distribuição das variáveis, irei plotar o histograma.
# plotar o histograma das variáveis numéricas
df.hist(bins=15, figsize=(15,10));
Pela distribuição do histograma, é possível verificar indícios da presença de outliers. Olhe por exemplo as variáveis price
, minimum_nights
e calculated_host_listings_count
.
Os valores não seguem uma distruição, e distorcem toda a representação gráfica. Para confirmar, há duas maneiras rápidas que auxiliam a detecção de outliers. São elas:
describe()
boxplots
para a variável.# ver o resumo estatístico das variáveis numéricas
df[['price', 'minimum_nights', 'number_of_reviews', 'reviews_per_month',
'calculated_host_listings_count', 'availability_365']].describe()
price | minimum_nights | number_of_reviews | reviews_per_month | calculated_host_listings_count | availability_365 | |
---|---|---|---|---|---|---|
count | 24640.000000 | 24640.000000 | 24640.000000 | 20487.000000 | 24640.000000 | 24640.000000 |
mean | 110.739732 | 3.294440 | 41.781291 | 1.459542 | 15.057224 | 225.239448 |
std | 350.763230 | 15.970015 | 65.881125 | 1.461515 | 43.355842 | 132.790977 |
min | 0.000000 | 1.000000 | 0.000000 | 0.010000 | 1.000000 | 0.000000 |
25% | 47.000000 | 1.000000 | 2.000000 | 0.320000 | 1.000000 | 118.000000 |
50% | 69.000000 | 2.000000 | 14.000000 | 0.960000 | 3.000000 | 269.000000 |
75% | 100.000000 | 3.000000 | 53.000000 | 2.210000 | 10.000000 | 349.000000 |
max | 10000.000000 | 1000.000000 | 877.000000 | 19.890000 | 340.000000 | 365.000000 |
price
possui 75% do valor abaixo de 100, porém seu valor máximo é 10000.minimum_nights
) está acima de 365 dias no ano.# minimum_nights
df.minimum_nights.plot(kind='box', vert=False, figsize=(15, 3))
plt.show()
# ver quantidade de valores acima de 30 dias para minimum_nights
print("minimum_nights: valores acima de 30:")
print("{} entradas".format(len(df[df.minimum_nights > 30])))
print("{:.4f}%".format((len(df[df.minimum_nights > 30]) / df.shape[0])*100))
minimum_nights: valores acima de 30:
100 entradas
0.4058%
# price
df.price.plot(kind='box', vert=False, figsize=(15, 3),)
plt.show()
# ver quantidade de valores acima de 1500 para price
print("\nprice: valores acima de 1500")
print("{} entradas".format(len(df[df.price > 1500])))
print("{:.4f}%".format((len(df[df.price > 1500]) / df.shape[0])*100))
# df.price.plot(kind='box', vert=False, xlim=(0,1300), figsize=(15,3));
price: valores acima de 1500
57 entradas
0.2313%
Já que identificamos outliers nas variáveis price
e minimum_nights
, vamos agora limpar o DataFrame delas e plotar novamente o histograma.
# remover os *outliers* em um novo DataFrame
df_clean = df.copy()
df_clean.drop(df_clean[df_clean.price > 1500].index, axis=0, inplace=True)
df_clean.drop(df_clean[df_clean.minimum_nights > 30].index, axis=0, inplace=True)
df_clean.drop(df_clean[df_clean.calculated_host_listings_count > 60].index, axis=0, inplace=True)
# plotar o histograma para as variáveis numéricas
df_clean.hist(bins=15, figsize=(15,10));
print('Quantidade de variáveis {}'.format(df_clean.shape[0]))
print('Quantidade de entradas {}'.format(df_clean.shape[1]))
df_limpo = (df_clean.shape[0] / df.shape[0]) -1
print('Foram removidos {:.2%} dos dados na limpeza do dataset.'.format(df_limpo * (-1)))
Quantidade de variáveis 23220
Quantidade de entradas 16
Foram removidos 5.76% dos dados na limpeza do dataset.
df_clean[['price', 'minimum_nights','number_of_reviews', 'reviews_per_month', 'calculated_host_listings_count',
'availability_365']].describe()
price | minimum_nights | number_of_reviews | reviews_per_month | calculated_host_listings_count | availability_365 | |
---|---|---|---|---|---|---|
count | 23220.000000 | 23220.000000 | 23220.000000 | 19336.000000 | 23220.000000 | 23220.000000 |
mean | 97.228424 | 2.716968 | 43.391602 | 1.502402 | 7.262317 | 224.608570 |
std | 124.306829 | 3.384602 | 67.243900 | 1.483627 | 10.123960 | 132.692619 |
min | 0.000000 | 1.000000 | 0.000000 | 0.010000 | 1.000000 | 0.000000 |
25% | 45.000000 | 1.000000 | 2.000000 | 0.330000 | 1.000000 | 117.000000 |
50% | 69.000000 | 2.000000 | 14.000000 | 1.010000 | 3.000000 | 269.000000 |
75% | 100.000000 | 3.000000 | 56.000000 | 2.300000 | 8.000000 | 349.000000 |
max | 1500.000000 | 30.000000 | 877.000000 | 19.890000 | 60.000000 | 365.000000 |
Correlação significa que existe uma relação entre duas coisas. No nosso contexto, estamos buscando relação ou semelhança entre duas variáveis.
Essa relação pode ser medida, e é função do coeficiente de correlação estabelecer qual a intensidade dela. Para identificar as correlações existentes entre as variáveis de interesse, vou:
seaborn
# criar uma matriz de correlação
corr = df_clean[['price', 'minimum_nights', 'number_of_reviews', 'reviews_per_month',
'calculated_host_listings_count', 'availability_365']].corr()
display(corr)
price | minimum_nights | number_of_reviews | reviews_per_month | calculated_host_listings_count | availability_365 | |
---|---|---|---|---|---|---|
price | 1.000000 | -0.035038 | -0.126661 | -0.124047 | -0.002353 | 0.010003 |
minimum_nights | -0.035038 | 1.000000 | -0.051181 | -0.101343 | -0.049146 | -0.023528 |
number_of_reviews | -0.126661 | -0.051181 | 1.000000 | 0.780244 | -0.072072 | 0.085859 |
reviews_per_month | -0.124047 | -0.101343 | 0.780244 | 1.000000 | -0.063167 | 0.076246 |
calculated_host_listings_count | -0.002353 | -0.049146 | -0.072072 | -0.063167 | 1.000000 | 0.040110 |
availability_365 | 0.010003 | -0.023528 | 0.085859 | 0.076246 | 0.040110 | 1.000000 |
sns.heatmap(corr, cmap='RdBu', fmt='.2f', square=True, linecolor='white', annot=True);
A coluna da variável room_type
indica o tipo de locação que está anunciada no Airbnb. Se você já alugou no site, sabe que existem opções de apartamentos/casas inteiras, apenas o aluguel de um quarto ou mesmo dividir o quarto com outras pessoas.
Vamos contar a quantidade de ocorrências de cada tipo de aluguel, usando o método value_counts()
.
# mostrar a quantidade de cada tipo de imóvel disponível
df_clean.room_type.value_counts()
Entire home/apt 16618
Private room 5588
Hotel room 571
Shared room 443
Name: room_type, dtype: int64
#gráfico representando por tipo de imóvel
plt.figure(figsize=(15,5))
sns.countplot(df_clean['room_type'])
plt.title('Tipos de imóveis para se alugar');
# mostrar a porcentagem de cada tipo de imóvel disponível
a = (df_clean.room_type.value_counts() / df_clean.shape[0])
df_clean_a = pd.DataFrame(a)
df_clean_a.style.format('{:.2%}')
room_type | |
---|---|
Entire home/apt | 71.57% |
Private room | 24.07% |
Hotel room | 2.46% |
Shared room | 1.91% |
Podemos concluir que:
Uma maneira de se verificar uma variável em função da outra é usando groupby()
. No caso, queremos comparar os bairros (neighbourhoods) a partir do preço de locação.
df_clean.groupby(['neighbourhood']).price.mean().sort_values(ascending=False)[:10]
neighbourhood
Vermelha 292.500000
Cardosas 280.000000
Aveiras de Baixo 269.500000
Ota 249.000000
Freiria 220.000000
Peral 174.800000
Bucelas 170.300000
So Domingos de Benfica 167.225352
Alcabideche 154.391509
Vale do Paraso 150.000000
Name: price, dtype: float64
Só para dar um único exemplo de como uma amostra pode ser não-representativa, veja quantas entradas há para Vermelha, Cardosas e Aveiras de Baixo.
#ver quantidade de imóveis nos 3 primeiros bairros e a porcentagem em relação ao total de imóveis
print("{} entradas".format(len(df_clean[df_clean.neighbourhood == "Vermelha"].index)))
print("{:.4f}%".format((len(df_clean[df_clean.neighbourhood == "Vermelha"].index) / df.shape[0])*100))
print("{} entradas".format(len(df_clean[df_clean.neighbourhood == "Cardosas"].index)))
print("{:.4f}%".format((len(df_clean[df_clean.neighbourhood == "Cardosas"].index) / df.shape[0])*100))
print("{} entradas".format(len(df_clean[df_clean.neighbourhood == "Aveiras de Baixo"].index)))
print("{:.4f}%".format((len(df_clean[df_clean.neighbourhood == "Aveiras de Baixo"].index) / df.shape[0])*100))
2 entradas
0.0081%
1 entradas
0.0041%
2 entradas
0.0081%
#quantidade de imóveis por bairro
df_clean.neighbourhood.value_counts()
Santa Maria Maior 3450
Misericrdia 2730
Arroios 2235
Cascais e Estoril 1484
So Vicente 1322
...
Vila Nova da Rainha 1
Cardosas 1
Alcoentre 1
Vale do Paraso 1
Ota 1
Name: neighbourhood, Length: 127, dtype: int64
As variáveis Latitude e Longitude dos imóveis possibilita plotar cada ponto e mapear os imóveis. Considera-se x=longitude e y=latitude.
# plotar os imóveis pela latitude-longitude
df_clean.plot(kind="scatter", x='longitude', y='latitude', alpha=1.0, c=df_clean['price'], s=8,
cmap=plt.get_cmap('jet'), figsize=(12,8));
Foi feita apenas uma análise superficial na base de dados do Airbnb, porém já se percebeu que existem outliers nas colunas price
, minimum_nights
e calculated_host_listings_count
. Também se notou que há em algumas localidades poucos imóveis disponíveis, o que pode distorcer as informações estatísticas de alguns atributos.
Por fim, lembra-se que este dataset é uma versão resumida, ideal apenas para uma abordagem inicial. Recomenda-se que seja usado, em uma próxima análise exploratória, o conjunto de dados completos, com 106 atributos disponíveis.