示例#1
0
def plot_philap_report_graphs_for_subject(subject_id, graphs_dir):
    # Download raw data if not present
    participant_details = load_philap_participant_details()

    try:
        airspeck_raw = load_personal_airspeck_file(subject_id,
                                                   upload_type='manual',
                                                   is_minute_averaged=False)
        airspeck = airspeck_raw.resample('1min').mean()
        respeck = load_respeck_file(subject_id, upload_type='manual')
    except:
        print(
            "Please download all Peeps data via download_all_philap_data(raw_airspeck=True) "
            "before calling this function")

    # Delete incorrect GPS. These coordinates are just outside the larger area of Delhi
    airspeck_raw.loc[airspeck_raw['gpsAccuracy'] > 1000,
                     'gpsLongitude':'gpsLatitude'] = np.nan
    airspeck_raw.loc[airspeck_raw['gpsLatitude'] < 10,
                     'gpsLongitude':'gpsLatitude'] = np.nan
    airspeck_raw.loc[airspeck_raw['gpsLatitude'] > 40,
                     'gpsLongitude':'gpsLatitude'] = np.nan
    airspeck_raw.loc[airspeck_raw['gpsLongitude'] < 10,
                     'gpsLongitude':'gpsLatitude'] = np.nan
    airspeck_raw.loc[airspeck_raw['gpsLongitude'] > 80,
                     'gpsLongitude':'gpsLatitude'] = np.nan

    home_gps = airspeck.loc[(1 < airspeck.index.hour)
                            & (airspeck.index.hour <= 3)].mean()
    # If there was no personal data during the night, fall back on the GPS coordinates the researchers provided
    if pd.isnull(home_gps['gpsLatitude']):
        home_gps = get_home_gps_for_subject(subject_id, participant_details)

    # Select locations near home
    radius_home = 0.002
    correction_factor = airspeck['gpsAccuracy'] * 0.00001
    home_mask = (np.abs(airspeck['gpsLatitude'] - home_gps['gpsLatitude']) < radius_home + correction_factor) & \
                (np.abs(airspeck['gpsLongitude'] - home_gps['gpsLongitude']) < radius_home + correction_factor)

    ##################################
    # Draw detailed exposure plot
    ##################################
    sns.set_style('darkgrid', {'xtick.bottom': True, 'xtick.major.size': 5})

    fig, ax = plt.subplots(figsize=(15, 5))

    if np.count_nonzero(home_mask) > 0:
        for ts in airspeck.loc[home_mask].index:
            ax.axvspan(ts,
                       ts + pd.DateOffset(minutes=1),
                       facecolor=CB_color_cycle[0],
                       alpha=0.3,
                       zorder=1)

    ax.scatter(airspeck.index, airspeck['pm2_5'], s=2, color='black', zorder=2)

    # Plot stationary airspeck home
    home_airspeck = load_static_airspeck_file(subject_id,
                                              suffix_filename='_home')
    if home_airspeck is not None and len(home_airspeck) > 0:
        ax.scatter(home_airspeck.index,
                   home_airspeck['pm2_5'],
                   s=2,
                   color='blue')

    ax.set_ylabel("PM2.5 (μg/m³)")

    start = airspeck.index[0].replace(hour=0, minute=0, second=0)
    end = airspeck.index[-1].replace(hour=0, minute=0,
                                     second=0) + pd.DateOffset(days=1)
    ax.set_xlim(start, end)

    formatter = mdates.DateFormatter('%d.%m %Hh',
                                     tz=dateutil.tz.gettz(
                                         project_mapping['philap'][1]))

    ax.xaxis.set_major_formatter(formatter)

    ax.set_title(
        "Continuous PM2.5 personal exposure levels and ambient concentrations")
    fig.autofmt_xdate()

    home_patch = mpatches.Patch(color=CB_color_cycle[0],
                                label='Home',
                                alpha=0.3)

    airs_home_patch = Line2D(range(1),
                             range(1),
                             marker='o',
                             color='#00000000',
                             markerfacecolor="blue",
                             label='Home sensor')
    airp_patch = Line2D(range(1),
                        range(1),
                        marker='o',
                        color='#00000000',
                        markerfacecolor="black",
                        label='Personal sensor')
    plt.legend(handles=[home_patch, airp_patch, airs_home_patch])

    plt.tight_layout()
    plt.savefig(graphs_dir + "{}_detailed_exposure.png".format(subject_id),
                dpi=300)
    plt.show()

    ##################################
    # Draw summary bar graph
    ##################################
    sns.set_style('darkgrid', {'xtick.bottom': False, 'xtick.major.size': 0.0})
    home = airspeck.loc[home_mask, 'pm2_5'].mean()
    other = airspeck.loc[~home_mask, 'pm2_5'].mean()
    overall = airspeck['pm2_5'].mean()
    if home_airspeck is not None:
        home_ambient = home_airspeck['pm2_5'].mean()
    else:
        home_ambient = np.nan

    mean_values = [home, other, overall, home_ambient]

    fig, ax = plt.subplots(figsize=(8, 5))
    ax.set_title(
        "Mean PM2.5 personal exposure levels and ambient concentrations")
    ax.bar(np.arange(len(mean_values)),
           mean_values,
           width=0.5,
           color=CB_color_cycle,
           edgecolor="none")
    ax.set_ylabel("PM2.5 (μg/m³)")
    ax.set_xlim(-0.5, len(mean_values) - 0.5)
    plt.xticks(np.arange(len(mean_values)), [
        "Home\npersonal", "Other\npersonal", "Overall\npersonal",
        "Home\nambient"
    ])
    plt.savefig(graphs_dir +
                "{}_mean_exposure.png".format(subject_id, subject_id),
                dpi=300)
    plt.show()

    ##################################
    # Draw map
    ##################################
    get_maps_image(airspeck_raw,
                   graphs_dir + "{}_airspeck_map.png".format(subject_id),
                   zoom=13)

    ##################################
    # Other statistics
    ##################################
    # Create new empty file
    open(graphs_dir + "{}_stats.txt".format(subject_id), 'w').close()

    # Append stats to this file
    with open(graphs_dir + "{}_stats.txt".format(subject_id), 'a') as f:
        f.write("Step count: {}\n".format(respeck['step_count'].sum()))
        f.write(
            "Mean breathing rate during night: {:.2f} breaths per minute\n".
            format(respeck.loc[(0 < respeck.index.hour) &
                               (respeck.index.hour < 6),
                               'breathing_rate'].mean()))
        f.write("Mean breathing rate during day: {:.2f} breaths per minute\n".
                format(respeck.loc[(6 <= respeck.index.hour) &
                                   (respeck.index.hour <= 23),
                                   'breathing_rate'].mean()))

        f.write("\nStart of recording: {}\n".format(
            airspeck.index[0].replace(tzinfo=None)))
        f.write("End of recording: {}\n".format(
            airspeck.index[-1].replace(tzinfo=None)))
        f.write("Total duration: {}\n".format(airspeck.index[-1] -
                                              airspeck.index[0]))

        f.write("Total recording time at home: {:.1f} h\n".format(
            np.count_nonzero(home_mask) / 60.))
def create_dublin_pixelgram_for_subject(
        subject_id, overwrite_pixelgram_if_already_exists=False):
    download_respeck_and_personal_airspeck_data(subject_id,
                                                upload_type='manual')
    respeck_data = load_respeck_file(subject_id, upload_type='manual')
    airspeck_data = load_personal_airspeck_file(subject_id,
                                                upload_type='manual')

    # Load correction factors for timezone
    corrections = pd.read_excel(dublin_timezones_correction_filepath).replace(
        np.nan, 0).set_index('subject_id')

    participant_details = load_dublin_participant_details()
    row = participant_details.loc[subject_id]

    # Load exposure period
    from_time = row['start_of_exposure_time_to_shs']
    to_time = row['end_of_exposure_time_to_shs']
    start_exposure = row['date_of_exposure_to_shs'].replace(
        hour=from_time.hour, minute=from_time.minute,
        second=from_time.second).to_pydatetime() + timedelta(
            hours=int(corrections.loc[subject_id, 'shs_times_difference']))
    if not pd.isnull(row['end_date_of_exposure_to_shs']):
        end_exposure = row['end_date_of_exposure_to_shs'].replace(
            hour=to_time.hour, minute=to_time.minute,
            second=to_time.second).to_pydatetime() + timedelta(
                hours=int(corrections.loc[subject_id, 'shs_times_difference']))
    else:
        end_exposure = row['date_of_exposure_to_shs'].replace(
            hour=to_time.hour, minute=to_time.minute,
            second=to_time.second).to_pydatetime() + timedelta(
                hours=int(corrections.loc[subject_id, 'shs_times_difference']))

    # Load recording period
    from_time = row['start_time_of_monitoring']
    start_recording = row['start_date_of_monitoring'].replace(
        hour=from_time.hour, minute=from_time.minute,
        second=from_time.second).to_pydatetime() + timedelta(
            hours=int(corrections.loc[subject_id,
                                      'recording_times_difference']))

    to_time = row['end_time_of_monitoring']
    end_recording = row['end_date_of_monitoring'].replace(
        hour=to_time.hour, minute=to_time.minute,
        second=to_time.second).to_pydatetime() + timedelta(
            hours=int(corrections.loc[subject_id,
                                      'recording_times_difference']))

    # Look up timezone
    tz = timezone(project_mapping[subject_id[:3]][1])

    print("Creating pixelgram for subject {}".format(subject_id))

    plot_combined_pixelgram_dublin(
        subject_id,
        respeck_data,
        airspeck_data,
        exposure_period=[
            tz.localize(start_exposure),
            tz.localize(end_exposure)
        ],
        recording_period=[
            tz.localize(start_recording),
            tz.localize(end_recording)
        ],
        overwrite_if_already_exists=overwrite_pixelgram_if_already_exists)
示例#3
0
def download_data_and_plot_combined_pixelgram(
        subject_id,
        timeframe=None,
        filter_out_not_worn_respeck=True,
        overwrite_pixelgram_if_already_exists=False,
        subject_visit_number=None,
        overwrite_data_if_already_exists=False,
        upload_type='automatic'):
    project_name = get_project_for_subject(subject_id)
    plot_dir = project_mapping[project_name][3]

    if subject_visit_number is None:
        label_files = "{}".format(subject_id)
    else:
        label_files = "{}({})".format(subject_id, subject_visit_number)

    pixelgram_filepath = plot_dir + "{}_combined_pixelgram.png".format(
        label_files)

    # Check if pixelgram already exists
    if not overwrite_pixelgram_if_already_exists and os.path.isfile(
            pixelgram_filepath):
        print("Pixelgram for subject {} already exists. Skipping subject.".
              format(label_files))
        return

    # Download data if not present
    download_respeck_and_personal_airspeck_data(
        subject_id,
        upload_type=upload_type,
        timeframe=timeframe,
        overwrite_if_already_exists=overwrite_data_if_already_exists,
        subject_visit_number=subject_visit_number)

    # Load data and create plot
    respeck_data = load_respeck_file(
        subject_id,
        project_name=project_name,
        upload_type=upload_type,
        subject_visit_number=subject_visit_number,
        filter_out_not_worn=filter_out_not_worn_respeck)
    airspeck_data = load_personal_airspeck_file(
        subject_id,
        project_name=project_name,
        upload_type=upload_type,
        subject_visit_number=subject_visit_number)

    if len(respeck_data) == 0:
        print("RESpeck data for subject {} empty. Skipping subject.".format(
            label_files))
        return

    if len(airspeck_data) == 0:
        print("Airspeck data for subject {} empty. Skipping subject.".format(
            label_files))
        return

    if timeframe is not None:
        tz = timezone(project_mapping[project_name][1])

        if timeframe[0].tzinfo is None:
            start_time = tz.localize(timeframe[0])
            end_time = tz.localize(timeframe[1])
        else:
            start_time = timeframe[0]
            end_time = timeframe[1]

        plot_combined_pixelgram(
            subject_id,
            respeck_data[start_time:end_time],
            airspeck_data[start_time:end_time],
            pixelgram_filepath,
            overwrite_if_already_exists=overwrite_pixelgram_if_already_exists,
            subject_visit_number=subject_visit_number)
    else:
        plot_combined_pixelgram(
            subject_id,
            respeck_data,
            airspeck_data,
            pixelgram_filepath,
            overwrite_if_already_exists=overwrite_pixelgram_if_already_exists,
            subject_visit_number=subject_visit_number)
示例#4
0
def download_respeck_data_and_plot_pixelgram(
        subject_id,
        project_name=None,
        upload_type='automatic',
        timeframe=None,
        overwrite_pixelgram_if_already_exists=False,
        filter_out_not_worn=True,
        overwrite_data_if_already_exists=False,
        subject_visit_number=None):
    if project_name is None:
        project_name = get_project_for_subject(subject_id)

    plot_dir = project_mapping[project_name][3]
    label_files = "{}({})".format(subject_id, subject_visit_number)

    pixelgram_filepath = plot_dir + "{}_respeck_pixelgram.png".format(
        label_files)

    # Check if pixelgram already exists
    if not overwrite_pixelgram_if_already_exists and os.path.isfile(
            pixelgram_filepath):
        print("Pixelgram for subject {} already exists. Skipping subject.".
              format(label_files))
        return

    # Download files if they weren't there yet before
    download_respeck_data(
        subject_id,
        upload_type=upload_type,
        timeframe=timeframe,
        overwrite_if_already_exists=overwrite_data_if_already_exists,
        subject_visit_number=subject_visit_number)

    respeck_data = load_respeck_file(subject_id,
                                     project_name,
                                     subject_visit_number=subject_visit_number,
                                     upload_type=upload_type,
                                     filter_out_not_worn=filter_out_not_worn)

    if len(respeck_data) == 0:
        print(
            "File for subject {} empty. Skipping subject.".format(subject_id))
        return

    if timeframe is not None:
        tz = timezone(project_mapping[project_name][1])

        if timeframe[0].tzinfo is None:
            start_time = tz.localize(timeframe[0])
            end_time = tz.localize(timeframe[1])
        else:
            start_time = timeframe[0]
            end_time = timeframe[1]
        plot_respeck_pixelgram(
            subject_id,
            respeck_data[start_time:end_time],
            pixelgram_filepath,
            overwrite_if_already_exists=overwrite_pixelgram_if_already_exists,
            subject_visit_number=subject_visit_number)
    else:
        plot_respeck_pixelgram(
            subject_id,
            respeck_data,
            pixelgram_filepath,
            overwrite_if_already_exists=overwrite_pixelgram_if_already_exists,
            subject_visit_number=subject_visit_number)
def plot_peeps_report_graphs_for_subject(subject_id,
                                         subject_visit_number=1,
                                         graphs_dir,
                                         max_value=100):
    # Download raw data if not present
    #download_all_peeps_data(download_raw_airspeck_data=True, phase=phase)
    participant_details = load_peeps_participant_details()

    calibration_date_pers, is_calibrated_pm_pers, airspeck = get_resampled_data(
        subject_id, subject_visit_number)

    if not is_calibrated_pm_pers:
        airspeck['pm2_5'] = airspeck['pm2_5'] * 0.60456 + 37.26849
        #calibration_date_pers = 'Calibrated with median'

    respeck = load_respeck_file(subject_id,
                                'peeps',
                                subject_visit_number=subject_visit_number,
                                upload_type='manual')

    #All subject details
    all_visit_subj_details = participant_details.loc[
        participant_details['Subject ID'] == subject_id]
    #Details for specific visit
    subj_details = all_visit_subj_details[
        all_visit_subj_details['Visit number'] == subject_visit_number]

    infer = subj_details['Infer Home Coordinates'][0]

    if infer:
        home_gps = airspeck.loc[(1 < airspeck.index.hour)
                                & (airspeck.index.hour <= 3)].mean()
        print('Subject ID: ' + str(subject_id) + ' home_gps: ' + str(home_gps))
    # If there was no personal data during the night, fall back on the GPS coordinates the researchers provided
    #if pd.isnull(home_gps['gpsLatitude']):
    else:
        home_gps = get_home_gps_for_subject(subject_id, participant_details)

    # Select locations near home
    #radius_home = 0.01
    radius_home = 0.002
    home_id = get_home_id_for_subject(subject_id, participant_details)

    correction_factor = airspeck['gpsAccuracy'] * 0.00001
    home_mask = (np.abs(airspeck['gpsLatitude'] - home_gps['gpsLatitude']) < radius_home + correction_factor) & \
                (np.abs(airspeck['gpsLongitude'] - home_gps['gpsLongitude']) < radius_home + correction_factor)

    all_factors_ids = ['33B45C90B13731DE', 'E1EFA8FCA05B3FF9']

    #Declare work and commute masks in case there is no work
    work_mask = np.zeros(len(airspeck)).astype(bool)
    commute_mask = np.zeros(len(airspeck)).astype(bool)

    radius_work = 0.01
    if subject_id in ['PEV018', 'PEV066', 'PEV047', 'PEV076']:
        radius_work = 0.002
        print('Smaller work radius set: {}'.format(radius_work))

    work_id = get_work_id_for_subject(subject_id, participant_details)
    hasWorkLocation = True
    if (work_id == 'Not deployed'):
        hasWorkLocation = False
        work_airspeck = []

    if hasWorkLocation:  #We need a work location to establish commuting

        if subject_visit_number == 1:
            work_gps = peeps_work_id_to_gps_phase1[get_work_id_for_subject(
                subject_id, participant_details)]
        if subject_visit_number == 2:
            work_gps = peeps_work_id_to_gps[get_work_id_for_subject(
                subject_id, participant_details)]

        use_all_features = False

        calibration_date_work, is_calibrated_spmwork, is_calibrated_sgaswork, work_airspeck = load_static_airspeck_file(
            work_id,
            sensor_label="{}".format(subject_id),
            suffix_filename='_work',
            project_name='peeps',
            upload_type='automatic',
            calibrate_pm_and_gas=False,
            return_calibration_flag=False,
            use_all_features_for_pm_calibration=use_all_features)

        if pd.isnull(work_gps['gpsLatitude']):
            work_airspeck_lat = work_airspeck.gpsLatitude.loc[
                work_airspeck.gpsLatitude > 0].dropna().mean()
            work_airspeck_lng = work_airspeck.gpsLongitude.loc[
                work_airspeck.gpsLongitude > 0].dropna().mean()
            work_gps = {
                'gpsLatitude': work_airspeck_lat,
                'gpsLongitude': work_airspeck_lng
            }

        if pd.isnull(work_gps['gpsLatitude']):
            print('Assuming work is wherever subjcet is between 10 and 11am')
            work_gps = airspeck.loc[(10 < airspeck.index.hour)
                                    & (airspeck.index.hour <= 11)].mean()

        work_mask = (np.abs(airspeck['gpsLatitude'] - work_gps['gpsLatitude']) < radius_work + correction_factor) & \
                (np.abs(airspeck['gpsLongitude'] - work_gps['gpsLongitude']) < radius_work + correction_factor)

        # Go through whole array and search for commuting
        begin_commute = 0
        last_location = ""
        for idx in range(1, len(airspeck)):

            if (home_mask[idx] == True and home_mask[idx - 1] == False and last_location == "work") or \
                (work_mask[idx] == True and work_mask[idx - 1] == False and last_location == "home"):
                #print('Finished commute')
                #commute_length = min(idx - begin_commute, 180) #if the commute is more than 3 hours it's not really a commute
                #print(commute_length)
                if (idx - begin_commute < 180):
                    commute_mask[begin_commute:idx] = True
            elif (home_mask[idx] == False and home_mask[idx - 1] == True):
                print('Entering leaving home commute')
                begin_commute = idx
                last_location = "home"
            elif (work_mask[idx] == False and work_mask[idx - 1] == True):
                print('Entering leaving work commute')
                begin_commute = idx
                last_location = "work"

        #print('hello2')

    ##################################
    # Draw detailed exposure plot
    ##################################
    sns.set_style('whitegrid', {'xtick.bottom': True, 'xtick.major.size': 5})

    fig, ax = plt.subplots(figsize=(15, 5))
    #print(airspeck.loc[home_mask])

    if np.count_nonzero(home_mask) > 0:
        for ts in airspeck.loc[home_mask].index:
            ax.axvspan(ts,
                       ts + pd.DateOffset(minutes=1),
                       facecolor=CB_color_cycle[0],
                       alpha=0.3,
                       zorder=1,
                       lw=0)

    if np.count_nonzero(work_mask) > 0:
        for ts in airspeck.loc[work_mask].index:
            ax.axvspan(ts,
                       ts + pd.DateOffset(minutes=1),
                       facecolor=CB_color_cycle[1],
                       alpha=0.3,
                       zorder=1,
                       lw=0)

    #print(np.count_nonzero(commute_mask))
    if np.count_nonzero(commute_mask) > 0:
        for ts in airspeck.loc[commute_mask].index:
            ax.axvspan(ts,
                       ts + pd.DateOffset(minutes=1),
                       facecolor=CB_color_cycle[2],
                       alpha=0.3,
                       zorder=1,
                       lw=0)

    #airspeck_plot = airspeck.resample('10min').mean()
    ax.scatter(airspeck.resample('10min').mean().index,
               airspeck.resample('10min').mean()['pm2_5'],
               s=2,
               color='black',
               zorder=2)

    # Plot stationary airspeck home

    use_all_features = False
    #if home_id in all_factors_ids:
    #    use_all_features = True

    calibration_date_home, is_calibrated_spmhome, is_calibrated_sgashome, home_airspeck = load_static_airspeck_file(
        home_id,
        sensor_label="{}".format(subject_id),
        suffix_filename='_home',
        project_name='peeps',
        upload_type='automatic',
        calibrate_pm_and_gas=False,
        use_all_features_for_pm_calibration=use_all_features,
        return_calibration_flag=False)

    home_airspeck = load_static_airspeck_file(subject_id,
                                              suffix_filename='_home')
    #home_aispeck_plot = home_airspeck.resample('10min').mean()
    start_personal = airspeck.index[0].replace(hour=0, minute=0, second=0)
    end_personal = airspeck.index[-1].replace(hour=0, minute=0,
                                              second=0) + pd.DateOffset(days=1)

    start_home = start_personal
    end_home = end_personal
    if len(home_airspeck) > 0:
        ax.scatter(home_airspeck.resample('10min').mean().index,
                   home_airspeck.resample('10min').mean()['pm2_5'],
                   s=2,
                   color=CB_color_cycle[0],
                   zorder=2)
        start_home = home_airspeck.index[0].replace(hour=0, minute=0, second=0)
        end_home = home_airspeck.index[-1].replace(
            hour=0, minute=0, second=0) + pd.DateOffset(days=1)

    # Plot stationary airspeck work
    #work_airpseck is already loaded above
    #work_airspeck = load_static_airspeck_file(subject_id, suffix_filename='_work')
    #work_aispeck_plot = work_airspeck.resample('10min').mean()
    start_work = start_personal
    end_work = end_personal
    if len(work_airspeck) > 0:
        ax.scatter(work_airspeck.resample('10min').mean().index,
                   work_airspeck.resample('10min').mean()['pm2_5'],
                   s=2,
                   color=CB_color_cycle[1],
                   zorder=2)
        start_work = work_airspeck.index[0].replace(hour=0, minute=0, second=0)
        end_work = work_airspeck.index[-1].replace(
            hour=0, minute=0, second=0) + pd.DateOffset(days=1)

    ax.set_ylabel("PM2.5 (μg/m³)")

    ax.set_xlim(min(start_personal, start_home, start_work),
                max(end_personal, end_home, end_work))

    formatter = mdates.DateFormatter('%d.%m %Hh',
                                     tz=dateutil.tz.gettz(
                                         project_mapping['peeps'][1]))

    ax.xaxis.set_major_formatter(formatter)

    ax.set_title(
        "Continuous PM2.5 personal exposure levels and ambient concentrations")
    fig.autofmt_xdate()

    home_patch = mpatches.Patch(color=CB_color_cycle[0],
                                label='Home',
                                alpha=0.3)
    work_patch = mpatches.Patch(color=CB_color_cycle[1],
                                label='Work',
                                alpha=0.3)
    commute_patch = mpatches.Patch(color=CB_color_cycle[2],
                                   label='Commute',
                                   alpha=0.5)

    airs_home_patch = Line2D(range(1),
                             range(1),
                             marker='o',
                             color='#00000000',
                             markerfacecolor=CB_color_cycle[0],
                             label='Home sensor')
    airp_patch = Line2D(range(1),
                        range(1),
                        marker='o',
                        color='#00000000',
                        markerfacecolor="black",
                        label='Personal sensor')
    airs_work_patch = Line2D(range(1),
                             range(1),
                             marker='o',
                             color='#00000000',
                             markerfacecolor=CB_color_cycle[1],
                             label='Work sensor')
    plt.legend(handles=[
        home_patch, work_patch, commute_patch, airp_patch, airs_home_patch,
        airs_work_patch
    ])

    plt.tight_layout()
    plt.savefig(graphs_dir + "{}_detailed_exposure.png".format(subject_id),
                dpi=300)
    plt.show()

    ##################################
    # Draw summary bar graph
    ##################################
    sns.set_style('darkgrid', {'xtick.bottom': False, 'xtick.major.size': 0.0})
    home = airspeck.loc[home_mask, 'pm2_5'].mean()
    work = airspeck.loc[work_mask, 'pm2_5'].mean()
    commute = airspeck.loc[commute_mask, 'pm2_5'].mean()
    other = airspeck.loc[~(work_mask | home_mask | commute_mask),
                         'pm2_5'].mean()
    overall = airspeck['pm2_5'].mean()
    home_ambient = home_airspeck['pm2_5'].mean()
    work_ambient = work_airspeck['pm2_5'].mean()

    mean_values = [
        home, work, commute, other, overall, home_ambient, work_ambient
    ]

    fig, ax = plt.subplots(figsize=(8, 5))
    ax.set_title(
        "Mean PM2.5 personal exposure levels and ambient concentrations")
    ax.bar(np.arange(7),
           mean_values,
           width=0.5,
           color=CB_color_cycle,
           edgecolor="none")
    plt.xticks(np.arange(7), [
        "Home\npersonal", "Work\npersonal", "Commute\npersonal",
        "Other\npersonal", "Overall\npersonal", "Home\nambient",
        "Work\nambient"
    ])
    ax.set_ylabel("PM2.5 (μg/m³)")
    plt.savefig(graphs_dir +
                "{}_mean_exposure.png".format(subject_id, subject_id),
                dpi=300)
    plt.show()

    ##################################
    # Draw map
    ##################################
    #print(airspeck)
    get_maps_image(airspeck,
                   graphs_dir + "{}_airspeck_map.png".format(subject_id),
                   zoom=13,
                   max_value=max_value)

    ##################################
    # Other statistics
    ##################################
    # Append stats to this file
    with open(graphs_dir + "{}_stats.txt".format(subject_id), 'a') as f:

        if not is_calibrated_pm_pers:
            calibration_date_pers = 'Calibrated with median'
        if not is_calibrated_spmhome:
            calibration_date_home = 'Calibrated with median'
        if not is_calibrated_spmwork:
            calibration_date_work = 'Calibrated with median'

        f.write(
            "The personal, home and work data in this report were calibrated using colocation data from the following dates. If colocation data was unavailable, a median calibration based on other similar sensors was used.\n"
        )

        f.write("Personal sensor: {}\n".format(calibration_date_pers))
        f.write("Home sensor: {}\n".format(calibration_date_home))
        f.write("Work sensor: {}\n".format(calibration_date_work))

        f.write("Step count: {}\n".format(respeck['step_count'].sum()))
        f.write(
            "Mean breathing rate during night: {:.2f} breaths per minute\n".
            format(respeck.loc[(0 < respeck.index.hour) &
                               (respeck.index.hour < 6),
                               'breathing_rate'].mean()))
        f.write("Mean breathing rate during day: {:.2f} breaths per minute\n".
                format(respeck.loc[(6 <= respeck.index.hour) &
                                   (respeck.index.hour <= 23),
                                   'breathing_rate'].mean()))

        f.write("\nStart of recording: {}\n".format(
            airspeck.index[0].replace(tzinfo=None)))
        f.write("End of recording: {}\n".format(
            airspeck.index[-1].replace(tzinfo=None)))
        f.write("Total duration: {}\n".format(airspeck.index[-1] -
                                              airspeck.index[0]))

        f.write("Total recording time at work: {:.1f} h\n".format(
            np.count_nonzero(work_mask) / 60.))
        f.write("Total recording time at home: {:.1f} h\n".format(
            np.count_nonzero(home_mask) / 60.))
        f.write(
            "Total recording time during the journey between home and work: {:.1f} h\n"
            .format(np.count_nonzero(commute_mask) / 60.))