Final Project: Mental Health and Music Survey Analysis Notebook¶

Final Project by Anjelica Martinez


Research Question¶

As someone with a background in psychology, this dataset immediately caught my attention. I was curious whether the music people listen to and how they feel about its effect on them might be related to the mental health challenges they report experiencing. I wanted to explore this relationship using real survey data rather than relying on assumptions about music's role in mental wellbeing.

Is there an association between music listening habits and perceived effects, and self-reported anxiety, depression, insomnia, and OCD levels among survey respondents?

To explore this question, I broke it down into four sub-questions:

  1. How do the four mental health scales (Anxiety, Depression, Insomnia, and OCD) relate to each other?
  2. Does favorite genre relate to anxiety and depression levels?
  3. How do hours per day and BPM preference relate to anxiety and depression?
  4. Do people who feel music "Worsens" their mental state report higher Anxiety, Depression, Insomnia, and OCD than those who feel it "Improves" their mental state, or report "No effect"?

Import Libraries and Data¶

In [1]:
# Import libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.formula.api as smf
In [2]:
# Load the cleaned dataset (produced in the data wrangling notebook)
df = pd.read_csv('data/mental_health_music_survey.csv', index_col=0)

# Quick check that the data loaded correctly and looks as expected
df.head()
Out[2]:
Age Primary streaming service Hours per day While working Instrumentalist Composer Fav genre Exploratory Foreign languages BPM ... Anxiety Depression Insomnia OCD Music effects While working_bool Instrumentalist_bool Composer_bool Exploratory_bool Foreign languages_bool
0 18.0 Spotify 3.0 Yes Yes Yes Latin Yes Yes 156.0 ... 3 0 1 0 NaN True True True True True
1 63.0 Pandora 1.5 Yes No No Rock Yes No 119.0 ... 7 2 2 1 NaN True False False True False
2 18.0 Spotify 4.0 No No No Video game music No Yes 132.0 ... 7 7 10 2 No effect False False False False True
3 61.0 YouTube Music 2.5 Yes No Yes Jazz Yes Yes 84.0 ... 9 7 3 3 Improve True False True True True
4 18.0 Spotify 4.0 Yes No No R&B Yes No 107.0 ... 7 2 5 9 Improve True False False True False

5 rows × 36 columns

About the Data¶

This dataset contains 736 survey responses across 36 columns, covering self-reported music listening habits (favorite genre, listening frequency by genre, hours per day, preferred tempo, streaming service) and four self-reported mental health scales (Anxiety, Depression, Insomnia, and OCD), each rated from 0 to 10. The data comes from a publicly available Kaggle dataset (Music & Mental Health Survey Results) and was collected through a voluntary, informally shared online survey.

In [3]:
# Glimpse at the structure of the dataframe
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 736 entries, 0 to 735
Data columns (total 36 columns):
 #   Column                        Non-Null Count  Dtype  
---  ------                        --------------  -----  
 0   Age                           735 non-null    float64
 1   Primary streaming service     735 non-null    object 
 2   Hours per day                 736 non-null    float64
 3   While working                 733 non-null    object 
 4   Instrumentalist               732 non-null    object 
 5   Composer                      735 non-null    object 
 6   Fav genre                     736 non-null    object 
 7   Exploratory                   736 non-null    object 
 8   Foreign languages             732 non-null    object 
 9   BPM                           621 non-null    float64
 10  Frequency [Classical]         679 non-null    object 
 11  Frequency [Country]           712 non-null    object 
 12  Frequency [EDM]               684 non-null    object 
 13  Frequency [Folk]              683 non-null    object 
 14  Frequency [Gospel]            727 non-null    object 
 15  Frequency [Hip hop]           647 non-null    object 
 16  Frequency [Jazz]              700 non-null    object 
 17  Frequency [K pop]             685 non-null    object 
 18  Frequency [Latin]             706 non-null    object 
 19  Frequency [Lofi]              661 non-null    object 
 20  Frequency [Metal]             672 non-null    object 
 21  Frequency [Pop]               566 non-null    object 
 22  Frequency [R&B]               650 non-null    object 
 23  Frequency [Rap]               629 non-null    object 
 24  Frequency [Rock]              572 non-null    object 
 25  Frequency [Video game music]  657 non-null    object 
 26  Anxiety                       736 non-null    int64  
 27  Depression                    736 non-null    int64  
 28  Insomnia                      736 non-null    int64  
 29  OCD                           736 non-null    int64  
 30  Music effects                 728 non-null    object 
 31  While working_bool            733 non-null    object 
 32  Instrumentalist_bool          732 non-null    object 
 33  Composer_bool                 735 non-null    object 
 34  Exploratory_bool              736 non-null    bool   
 35  Foreign languages_bool        732 non-null    object 
dtypes: bool(1), float64(3), int64(4), object(28)
memory usage: 207.7+ KB

Summary Statistics¶

Survey respondent ages ranged from 10 to 89 years old, with an average age of 25 and a standard deviation of 12 years. Daily listening time ranged from 0 to 24 hours, with an average of 4 hours and a standard deviation of 3 hours. Preferred listening tempo (BPM) ranged from 40 to 220 beats per minute, with an average of 124 BPM and a standard deviation of 32*.

All four mental health scales ranged from 0 (not severe) to 10 (severe). Respondents reported an average Anxiety score of 6 (SD = 3), an average Depression score of 5 (SD = 3), an average Insomnia score of 4 (SD = 3), and an average OCD score of 3 (SD = 3).

Note:
*One respondent did not report their age, but their response was not excluded from the dataset, since their remaining answers still provided useful data for analysis.
**The BPM field was left blank by 115 respondents.
In [4]:
# Reviewing the summary statistics for all continuous variable columns
df.describe()
Out[4]:
Age Hours per day BPM Anxiety Depression Insomnia OCD
count 735.000000 736.000000 621.000000 736.000000 736.000000 736.000000 736.000000
mean 25.206803 3.572758 123.758454 5.836957 4.794837 3.737772 2.635870
std 12.054970 3.028199 32.028258 2.792710 3.029564 3.088797 2.840047
min 10.000000 0.000000 40.000000 0.000000 0.000000 0.000000 0.000000
25% 18.000000 2.000000 100.000000 4.000000 2.000000 1.000000 0.000000
50% 21.000000 3.000000 120.000000 6.000000 5.000000 3.000000 2.000000
75% 28.000000 5.000000 144.000000 8.000000 7.000000 6.000000 5.000000
max 89.000000 24.000000 220.000000 10.000000 10.000000 10.000000 10.000000

Histogram Distributions for Age and BPM¶

Participant Ages

Participants ranged in age from 10 to 89, with an average age of 25. The distribution is right-skewed, meaning most respondents were in their 20s, with a smaller number of older participants stretching out the tail. This is probably just a reflection of who this survey actually reached, younger adults are more likely to use music streaming apps and to come across an informally shared online survey like this one, which probably explains why the sample doesn't really match the general population's age spread.

In [5]:
# Distribution histogram of participant ages
# Using 5-year bins since age is heavily right-skewed (mostly younger respondents,
# with a long thin tail of older ones)
sns.set_theme(style="white")
sns.histplot(data=df, x='Age', bins=range(10, 95, 5), color='#21908CFF')
plt.suptitle("What Does the Age Distribution of Respondents Look Like?", fontweight='bold')
plt.title("The majority of respondents were in their 20s, with a long tail of older respondents", pad=10, size=10)
plt.xlabel("Respondent Age (years)", size=10)
plt.ylabel("Number of Survey Respondents", size=10)
plt.show()
No description has been provided for this image

Popular Beats Per Minute Tempo (BPM)

Most people in this survey prefer listening to music somewhere between 100 and 150 beats per minute, with 120-125 BPM being the most common choice. A smaller group prefers much faster music, up to around 220 BPM. This roughly matches what you'd expect, since Pop and Rock (the two most popular favorite genres in this sample, see below) typically fall in that same tempo range. Before making this chart, eight clearly invalid BPM entries were removed (including one respondent who somehow entered almost a billion beats per minute), so this chart reflects the cleaned data.

In [6]:
# Distribution histogram of BPM (Beats per Minute)
# Uses the cleaned BPM column (outliers like the ~1 billion entry were already removed)
sns.set_theme(style="white")
sns.histplot(data=df, x='BPM', color='#21908CFF')

plt.suptitle("What Tempo Do People Prefer to Listen To? Most Cluster Around 100–150 BPM", fontweight='bold', size=13)
plt.title("A smaller number of respondents reported much faster preferred tempos", pad=10, size=10)
plt.xlabel("Music Beats Per Minute (BPM)", size=10, labelpad=10)
plt.ylabel("Number of Respondents", size=10)
plt.show()
No description has been provided for this image

Analysis¶

How Do Mental Health Scales Relate to Each Other and to Listening Habits?¶

The correlation heatmap shows that the four mental health scales are much more related to each other than to either listening variable. Anxiety and Depression had the strongest connection in the whole dataset (r = 0.52), followed by moderate relationships between Depression and Insomnia (r = 0.38), Anxiety and Insomnia (r = 0.29), and Anxiety and OCD (r = 0.35). Hours per day and BPM, on the other hand, barely related to any of the four scales; every single one of those correlations was under 0.14. So whatever's driving differences in anxiety, depression, insomnia, and OCD in this sample, it doesn't seem to be how much music someone listens to or how fast their preferred tempo is.

To check this more formally, I ran a linear regression predicting Anxiety from Hours per day and BPM (n = 621). Neither one came out significant (Hours per day: p = 0.199; BPM: p = 0.170), and together they explained less than 1% of the variation in Anxiety scores (R² = 0.006). The model overall didn't reach significance either (F-statistic p = 0.169). Basically, this just confirms what the heatmap already hinted at, neither how long someone listens nor their preferred tempo seems to matter much for anxiety.

I ran the same regression for Depression. This time it actually came out significant overall (F-statistic p = 0.0155). Hours per day turned out to be a real predictor (p = 0.009): each extra hour of daily listening was linked to about a 0.10-point bump in Depression score, holding BPM steady. BPM still wasn't significant (p = 0.220), same as before. But the actual size of this effect is small. Together, Hours per day and BPM only explained 1.3% of the variation in Depression scores (R² = 0.013). So there's a small, real connection between listening time and depression here, but it's nowhere close to being the main thing driving differences in people's depression scores.

Worth pointing out: this is correlational, not causal. I can't tell from this data whether listening more actually contributes to higher depression, or whether people who are more depressed just happen to listen to more music as a way of coping. Both are equally plausible, and this analysis can't tell them apart.

So overall, of the two listening variables I looked at, Hours per day shows a small but real link to Depression, while BPM doesn't seem to matter much for either Anxiety or Depression in this sample.

In [7]:
# Subset to the 6 numeric variables relevant to the research question:
# the four mental health scales plus the two listening habit variables
df_subset = df[['Anxiety', 'Depression', 'Insomnia', 'OCD', 'Hours per day', 'BPM']]

# Correlation heatmap to see how mental health scales relate to each other
# and to listening habits, answering sub-question 1
plt.figure(figsize=(8,6))
corr_matrix = df_subset.corr()
sns.heatmap(corr_matrix, annot=True, cmap="RdBu", vmin=-1, vmax=1)
plt.suptitle("How Do Mental Health Scales Relate to Each Other and to Listening Habits?", 
             fontweight='bold', 
             size=13, 
             y=.97)
plt.title("Anxiety and Depression are most strongly related; listening habits show little connection to any of them", 
          pad=11, 
          size=10,
          x= 0.6)
plt.show()
No description has been provided for this image

Regression: Anxiety predicted by Hours per day and BPM

In [8]:
# Linear regression testing whether Hours per day and BPM predict Anxiety
# (sub-question 3: do listening habits relate to mental health scores?)
model = smf.ols(formula='Anxiety ~ Q("Hours per day") + BPM', data=df)
results = model.fit()
results.summary()
Out[8]:
OLS Regression Results
Dep. Variable: Anxiety R-squared: 0.006
Model: OLS Adj. R-squared: 0.003
Method: Least Squares F-statistic: 1.784
Date: Fri, 26 Jun 2026 Prob (F-statistic): 0.169
Time: 18:55:14 Log-Likelihood: -1510.2
No. Observations: 621 AIC: 3026.
Df Residuals: 618 BIC: 3040.
Df Model: 2
Covariance Type: nonrobust
coef std err t P>|t| [0.025 0.975]
Intercept 5.0939 0.461 11.047 0.000 4.188 5.999
Q("Hours per day") 0.0460 0.036 1.286 0.199 -0.024 0.116
BPM 0.0048 0.003 1.373 0.170 -0.002 0.012
Omnibus: 50.474 Durbin-Watson: 1.903
Prob(Omnibus): 0.000 Jarque-Bera (JB): 32.144
Skew: -0.429 Prob(JB): 1.05e-07
Kurtosis: 2.287 Cond. No. 533.


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

Regression: Depression predicted by Hours per day and BPM

In [9]:
# Linear regression testing whether Hours per day and BPM predict Depression
# (sub-question 3, second outcome variable)
# Q() wraps "Hours per day" since the formula syntax can't handle column names with spaces
model = smf.ols(formula='Depression ~ Q("Hours per day") + BPM', data=df)
results = model.fit()
results.summary()
Out[9]:
OLS Regression Results
Dep. Variable: Depression R-squared: 0.013
Model: OLS Adj. R-squared: 0.010
Method: Least Squares F-statistic: 4.195
Date: Fri, 26 Jun 2026 Prob (F-statistic): 0.0155
Time: 18:55:14 Log-Likelihood: -1558.9
No. Observations: 621 AIC: 3124.
Df Residuals: 618 BIC: 3137.
Df Model: 2
Covariance Type: nonrobust
coef std err t P>|t| [0.025 0.975]
Intercept 3.9127 0.499 7.845 0.000 2.933 4.892
Q("Hours per day") 0.1012 0.039 2.614 0.009 0.025 0.177
BPM 0.0046 0.004 1.228 0.220 -0.003 0.012
Omnibus: 164.610 Durbin-Watson: 1.973
Prob(Omnibus): 0.000 Jarque-Bera (JB): 29.689
Skew: -0.110 Prob(JB): 3.57e-07
Kurtosis: 1.952 Cond. No. 533.


Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.

What Genres Do Respondents Prefer Most?¶

Rock came out as the most popular favorite genre by far, with 188 people picking it, followed by Pop (114), Metal (88), Classical (53), and Video Game Music (44). Together, these five genres cover most of the sample, while the other 11 genres in the dataset were each picked by much fewer people. Since some of those smaller genres had really small sample sizes (some with fewer than 10 respondents), I stuck to just these top 5 for the genre-based comparisons later on (the listening frequency chart and the genre-by-mental-health boxplots), since those numbers are big enough to actually mean something.

In [10]:
# Music genre color dictionary for consistent color mapping
# Ensures each genre gets the same color across multiple charts
# (seaborn's default palette assigns colors by position, which caused
# mismatched colors between the bar chart and boxplot)
genre_colors = {
    'Rock': '#4b4279',
    'Pop': '#3c6682',
    'Metal': '#2f837f',
    'Classical': '#45a778',
    'Video game music': '#8fc456'
}
In [11]:
# Count how many respondents picked each genre as their favorite, keep top 5
top5_genre_counts = df['Fav genre'].value_counts().head(5).reset_index()

# Bar chart of the 5 most popular favorite genres
# Uses genre_colors dictionary so colors stay consistent with the boxplot below
plt.figure(figsize=(8,6))
sns.barplot(data=top5_genre_counts, x='Fav genre', y='count', palette=genre_colors, hue='Fav genre', legend=False)
plt.suptitle("What Genres Do Respondents Prefer Most? Rock Is the Clear Favorite", fontweight='bold', size=12, y=.96)
plt.title("Pop and Metal follow, while Classical and Video Game Music are far less common favorites", pad=10, size=10)
plt.xlabel("Favorite Genre", size=10, labelpad=10)
plt.ylabel("Number of Respondents", size=10)
plt.show()
No description has been provided for this image

Does Favorite Genre Relate to Anxiety Levels?¶

Looking at Anxiety scores across the top 5 genres, Rock fans reported the highest typical anxiety (median = 7), while Classical fans came in lowest (median = 6, though Classical also had the widest spread of responses by far). Pop, Metal, and Video Game Music all landed on the same median (6), even though their distributions looked a bit different in spread and shape.

These differences are real, but pretty small, every genre's median falls somewhere in a narrow 6-to-7 range, and the boxes for all five genres overlap a lot. So while favorite genre might have some connection to anxiety level in this sample, it's a weak one. You couldn't reliably guess someone's anxiety level just from knowing their favorite genre.

And like everything else here, this is correlational and self-reported, so I can't say which way it goes. It's just as possible that people with different anxiety levels are drawn to different music, as it is that the music itself is shaping their anxiety. This data just can't tell us which.

In [12]:
# Restrict to the top 5 favorite genres (established above) since smaller
# genres have too few respondents for a reliable comparison
top5_genre = ['Rock', 'Pop', 'Metal', 'Classical', 'Video game music']
filtered_df = df[df['Fav genre'].isin(top5_genre)] 

# Boxplot of Anxiety by favorite genre (sub-question 2)
# Uses genre_colors so colors match the genre popularity bar chart above
plt.figure(figsize=(10,6))
sns.boxplot(filtered_df, x='Fav genre', y='Anxiety', 
            palette=genre_colors, hue='Fav genre', 
            legend=False, order=top5_genre)
plt.suptitle("Does Favorite Genre Relate to Anxiety Levels? Rock Fans Report the Highest Typical Anxiety", 
             fontweight='bold', 
             size=12,
             y=.97)
plt.title("Classical fans show the lowest median anxiety, but with the widest range of responses", 
          pad=14, 
          size=11)
plt.xlabel("Favorite Genre", 
           size=10, 
           labelpad=10,
           fontweight='bold')
plt.ylabel("Self-Reported Anxiety Level (0 = none, 10 = severe)", 
           size=10,
           fontweight='bold')
plt.show()
No description has been provided for this image

Do People Who Feel Music "Worsens" Their Mental State Report Higher Distress?¶

People who said music "Worsens" their mental state reported noticeably higher scores across all four mental health measures than those who said it "Improves" their state or has "No effect." This showed up most clearly with Depression: the "Worsen" group averaged 7.2, compared to 4.9 for "Improve" and 4.4 for "No effect." The same pattern held for Anxiety (6.8 vs. 6.0 vs. 5.1), Insomnia (4.5 vs. 3.8 vs. 3.7), and OCD (3.1 vs. 2.7 vs. 2.4).

What's interesting is that "Improve" and "No effect" were pretty close to each other across the board, while "Worsen" stood apart from both. So the real divide here might not be "does music help vs. not help", it's more like "does music actively make things worse."

That said, this one needs a caveat: the "Worsen" group was tiny, only 17 people, compared to 542 for "Improve" and 169 for "No effect." With a group that small, a few people with unusually high scores could easily skew the average. The wide error bars on the chart are basically showing that uncertainty.

And like everything else in this analysis, I can't say which way the relationship actually goes. It's just as plausible that people who are already struggling more tend to feel like music is making things worse, as it is that music itself is contributing to that. Even so, out of everything I looked at in this project, this is the clearest and most consistent pattern I found.

In [13]:
# Reshape the four mental health scales into long format, keeping Music effects
# attached to each row, so they can all be plotted together grouped by Scale
mental_scales = ['Anxiety', 'Depression', 'Insomnia', 'OCD']
mental_long_df = df.melt(id_vars='Music effects', 
                         value_vars=mental_scales,
                         var_name='Scale',
                         value_name='Score')

# Grouped bar chart of average score per mental health scale, split by Music effects
# (sub-question 4) — seaborn calculates the means and confidence intervals automatically
sns.barplot(data = mental_long_df, x='Scale', y='Score', hue='Music effects', palette='Reds')
plt.suptitle("""Do People Who Feel Music "Worsens" Their Mental State Report Higher Distress? Yes, Especially for Depression""", 
             fontweight='bold', 
             size=11)
plt.title("""The "Worsen" group's wide error bars reflect its small sample size (n = 17)""", 
          pad=10, 
          size=11)
plt.xlabel("Mental Health Scale", size=12, labelpad=10)
plt.ylabel("Average Self-Reported Score (0–10)", size=12)
plt.show()
No description has been provided for this image

Conclusion¶

I set out to see whether music listening habits, and how people feel about music's effect on them, are connected to self-reported anxiety, depression, insomnia, and OCD. A few clear patterns showed up.

First, the four mental health scales were much more related to each other than to anything about people's listening habits. Anxiety and Depression had the strongest connection (r = 0.52), and the other pairs were also moderately related. This makes sense since these four things probably overlap a lot, even though respondents rated them separately.

Second, the actual mechanics of listening (how long, how fast) barely mattered. BPM didn't predict Anxiety or Depression at all. Hours per day didn't predict Anxiety either, but it did show a small, real connection to Depression. Each extra hour of listening per day was linked to about a 0.10-point bump in Depression score. That said, it's a tiny effect; it only explains about 1% of why people's Depression scores differ.

Third, favorite genre had a modest link to Anxiety. Rock fans reported the highest typical anxiety, Classical fans the lowest, but honestly, all five genres overlapped a lot, so this isn't a huge difference.

The most interesting finding by far was about how people feel about music's effect on them. People who said music "Worsens" their mental state reported noticeably higher scores across all four mental health measures, especially Depression. That group was small (only 17 people), so I'd take it with a grain of salt, but the pattern held up consistently across every scale, which makes it the strongest result in this whole project.

Limitations¶

This is correlational, not causal. I can't say which direction any of these relationships actually go. It's just as likely that people who are already struggling turn to music more, or feel like it's worsening things, as it is that music itself is driving these outcomes. The sample also skewed pretty young (median age 21), people opted in themselves, and everything is self-reported, so I wouldn't generalize this too far beyond this specific group. A few genres and the "Worsen" group especially had small sample sizes, which adds more uncertainty there. And there wasn't really a meaningful time variable in this dataset, so a time-based chart wasn't possible.

Overall, it seems like how someone feels about music's role in their life might matter more than how or how much they actually listen. To really dig into whether music is shaping mental health, or the other way around, you'd need a bigger, more representative sample and ideally data that follows people over time.