diff --git a/README.md b/README.md index cc5dc0c..a09a22b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,24 @@ | Autumn Scene | 72.89 | 850.44 | 41.89 | | Breathing Scene 2 | 73.78 | 839.94 | 42.59 | + +### Timestamp Timing Analysis + +This table presents the timing analysis for each recording, focusing on durations in seconds. It includes the time from the first heart rate (HR) recording to the first timestamp marker, the intervals between consecutive timestamp markers, and the time from the last timestamp marker to the end of the HR recording. + +| Recording ID | Breathing Scene 1 (s) | Spring Scene (s) | Summer Scne (s) | Autumn Scene (s) | Breathing Scene 2 (s) | +|--------------|------------------------|------------------|------------------|------------------|---------------------------| +| 01 | 214.037 | 160.618 | 168.298 | 156.118 | 79.05 | +| 02 | 189.707 | 196.964 | 162.457 | 145.152 | 101.075 | +| 03 | 177.139 | 184.446 | 174.979 | 160.041 | 104.475 | +| 04 | 253.685 | 167.817 | 169.903 | 150.954 | 103.016 | +| 05 | 220.387 | 157.653 | 184.168 | 149.795 | 138.08 | +| 06 | 253.251 | 167.564 | 177.214 | 153.651 | 96.502 | +| 07 | 218.86 | 163.38 | 177.967 | 154.913 | 122.008 | +| 08 | 184.833 | 160.343 | 180.283 | 154.76 | 152.834 | +| 10 | 182.163 | 164.495 | 184.733 | 144.337 | 113.889 | + + ## Plots ### Aggregate Boxplots diff --git a/time_to_first_timestamp.py b/time_to_first_timestamp.py new file mode 100644 index 0000000..f39e16d --- /dev/null +++ b/time_to_first_timestamp.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +""" +Script to calculate timing analysis for meditation recordings including: +- Time from first HR recording to first timestamp marker +- Time differences between consecutive timestamps +- Time from last timestamp to end of recording +Only processes recordings that have exactly 4 timestamps. +""" + +import pandas as pd +import os + +def get_recording_timing_analysis(recording_id): + """ + Calculate comprehensive timing analysis for a given recording. + + Args: + recording_id (str): Recording ID (e.g., '01', '02', etc.) + + Returns: + dict: Complete analysis results or None if invalid + """ + recording_dir = f"SingleRecordings/{recording_id}" + + # Check if files exist + hr_file = f"{recording_dir}/hr.csv" + timestamps_file = f"{recording_dir}/timestamps.csv" + + if not os.path.exists(hr_file) or not os.path.exists(timestamps_file): + return None + + try: + # Read HR data + hr_data = pd.read_csv(hr_file) + if hr_data.empty: + return None + + # Read timestamp data + timestamps_data = pd.read_csv(timestamps_file) + if timestamps_data.empty: + return None + + # Check if there are exactly 4 timestamps + if len(timestamps_data) != 4: + return None + + # Get timestamps + first_hr_timestamp = hr_data['timestamp'].iloc[0] + last_hr_timestamp = hr_data['timestamp'].iloc[-1] + timestamp_markers = timestamps_data['timestamp'].tolist() + + # Calculate time from first HR to first timestamp + time_to_first_marker_ms = timestamp_markers[0] - first_hr_timestamp + time_to_first_marker_s = time_to_first_marker_ms / 1000.0 + + # Calculate intervals between consecutive timestamps + intervals_ms = [] + for i in range(len(timestamp_markers) - 1): + interval = timestamp_markers[i + 1] - timestamp_markers[i] + intervals_ms.append(interval) + intervals_s = [interval / 1000.0 for interval in intervals_ms] + + # Calculate time from last timestamp to end of recording + time_from_last_marker_ms = last_hr_timestamp - timestamp_markers[-1] + time_from_last_marker_s = time_from_last_marker_ms / 1000.0 + + return { + 'recording_id': recording_id, + 'time_to_first_marker_ms': time_to_first_marker_ms, + 'time_to_first_marker_s': time_to_first_marker_s, + 'interval_1_2_ms': intervals_ms[0], + 'interval_1_2_s': intervals_s[0], + 'interval_2_3_ms': intervals_ms[1], + 'interval_2_3_s': intervals_s[1], + 'interval_3_4_ms': intervals_ms[2], + 'interval_3_4_s': intervals_s[2], + 'time_from_last_marker_ms': time_from_last_marker_ms, + 'time_from_last_marker_s': time_from_last_marker_s, + 'first_hr_timestamp': first_hr_timestamp, + 'last_hr_timestamp': last_hr_timestamp, + 'timestamp_markers': timestamp_markers + } + + except Exception as e: + print(f"Error processing recording {recording_id}: {e}") + return None + +def main(): + """Main function to process all recordings and print results.""" + print("Meditation Recording Timing Analysis") + print("=" * 80) + print("Processing recordings with exactly 4 timestamps") + print("=" * 80) + + valid_recordings = [] + + # Process recordings 01-11 + for i in range(1, 12): + recording_id = f"{i:02d}" + result = get_recording_timing_analysis(recording_id) + + if result: + valid_recordings.append(result) + print(f"\nRecording {recording_id}:") + print(f" Time to first marker: {result['time_to_first_marker_s']:.3f}s ({result['time_to_first_marker_ms']:.0f}ms)") + print(f" Interval 1→2: {result['interval_1_2_s']:.3f}s ({result['interval_1_2_ms']:.0f}ms)") + print(f" Interval 2→3: {result['interval_2_3_s']:.3f}s ({result['interval_2_3_ms']:.0f}ms)") + print(f" Interval 3→4: {result['interval_3_4_s']:.3f}s ({result['interval_3_4_ms']:.0f}ms)") + print(f" Time from last marker to end: {result['time_from_last_marker_s']:.3f}s ({result['time_from_last_marker_ms']:.0f}ms)") + else: + print(f"\nRecording {recording_id}: SKIPPED (does not have exactly 4 timestamps)") + + # Create CSV output + if valid_recordings: + print(f"\n" + "=" * 80) + print(f"Creating CSV file with timing analysis...") + + # Prepare data for CSV + csv_data = [] + for result in valid_recordings: + csv_data.append({ + 'recording_id': result['recording_id'], + 'time_to_first_marker_s': result['time_to_first_marker_s'], + 'time_to_first_marker_ms': result['time_to_first_marker_ms'], + 'interval_1_2_s': result['interval_1_2_s'], + 'interval_1_2_ms': result['interval_1_2_ms'], + 'interval_2_3_s': result['interval_2_3_s'], + 'interval_2_3_ms': result['interval_2_3_ms'], + 'interval_3_4_s': result['interval_3_4_s'], + 'interval_3_4_ms': result['interval_3_4_ms'], + 'time_from_last_marker_s': result['time_from_last_marker_s'], + 'time_from_last_marker_ms': result['time_from_last_marker_ms'] + }) + + # Create DataFrame and save to CSV + df = pd.DataFrame(csv_data) + csv_filename = 'timestamp_timing_analysis.csv' + df.to_csv(csv_filename, index=False) + print(f"CSV file saved as: {csv_filename}") + + # Summary statistics + print(f"\n" + "=" * 80) + print("SUMMARY STATISTICS") + print("=" * 80) + print(f"Total recordings processed: {len(valid_recordings)}") + + # Time to first marker statistics + times_to_first = [r['time_to_first_marker_s'] for r in valid_recordings] + print(f"\nTime to first marker:") + print(f" Average: {sum(times_to_first)/len(times_to_first):.3f}s") + print(f" Min: {min(times_to_first):.3f}s") + print(f" Max: {max(times_to_first):.3f}s") + + # Interval statistics + interval_names = ['interval_1_2_s', 'interval_2_3_s', 'interval_3_4_s'] + interval_labels = ['Interval 1→2', 'Interval 2→3', 'Interval 3→4'] + + for interval_key, label in zip(interval_names, interval_labels): + interval_times = [r[interval_key] for r in valid_recordings] + print(f"\n{label}:") + print(f" Average: {sum(interval_times)/len(interval_times):.3f}s") + print(f" Min: {min(interval_times):.3f}s") + print(f" Max: {max(interval_times):.3f}s") + + # Time from last marker statistics + times_from_last = [r['time_from_last_marker_s'] for r in valid_recordings] + print(f"\nTime from last marker to end:") + print(f" Average: {sum(times_from_last)/len(times_from_last):.3f}s") + print(f" Min: {min(times_from_last):.3f}s") + print(f" Max: {max(times_from_last):.3f}s") + else: + print("\nNo valid recordings found with exactly 4 timestamps.") + +if __name__ == "__main__": + main() diff --git a/timestamp_timing_analysis.csv b/timestamp_timing_analysis.csv new file mode 100644 index 0000000..3f23f6a --- /dev/null +++ b/timestamp_timing_analysis.csv @@ -0,0 +1,10 @@ +recording_id,time_to_first_marker_s,time_to_first_marker_ms,interval_1_2_s,interval_1_2_ms,interval_2_3_s,interval_2_3_ms,interval_3_4_s,interval_3_4_ms,time_from_last_marker_s,time_from_last_marker_ms +01,214.037,214037,160.618,160618,168.298,168298,156.118,156118,79.05,79050 +02,189.707,189707,196.964,196964,162.457,162457,145.152,145152,101.075,101075 +03,177.139,177139,184.446,184446,174.979,174979,160.041,160041,104.475,104475 +04,253.685,253685,167.817,167817,169.903,169903,150.954,150954,103.016,103016 +05,220.387,220387,157.653,157653,184.168,184168,149.795,149795,138.08,138080 +06,253.251,253251,167.564,167564,177.214,177214,153.651,153651,96.502,96502 +07,218.86,218860,163.38,163380,177.967,177967,154.913,154913,122.008,122008 +08,184.833,184833,160.343,160343,180.283,180283,154.76,154760,152.834,152834 +10,182.163,182163,164.495,164495,184.733,184733,144.337,144337,113.889,113889