1. Introduction

This report compares Airbnb listings in New York City and Los Angeles, focusing on nightly prices, room types, neighborhood prices, and minimum-stay requirements. The two datasets are combined to make comparisons between the cities easier. Five visualizations summarize the data and highlight differences in pricing and listing characteristics.

setwd("C:/Users/bryan/Documents/Data Visualization Data Files")

nyc <- read.csv("New_York_City/listings.csv")
la <- read.csv("Los_Angeles/listings.csv")

nyc$city <- "New York City"
la$city <- "Los Angeles"

airbnb <- bind_rows(nyc, la)

2. Dataset

The dataset contains Airbnb listing information from New York City and Los Angeles, allowing for a comparison of the two short-term rental markets.The dataset contains 37,548 listings from New York City and 45,533 listings from Los Angeles, for a combined total of 83,081 listings. It includes variables such as nightly price, neighborhood, room type, and minimum-night stay requirements. These variables provide insight into pricing patterns, geographic differences, and the types of accommodations available in each city. By combining the two datasets into one, the analysis can compare listings across both locations using histograms, heatmaps, line charts, bar charts, and pie charts. The analysis examines how Airbnb listings differ between the two cities and identifies patterns that may influence travelers’ accommodation choices.

3. Visualizations

Select a tab below to view each visualization and its description.

Individual Visualizations

Visualization 1: Distribution of Airbnb Prices

The histogram compares nightly Airbnb prices in New York City and Los Angeles for listings priced between $1 and $1,000. The horizontal axis represents nightly prices, while the vertical axis shows the number of listings in each price range. The overlapping colors make it easier to compare price distributions and identify the ranges containing the most listings.

airbnb_price <- airbnb %>%
  filter(!is.na(price), price > 0, price <= 1000)

p1 <- ggplot(
  airbnb_price,
  aes(x = price, fill = city)
) +
  geom_histogram(
    bins = 40,
    alpha = 0.5,
    position = "identity"
  ) +
  labs(
    title = "Distribution of Airbnb Prices",
    subtitle = "Listings priced at $1,000 or less per night",
    x = "Price per Night ($)",
    y = "Number of Listings",
    fill = "City"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(
      hjust = 0.5, size = 14, face = "bold"
    ),
    plot.subtitle = element_text(hjust = 0.5),
    legend.position = "bottom"
  )

p1

Visualization 2: Neighborhood Price Heat Map

The heat map compares the ten neighborhoods with the highest average nightly prices in each city. Each tile represents a neighborhood, with color indicating its average price and labels showing the average price and number of listings. This visualization helps identify expensive neighborhoods and compare neighborhood-level prices while providing listing counts to help interpret the averages.

heatmap_data <- airbnb %>%
  filter(
    !is.na(price),
    price > 0,
    !is.na(neighbourhood),
    neighbourhood != ""
  ) %>%
  group_by(city, neighbourhood) %>%
  summarise(
    average_price = mean(price),
    listing_count = n(),
    .groups = "drop"
  )

top_neighborhoods <- heatmap_data %>%
  group_by(city) %>%
  slice_max(
    order_by = average_price,
    n = 10,
    with_ties = FALSE
  ) %>%
  ungroup()

p2 <- ggplot(
  top_neighborhoods,
  aes(
    x = city,
    y = reorder(neighbourhood, average_price),
    fill = average_price
  )
) +
  geom_tile(color = "white", linewidth = 0.8) +
  geom_text(
    aes(
      label = paste0(
        "$", round(average_price),
        "\n(n=", scales::comma(listing_count), ")"
      )
    ),
    size = 3.5,
    lineheight = 0.9
  ) +
  scale_fill_gradient(
    low = "lightblue",
    high = "darkred",
    labels = scales::dollar,
    breaks = scales::breaks_pretty(n = 4),
  guide = guide_colorbar(
    barwidth = grid::unit(20, "cm"),
    barheight = grid::unit(0.8, "cm"),
    title.position = "top",
    title.hjust = 0.5,
    title.theme = element_text(size = 12),
    label.theme = element_text(size = 10),
    nbin = 100
  )
  ) +
  labs(
    title = "Neighborhood Prices and Listing Volume",
    subtitle = "Top 10 highest-priced neighborhoods per city",
    x = "City",
    y = "Neighborhood",
    fill = "Average Price"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(
      hjust = 0.5, size = 16, face = "bold"
    ),
    plot.subtitle = element_text(hjust = 0.5, size = 12),
    axis.text.y = element_text(size = 10),
    axis.text.x = element_text(size = 11, face = "bold"),
    legend.position = "bottom",
    legend.box = "vertical",
    legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
legend.spacing.y = unit(0.6, "cm"),
legend.margin = margin(t = 10, b = 10),
    plot.margin = margin(15, 15, 15, 15)
  )

p2

Visualization 3: Average Price by Minimum Stay

The line plot compares average nightly prices across four minimum-stay categories: 1–7 nights, 8–30 nights, 31–90 nights, and 91 or more nights. Separate lines represent New York City and Los Angeles. The chart helps show how average prices change across minimum-stay categories and whether the patterns differ between the cities.

line_data <- airbnb %>%
  filter(
    !is.na(price),
    price > 0,
    !is.na(minimum_nights),
    minimum_nights > 0
  ) %>%
  mutate(
    stay_category = case_when(
      minimum_nights <= 7 ~ "1-7 nights",
      minimum_nights <= 30 ~ "8-30 nights",
      minimum_nights <= 90 ~ "31-90 nights",
      TRUE ~ "91+ nights"
    )
  ) %>%
  group_by(city, stay_category) %>%
  summarise(
    average_price = mean(price),
    listing_count = n(),
    .groups = "drop"
  )

line_data$stay_category <- factor(
  line_data$stay_category,
  levels = c(
    "1-7 nights",
    "8-30 nights",
    "31-90 nights",
    "91+ nights"
  )
)

p3 <- ggplot(
  line_data,
  aes(
    x = stay_category,
    y = average_price,
    color = city,
    group = city
  )
) +
  geom_line(linewidth = 1.1) +
  geom_point(size = 3) +
  scale_y_continuous(labels = dollar_format()) +
  labs(
    title = "Average Price by Minimum Stay",
    x = "Minimum Stay",
    y = "Average Price per Night",
    color = "City"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(
      hjust = 0.5, size = 14, face = "bold"
    ),
    axis.text.x = element_text(angle = 20, hjust = 1),
    legend.position = "bottom"
  )

p3

Visualization 4: Median Price by Room Type

The grouped bar chart compares median nightly prices for different room types in New York City and Los Angeles. The bars appear side by side, making it easier to compare entire homes or apartments, private rooms, and shared rooms between the cities. Median prices are used because they are less affected by unusually expensive listings than averages.

price_by_room <- airbnb %>%
  filter(
    !is.na(price),
    price > 0,
    price <= 1000,
    !is.na(room_type)
  ) %>%
  group_by(city, room_type) %>%
  summarise(
    median_price = median(price),
    .groups = "drop"
  )

p4 <- ggplot(
  price_by_room,
  aes(
    x = room_type,
    y = median_price,
    fill = city
  )
) +
  geom_col(position = "dodge") +
  scale_y_continuous(labels = dollar_format()) +
  labs(
    title = "Median Price by Room Type",
    x = "Room Type",
    y = "Median Price per Night",
    fill = "City"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(
      hjust = 0.5, size = 14, face = "bold"
    ),
    axis.text.x = element_text(angle = 20, hjust = 1),
    legend.position = "bottom"
  )

p4

Visualization 5: Minimum-Stay Requirements

The pie charts compare the percentages of Airbnb listings in four minimum-stay categories for New York City and Los Angeles. Each chart represents one city, and each slice shows the proportion of listings in a particular category. The visualization helps identify which minimum-stay requirements are most common and compare the distributions between the cities.

stay_data <- airbnb %>%
  filter(
    !is.na(minimum_nights),
    minimum_nights > 0
  ) %>%
  mutate(
    stay_category = case_when(
      minimum_nights <= 7 ~ "1-7 nights",
      minimum_nights <= 30 ~ "8-30 nights",
      minimum_nights <= 90 ~ "31-90 nights",
      TRUE ~ "91+ nights"
    )
  )

stay_counts <- stay_data %>%
  count(city, stay_category) %>%
  group_by(city) %>%
  mutate(
    percent = n / sum(n),
    label = scales::percent(percent, accuracy = 0.1)
  ) %>%
  ungroup()

p5 <- ggplot(
  stay_counts,
  aes(x = "", y = percent, fill = stay_category)
) +
  geom_col(width = 1, color = "white") +
  geom_text(
    aes(label = label),
    position = position_stack(vjust = 0.5),
    size = 3
  ) +
  coord_polar(theta = "y") +
  facet_wrap(~city) +
  labs(
    title = "Minimum-Stay Requirements",
    fill = "Minimum Stay"
  ) +
  theme_void() +
  theme(
    plot.title = element_text(
      hjust = 0.5, size = 14, face = "bold"
    ),
    legend.position = "bottom"
  )

p5

4. Findings

The data reveals several important differences between Airbnb listings in New York City and Los Angeles. Los Angeles has a higher average nightly price, at $289.38 compared with $216.74 in New York City. However, the median prices are much closer: $155 in Los Angeles and $149 in New York City. This difference between the mean and median suggests that a relatively small number of expensive listings increase the average price in both cities. The dataset includes extreme prices of up to $56,425 in Los Angeles and $20,000 in New York City, so these outliers can distort price comparisons. Room-type comparisons also help show how prices vary between entire homes or apartments, private rooms, and shared rooms. Minimum stay requirements differ substantially: New York City has an average minimum stay of 29.32 nights and a median of 30 nights, compared with an average of 17.86 nights and a median of 14 nights in Los Angeles. This suggests that longer minimum stays are more common in the New York City listings. Los Angeles listings also have greater average annual availability, at 195.09 days compared with 158.74 days in New York City. The neighborhood heat map provides additional insight by highlighting areas with higher average listing prices and showing the number of listings represented in each neighborhood. However, missing price data is an important limitation: approximately 39.4% of New York City listings and 18.1% of Los Angeles listings have no recorded price. As a result, price comparisons may not represent every listing equally.

5. Conclusion

Overall, the analysis shows that Airbnb listings in New York City and Los Angeles differ in pricing, minimum stay requirements, and availability. Although Los Angeles has a higher average nightly price, the cities have similar median prices, indicating that unusually expensive listings influence the averages. New York City listings generally require longer stays, while Los Angeles listings have more days of annual availability on average. Neighborhood and room-type comparisons provide further detail about where prices are higher and how accommodation options differ. These findings can help travelers compare lodging options and understand differences between the two markets. Together, these visualizations provide an overview of differences in the two cities’ Airbnb listings.