Assessment of Noise in the Intensive Care Unit Using Apple Watch
By: Tommaso Scquizzato
Tags: Noise Pollution, Python, Data Science, Intensive Care, 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.

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.