Assessment of Noise in the Intensive Care Unit Using Apple Watch

Alarms from monitors, medical devices and staff activities contribute to high noise levels in the Intensive Care Unit (ICU). Excessive levels of noise disrupt sleep patterns in patients admitted to the ICU and may contribute to the development of delirium and post-intensive care syndrome (PICS).

The World Health Organization (WHO) recommends that hospital noise levels should not exceed 35 A-weighted decibels (dBA) during the day and 30 dBA at night. However, daytime noise levels in ICU were found to be above acceptable levels, around 60 dBA. Although polysomnography is the standard for assessing sleep, it is time-consuming and challenging to interpret.

More recent versions of the Apple Watch (Series 4 and 5) take advantage of the internal microphone to regularly sample and track sound levels in the environment. These devices might therefore play a role in monitoring noise levels in the ICU, thanks to their simplicity and popularity. We investigated the feasibility of collecting and analysing data from an Apple Watch to measure noise levels in the ICU.

Apple Watch worn by an ICU nurse while measuring noise levels
Apple Watch worn by an ICU nurse while measuring noise levels

Data export and analysis

Accordingly, we exported Health data from the personal Apple Watch of a nurse working in a referral cardiothoracic ICU managing patients after cardiac surgery and those with cardiogenic shock, refractory cardiac arrest, and respiratory failure. The ICU was composed of 14 beds organized in four rooms with two, four, six and two patients.

Sound levels were measured in A-weighted decibels (dBA), a widely adopted standard for environmental noise measurement. The A-weighting curve adjusts the sound levels to match the perception of the human ear, less sensitive to low audio frequencies. This technique effectively cuts off the lower and higher frequencies that the average person cannot hear.

Sound levels were compared between daytime, from 7 a.m. to 11 p.m., and night-time, from 11 p.m. to 7 a.m. Data extraction and statistical analysis were performed with the “Pandas” Python Library.

Step 1: Export Data from Apple Health App on your iPhone

  • Export your data from the Health app on your iPhone
  • Unzip export.zip into this folder and rename the unzipped folder to data
  • Inside the folder /data there should be a file called export.xml

Step 2: Extract Environmental Audio Exposure data

  • Command Line Tool to process data exported from Health app contained in the export.xml file (Credits: https://github.com/tdda/applehealthdata)
  • This script create multiple .csv files for each data type
  • To analyse noise levels we need only EnvironmentalAudioExposure.csv file as it contains recordings of noise levels
  • Based on the size of your Health data, this script may take several minutes to complete
%run -i 'apple-health-data-parser' 'data/export.xml'

Step 3: Manually select samples recorded in the ICU

  • Open EnvironmentalAudioExposure.csv file with Excel containing all data about noise levels recorded by Apple Watch
  • Remove rows of samples recorded outside ICU
  • Save file as noise_samples_icu.csv inside /data folder

Step 4: Data Analysis

#Import necessary Libraries to perform data analysis
from datetime import date, datetime, timedelta as td
import pytz
import numpy as np
import scipy.stats as stats
import pandas as pd
import glob
import matplotlib, matplotlib.pyplot as plt
%matplotlib inline

# Functions to convert UTC to your time zone and extract date and time
convert_tz = lambda x: x.to_pydatetime().replace(tzinfo=pytz.utc).astimezone(pytz.timezone('Europe/Rome'))
extract_year = lambda x: convert_tz(x).year
extract_month = lambda x: '{}-{:02}'.format(convert_tz(x).year, convert_tz(x).month)
extract_date = lambda x: '{}-{:02}-{:02}'.format(convert_tz(x).year, convert_tz(x).month, convert_tz(x).day)
extract_day = lambda x: convert_tz(x).day
extract_hour = lambda x: convert_tz(x).hour
extract_time = lambda x: convert_tz(x).time()
extract_minute = lambda x: convert_tz(x).minute
extract_day_of_week = lambda x: convert_tz(x).weekday()

#Read file noise_samples_icu.csv containing all data about noise levels recorded by Apple Watch in ICU
df_noise = pd.read_csv("data/noise_samples_icu.csv")

#Read file noise_samples_icu.csv containing all data about noise levels recorded by Apple Watch in ICU
df_noise = pd.read_csv("data/noise_samples_icu.csv")

#Coversion of date and time field
df_noise['startDate'] = pd.to_datetime(df_noise['startDate'])
df_noise['endDate'] = pd.to_datetime(df_noise['endDate'])

#Extract of date from datetime field
df_noise['date'] = df_noise['startDate'].map(extract_date)

#Extract hour from datetime field: 2019-11-09 06:40:12+01:00 ==> 6
df_noise['hour'] = df_noise['startDate'].map(extract_hour)

#Extract Day Of Week (DOW)
#0 = Monday, ..., 6 = Sunday
df_noise['dow'] = df_noise['startDate'].map(extract_day_of_week)

#Use only needed columns
df_noise = df_noise[['startDate','endDate', 'value', 'date', 'hour', 'dow']]

#Display top 10 rows
df_noise.head(10)

Analysis 1: Noise levels in ICU during daytime vs night-time

# 1. Define daytime and night-time hours
h_start_day = 7 #included
h_end_day = 22 #included

# 2. Compute a column that identifies samples as daytime (= 1) and night-time (= 0)
df_noise.loc[(df_noise['hour'] >= h_start_day) & (df_noise['hour'] <= h_end_day), 'daytime'] = 1 
df_noise.loc[df_noise['daytime'] != 1, 'daytime'] = 0

# 3. Display top 10 rows
df_noise.head(10)

# 4. Group by daytime (= 1) and night-time (= 0)
groupby_hours = df_noise.groupby('daytime')

# 5. Display count, mean, std, min, 25%, 50%, 75% and max
groupby_hours["value"].describe()
# 6. Compute t-test between the two groups: daytime (= 1) and night-time (= 0)

# Create two dataframes with noise levels from the two groups
df_values_daytime = df_noise[df_noise["daytime"]==1]["value"]
df_values_nighttime = df_noise[df_noise["daytime"]==0]["value"]

# 7. Compute and display t-test
t2, p2 = stats.ttest_ind(a=df_values_daytime,b=df_values_nighttime ,equal_var = False)
print("t = " + str(t2))
print("p = " + str(p2))

t = 7.385116247757598
p = 3.108910072761726e-13
# 8. Display BoxPlot and save the image

# Create BoxPlot
boxplot = groupby_hours.boxplot(column=['value'], return_type='axes')

# Save figure as PDF
plt.savefig('figures/boxplot_daytime_vs_nighttime.pdf')

Analysis 2: Noise levels in ICU by day of week

# 1. Group by day of week (dow) and calculate mean and std for each dow
# 0 = Monday, ..., 6 = Sunday
df_avg_noise_dow = df_noise.groupby('dow').agg({'value': ['mean', 'std']})

# 2. Display top 10 rows
df_avg_noise_dow.head(10)
# 3. Display Bar Plot with std
fig, ax = plt.subplots(figsize=[10, 6])

ax = df_avg_noise_dow["value"]["mean"].plot(kind='bar', x='day_of_week', color='blue', yerr=df_avg_noise_dow["value"]["std"])
ax.set_ylim((0, 80))

n_groups = len(df_avg_noise_dow)
index = np.arange(n_groups)
opacity = 0.75

ax.yaxis.grid(True)

plt.suptitle('Average Noise Levels by Day of the Week', fontsize=16)
dow_labels = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
plt.xticks(index, dow_labels, rotation=45)
plt.xlabel('Day of Week', fontsize=12, color='black')
plt.ylabel('Noise Levels (dBA)', fontsize=12, color='black')

# 4. Save figure as PDF
plt.savefig('figures/barplot_avg_noise_by_dow.pdf')

Analysis 3: Noise levels in ICU by hour of the day

# 1. Group by hour of the day and calculate mean and std for each hour
df_noise_by_hour = df_noise.groupby('hour').agg({'value': ['mean', 'std']})

# 2. Display all rows with mean and std
df_noise_by_hour
# 3. Display Line Plot with std
fig, ax = plt.subplots(figsize=[10, 6])
ax = df_noise_by_hour["value"]["mean"].plot(kind='line', figsize=(10, 5), yerr=df_noise_by_hour["value"]["std"], legend=False, linewidth=2, alpha=1, marker='o', color='black', markeredgecolor='black', markerfacecolor='yellow', markersize=8, markeredgewidth=2)

xlabels = df_noise_by_hour.index.map(lambda x: '{:02}:00'.format(x))
ax.set_xticks(range(len(xlabels)))
ax.set_xticklabels(xlabels, rotation=45, rotation_mode='anchor', ha='right')

ax.set_xlim((df_noise_by_hour.index[0], df_noise_by_hour.index[-1]))
ax.set_ylim((0, 100))

ax.yaxis.grid(True)
ax.set_ylabel('ICU Noise Levels (dBA)')
ax.set_xlabel('Hours')
plt.suptitle('Average noise levels by hour of the day', fontsize=16)

# 4. Save figure as PDF
plt.savefig('figures/lineplot_noise_by_hour.pdf')

Analysis 4: Noise levels in ICU within WHO recommended range

# 1. Filters
dayhours_filter = df_noise["daytime"] == 1
nighthours_filter = df_noise["daytime"] == 0
WHO_day_35dBA_limit = df_noise["value"] <= 35
WHO_night_30dBA_limit = df_noise["value"] <= 30

# 2. Count totale samples and samples within range during daytime and nighttime
values_during_dayhours = df_noise["value"][dayhours_filter].count()
values_below_35dba_during_dayhours = df_noise["value"][WHO_day_35dBA_limit].count()

values_during_nighthours = df_noise["value"][nighthours_filter].count()
values_below_30dba_during_nighthours = df_noise["value"][WHO_night_30dBA_limit].count()

# 3. Display results
print("% of values within WHO 35 dBA limit during daytime:")
print((values_below_35dba_during_dayhours/values_during_dayhours)*100)

% of values within WHO 35 dBA limit during daytime:
2.781641168289291

print("% of values within WHO 30 dBA limit during night:")
print((values_below_30dba_during_nighthours/values_during_nighthours)*100)

% of values within WHO 30 dBA limit during night:
0.0

Other analysis

# 1. Lowest noise level
df_noise.sort_values(by=['value'], ascending = True).head(1)
# 2. Highest noise level
df_noise.sort_values(by=['value'], ascending = False).head(1)

Results

Consecutive 1,086 samples measured during 48 shifts (48% from 7 a.m. to 7:30 p.m. and 52% from 7:15 p.m. to 7:15 a.m.) between November 1st, 2019 and February 29th, 2020 were selected and extracted. The average sound level was 66 ± 6.1 dBA (Figure 1). Sound levels significantly differed between daytime and night-time (67 ± 6.7 dBA vs. 64 ± 4.2 dBA, p < 0.001) (Figure 2). The highest average sound level was 89 dBA and was recorded on Monday between 12 a.m. and 1 p.m. The lowest one was 31 dBA and was recorded between 3 p.m. and 4 p.m. on Sunday. In only 2.8% of samples, noise levels during daytime were below the 35 dBA recommended by the WHO. During the night, sound levels were always above the recommended 30 dBA.

Conclusion

The analysis of noise levels in the ICU using an Apple Watch is feasible and easy to perform. We found that overall, noise levels were almost always above the WHO recommended values, consistent with previously published studies. The role of wearable devices to measure noise levels deserves to be further investigated. Apart from healthcare professionals, such devices (provided or personal) might also be worn by patients to more accurately quantify noise levels and compare with the quality of sleep and recovery after ICU discharge. Effective interventions to reduce noise pollution and improve sleep during ICU stay are highly needed.

An open-source Jupyter notebook is available on GitHub with a step-by-step guide to analyse data from other ICUs and repeat our experience.

Published by Tommaso Scquizzato

Tommaso Scquizzato is a medical student at Vita-Salute San Raffaele University in Milan, Italy interested in Critical Care and Emergency Medicine with particular focus on Resuscitation and Cardiac Arrest science. He does research in the Department of Anesthesia and Intensive Care of San Raffaele Hospital in Milan, Italy. Tommaso is also a software developer skilled in designing and developing mobile apps.

Leave a comment

Your email address will not be published. Required fields are marked *