This dataset is a collection of Airbnb listings throughout Madrid, Spain, including the surrounding region. It is a detailed dataset that provides information about each listing, including the host, location, coordinates, price, number of reviews, and other details. This dataset caught my interest because I studied abroad in Madrid during the Spring 2026 semester. I loved living in the city for almost four months, so having the opportunity to analyze data from a place I enjoyed so much was exciting. Being able to apply my data analytics skills to a city I have a personal connection with made this project even more interesting to me. At first, I wanted to see where the highest concentration of Airbnb listings was located and which districts had the highest prices. I was also interested in finding out which areas were more affordable, so that on future trips to Madrid, I would know where to look for an Airbnb without spending too much money!
For this project, I created several visualizations to better understand where Airbnb listings were concentrated and how prices varied across different districts in Madrid. I used a scatter plot to show where the more expensive listings were located based on their latitude and longitude coordinates. I also used Folium to create an interactive map of the first 100 listings to get a better idea of where they were located throughout the city. Another visualization I created was a bar graph comparing the average Airbnb price in each district to the overall average price across the Madrid region. I also used a line plot to explore whether listings with more reviews tended to have higher prices. In addition, I created a multi-line plot to compare the different room types available in each district. Lastly, I used a pie chart to show the overall distribution of room types across all Airbnb listings in the dataset. These visualizations helped me explore the different pricing trends, locations, and types of Airbnb listings throughout Madrid.
import pandas as pd
import matplotlib.pyplot as plt
import numpy
import warnings
warnings.filterwarnings("ignore")
df = pd.read_csv(r"C:\Users\markp\Downloads\listings.csv")
df.head()
type(df)
df.columns
This dataset had several problems that made creating visualizations more difficult. One of the biggest issues was that many of the listings had missing price values (NaN). To fix this, I removed the listings with missing pricing data so I could perform a more accurate analysis of the average Airbnb prices across different districts. Keeping these missing values in the dataset would have made it more difficult to calculate the mean prices and create accurate visualizations. Cleaning the data helped me get more reliable results and made it easier to compare Airbnb prices throughout Madrid.
df.columns
## Index(['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', 'number_of_reviews_ltm', 'license'],
## dtype='str')
df.info()
## <class 'pandas.DataFrame'>
## RangeIndex: 26980 entries, 0 to 26979
## Data columns (total 18 columns):
## # Column Non-Null Count Dtype
## --- ------ -------------- -----
## 0 id 26980 non-null int64
## 1 name 26980 non-null str
## 2 host_id 26980 non-null int64
## 3 host_name 26977 non-null str
## 4 neighbourhood_group 26980 non-null str
## 5 neighbourhood 26980 non-null str
## 6 latitude 26980 non-null float64
## 7 longitude 26980 non-null float64
## 8 room_type 26980 non-null str
## 9 price 21068 non-null float64
## 10 minimum_nights 26980 non-null int64
## 11 number_of_reviews 26980 non-null int64
## 12 last_review 21283 non-null str
## 13 reviews_per_month 21283 non-null float64
## 14 calculated_host_listings_count 26980 non-null int64
## 15 availability_365 26980 non-null int64
## 16 number_of_reviews_ltm 26980 non-null int64
## 17 license 2630 non-null str
## dtypes: float64(4), int64(7), str(7)
## memory usage: 5.9 MB
df = df.dropna(subset=['price'])
df = df[df['price'] > 0]
df.isna().sum()
df.info()
import folium
my_map = folium.Map(location=[40.4168, -3.7038], zoom_start=12)
for lat, lon in df[['latitude', 'longitude']].dropna().head(1000).values:
folium.CircleMarker(
location=[lat, lon],
radius=2,
color='blue'
).add_to(my_map)
my_map
The map shows that most of the first 100 Airbnb listings in the dataset are located in the center of Madrid, which is known as the Centro district. From looking at the map, there appears to be a higher concentration of Airbnb listings in the central part of the city compared to the surrounding areas. This is likely because Centro is one of the main tourist areas in Madrid, with many popular attractions, restaurants, and shops nearby. Tourists may prefer staying in this area because it provides easier access to many of the city’s main attractions. Many of Madrid’s most popular tourist destinations, such as Plaza Mayor, Puerta del Sol, and the Royal Palace, are located in or near this district. This could explain why there is a larger concentration of Airbnb listings around these areas, as visitors often look for accommodations within walking distance of major attractions. Overall, the map suggests that location plays an important role in where Airbnb listings are concentrated throughout Madrid, especially in areas that attract a large number of tourists.
plt.figure(figsize=(12, 10))
plt.scatter(
df['longitude'],
df['latitude'],
c=df['price'],
cmap='coolwarm',
alpha=0.5,
vmin=0,
vmax=500
)
plt.colorbar(label='Airbnb Price (€)')
plt.title('Airbnb Prices by Location in Madrid')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.show()
This graph also shows that there is a larger concentration of Airbnb
listings in the center of Madrid, where many of the more expensive
listings are located. The use of warm and cool colors in the scatter
plot helps show the differences in pricing throughout the city. I
created this visualization to focus on Airbnb listings priced over 500
euros and to see where the higher-priced listings are concentrated. From
looking at the graph, it appears that many of the more expensive
listings are located in areas that attract a large number of tourists.
For example, Puerta del Sol is one of the most popular tourist
destinations in Madrid, which could explain why there are more expensive
Airbnb listings around this area. Another interesting finding is that
the outskirts of Madrid have a much smaller concentration of Airbnb
listings compared to the inner city. Overall, this graph suggests that
Airbnb prices and the number of listings are influenced by location,
with central tourist areas having more listings and a greater
concentration of higher-priced accommodations.
def pick_colors_to_mean(this_data):
colors = []
avg = this_data.mean()
for each in this_data:
if each > avg:
colors.append('red') # Above average price
elif each < avg:
colors.append('green') # Below average price
else:
colors.append('blue') # Equal to average
return colors
avg_price = df.groupby('neighbourhood_group')['price'].mean().sort_values()
colors = pick_colors_to_mean(avg_price)
plt.figure(figsize=(12, 12))
plt.barh(avg_price.index, avg_price.values, color=colors)
plt.axvline(avg_price.mean(), color='black',label='District Average')
plt.legend(loc='lower right', fontsize=14)
plt.title('Average Airbnb Price by District in Madrid')
plt.xlabel('Average Price (€)')
plt.ylabel('District')
plt.show()
Based on this graph, the most expensive district in Madrid for Airbnb
listings is Salamanca, while the cheapest district is Vicálvaro. We can
see that Salamanca has an average Airbnb price that is almost 60 euros
higher than the average of all the districts in Madrid. The graph also
uses different colors to help show which districts are above or below
the average. Any district shown in green has an average Airbnb price
below the district average for Madrid. These districts would be better
options for tourists looking for more affordable Airbnb accommodations
because their listings are cheaper on average. On the other hand,
districts above the average tend to have more expensive Airbnb listings.
Overall, this graph helps show the differences in Airbnb pricing
throughout Madrid and how choosing a different district could help
tourists save money on accommodations.
avg_reviews = df.groupby('number_of_reviews')['price'].mean()
plt.figure(figsize=(12, 8))
plt.plot(avg_reviews.index,avg_reviews.values,color='blue',marker='o',markersize=3)
plt.title('Average Airbnb Price by Number of Reviews in Madrid')
plt.xlabel('Number of Reviews')
plt.ylabel('Average Price (€)')
plt.grid(True)
plt.show()
This graph shows the relationship between the number of reviews an
Airbnb listing receives and its average price in Madrid. From looking at
the graph, we can see that most of the average prices are concentrated
between 50 euros and 200 euros, regardless of the number of reviews.
There are also some significant outliers where the average price is much
higher than the rest of the listings. For example, around 600 reviews,
there is an average price reaching approximately 700 euros, while
another point around 325 reviews reaches over 600 euros. Another
interesting finding is that listings with a higher number of reviews do
not necessarily have higher average prices. I thought this was
especially interesting because I would have expected more expensive
listings to receive more reviews from people discussing whether the
higher price was worth it or not. I also believe that the pricing data
could be right-skewed because many of the Airbnb listings are located in
the center of Madrid, where prices tend to be higher due to tourism and
the popularity of the area. These more expensive listings could be
pulling the average prices higher and creating some of the outliers we
see in the graph. Overall, this graph suggests that the number of
reviews does not necessarily determine the price of an Airbnb listing,
and other factors, especially location, could have a greater influence
on pricing.
room_counts = df.groupby(['neighbourhood_group', 'room_type']).size().unstack()
plt.figure(figsize=(12, 8))
for room in room_counts.columns:
plt.plot(room_counts.index,room_counts[room], marker='o',label=room)
plt.title('Airbnb Listings by District and Room Type')
plt.xlabel('District')
plt.ylabel('Number of Listings')
plt.xticks(rotation=90)
plt.legend()
plt.grid(True)
plt.show()
This graph shows the number of Airbnb listings in each district of
Madrid based on the different room types available. From looking at the
graph, we can see that entire homes and apartments are the most common
type of Airbnb listing throughout Madrid, followed by private rooms. The
Centro district has by far the largest number of listings, with over
7,000 entire homes and apartments and around 1,700 private rooms. This
is significantly higher than any other district in Madrid.
Another interesting finding is that districts such as Salamanca, ChamberĂ, and Tetuán also have a relatively high number of entire home and apartment listings compared to other districts. On the other hand, districts such as Vicálvaro, Barajas, and Moratalaz have much fewer Airbnb listings. The graph also shows that hotel rooms and shared rooms make up a very small portion of the listings compared to entire homes and private rooms. Overall, this graph suggests that the location of a district plays an important role in the number and types of Airbnb accommodations available. The central districts tend to have more listings, while districts farther away from the city center generally have fewer options for tourists.
room_counts = df['room_type'].value_counts()
plt.figure(figsize=(10, 8))
plt.pie(room_counts,
autopct=lambda p: f'{p:.1f}%' if p > 3 else '')
plt.title('Airbnb Listings by Room Type in Madrid')
plt.legend(room_counts.index, title='Room Type',
loc='upper right', bbox_to_anchor=(1.3, 1))
plt.tight_layout()
plt.show()
df_filtered = df[df['price'] < 500]
df_filtered.boxplot(
column='price',
by='room_type',
figsize=(12, 8)
)
plt.title('Airbnb Prices by Room Type in Madrid')
plt.xlabel('Room Type')
plt.ylabel('Price (€)')
plt.xticks(rotation=30)
plt.show()
The pie chart shows that most of the Airbnb listings in this dataset are
entire homes or apartments. I think this makes sense because many Airbnb
hosts are homeowners who rent out their properties to travelers, and I
believe this trend would also apply to Airbnb listings overseas. Another
interesting factor is that many younger travelers in Europe, especially
those in their twenties, often choose to stay in hostels because they
are more affordable and provide opportunities to meet other travelers.
Because of this, I think many of the entire home and apartment listings
in Madrid could be targeted toward families or larger groups traveling
together who want more privacy and space. These accommodations may also
be popular among tourists visiting Madrid for longer periods of time.
Overall, the pie chart shows that entire homes and apartments make up
the majority of Airbnb listings in Madrid, suggesting that travelers
looking for a private place to stay have more options than those looking
for shared accommodations.
The boxplot shows some limitations in the dataset because there are significantly more listings for entire homes and apartments compared to hotel rooms and shared rooms. We can see that there are many outliers in the entire home and apartment category, as well as in private rooms. This could be because these categories have a much larger number of listings and a wider range of prices. On the other hand, hotel rooms and shared rooms have much smaller sample sizes, which makes their median prices less representative of the overall Airbnb market in Madrid. Because of the differences in the number of listings between each room type, it can be difficult to make accurate comparisons between the categories. Overall, the boxplot is useful for showing the differences in pricing and identifying outliers, but the uneven distribution of listings should be considered when analyzing the results.
Overall, this project helped me better understand the Airbnb market in Madrid, Spain, and how different factors such as location, price, room type, and number of reviews affect the listings throughout the city. From analyzing the dataset and creating different visualizations, I was able to identify several interesting trends in the Airbnb market.
One of the biggest findings from this project was that the Centro district has by far the largest concentration of Airbnb listings compared to the other districts in Madrid. This makes sense because Centro is one of the most popular tourist areas in the city, with major attractions such as Puerta del Sol and Plaza Mayor nearby. Another important finding was the difference in average Airbnb prices between districts. Salamanca had the highest average price, while Vicálvaro had the lowest. This shows how much the location of an Airbnb can affect its price and how tourists could save money by staying in districts outside the more expensive areas.
Another interesting finding was that entire homes and apartments make up the majority of Airbnb listings in Madrid, followed by private rooms. This could suggest that many Airbnb properties are targeted toward families and groups traveling together rather than individuals looking for shared accommodations. I also noticed that the number of reviews a listing receives does not necessarily mean it will have a higher price. There were many listings with fewer reviews that had higher prices than listings with hundreds of reviews.
There were also some limitations when analyzing the dataset, especially when looking at the boxplots. Since there were significantly more entire homes and apartments than hotel rooms and shared rooms, it was difficult to make accurate comparisons between some of the categories. There were also many outliers in the pricing data that could affect the averages.