[Python] How to open a nested MatLab data in Python?

Discussão em 'Python' iniciado por Stack, Setembro 3, 2025.

  1. Stack

    Stack Membro Participativo

    I have MatLab data and am trying to open it in Python data frame to conduct some statiscitcs. However RT variable (column) includes 60ish values, and I want them to be seperate: trial1, trial2... etc like this so I can conduct some descriptive statistics. In the current form, it is like one variable with lots of values inside so it does not make sense. How can I change this structure to a regular data frame, is there any other way to do it?

    import os
    import sys
    import re
    import warnings

    # Attempt to import necessary libraries.
    try:
    from scipy.io import loadmat
    import pandas as pd
    import numpy as np
    except ImportError as e:
    print(f"Error: A required library is not installed: {e.name}")
    print(f"Please install it using 'pip install {e.name}'")
    sys.exit(1)

    def extract_metadata_from_filename(filename):
    """
    Parses a filename to extract subject, session, block, and stimulation information.
    Example filename: Suj1_B1_S1_Stim1_2024-01-09_15h14m06s.mat

    Args:
    filename (str): The name of the file.

    Returns:
    A dictionary with extracted metadata or None if parsing fails.
    """
    # Updated regex pattern to find SujX, Bx, Sx, and optionally StimX
    # The '(?:_Stim(\d+))?' part makes the stimulation group optional.
    match = re.search(r'Suj(\d+)_B(\d+)_S(\d+)(?:_Stim(\d+))?', filename)
    if match:
    subject, block, session, stim = match.groups()
    return {
    'Subject': f"Subj{subject}",
    'Block': f"B{block}",
    'Session': f"S{session}",
    'Stim': f"Stim{stim}" if stim else None # Assign None if no stimulation found
    }
    return None

    def process_mat_data(filepath):
    """
    Loads a .mat file, extracts 'rt' data from 'trials', and returns
    a list of dictionaries, each ready to be added to a DataFrame.

    Args:
    filepath (str): The full path to the .mat file.

    Returns:
    A list of dictionaries with RT data and metadata.
    """
    # Extract metadata from the filename
    filename = os.path.basename(filepath)
    metadata = extract_metadata_from_filename(filename)

    if not metadata:
    print(f"Warning: Could not parse metadata from filename: {filename}")
    return []

    print(f"Processing file: {filename}")
    print(f" Extracted metadata: {metadata}")

    try:
    # Load the .mat file without squeezing to preserve the original structure.
    mat_data = loadmat(filepath, squeeze_me=False)

    rt_records = []

    # Check for the top-level 'trials' key
    if 'trials' in mat_data:
    # The 'trials' variable is a nested structure. Access its contents
    trials_object = mat_data['trials'][0, 0]

    # Now, access the fields within that structured object.
    # The RT data is nested within an array inside the structured array.
    if 'rt' in trials_object.dtype.names:
    # Access the array of RT values
    rt_values_array = trials_object['rt']

    # Check if the data is what we expect (a structured array with a single field)
    if isinstance(rt_values_array, np.ndarray) and rt_values_array.dtype.names:
    # Access the first field's data and flatten it
    rt_values = rt_values_array[rt_values_array.dtype.names[0]].flatten()
    elif isinstance(rt_values_array, np.ndarray):
    # For a simple NumPy array, just flatten it
    rt_values = rt_values_array.flatten()
    else:
    print(f"Warning: The 'rt' data for file {filename} is not a valid array structure.")
    return []

    # Iterate through the flattened RT values and create a record for each one
    for rt_value in rt_values:
    record = metadata.copy()
    record['RT'] = rt_value
    rt_records.append(record)
    else:
    print(f"Warning: 'rt' field not found in 'trials' for file: {filename}")
    return []

    # Check the number of trials processed as a sanity check
    num_trials = len(rt_records)
    if num_trials != 64:
    warnings.warn(f"Expected 64 trials but found {num_trials} in file: {filename}")

    return rt_records

    else:
    print(f"Warning: 'trials' variable not found in file: {filename}")
    return []

    except Exception as e:
    print(f"An error occurred while trying to read the file {filename}: {e}")
    return []

    def main():
    """
    Main function to traverse the directory structure, process the files,
    and perform descriptive statistics.
    """
    # Define the root directory where your 'raw_data' folder is located.
    # IMPORTANT: Change this path to match the location on your system.
    data_root_dir = './raw_data'

    if not os.path.exists(data_root_dir):
    print(f"Error: The directory '{data_root_dir}' was not found.")
    print("Please create this folder or change the 'data_root_dir' variable to the correct path.")
    sys.exit(1)

    print(f"Starting to traverse directory: {os.path.abspath(data_root_dir)}")
    print("This script will extract RT data and perform descriptive statistics.")
    print("\n" + "=" * 50 + "\n")

    # List to hold all the data records before creating the DataFrame
    all_rt_data = []

    # Use os.walk to recursively traverse the directory tree.
    for root, dirs, files in os.walk(data_root_dir):
    files.sort()
    for filename in files:
    if filename.endswith('.mat'):
    filepath = os.path.join(root, filename)
    rt_records = process_mat_data(filepath)
    all_rt_data.extend(rt_records)

    # Create the pandas DataFrame from the collected data
    if all_rt_data:
    df = pd.DataFrame(all_rt_data)

    print("\n" + "=" * 50 + "\n")
    print("DataFrame created successfully. Here is a sample:")
    print(df.head())
    print(f"\nTotal records processed: {len(df)}")

    # Perform descriptive statistics on 'RT' grouped by Subject, Session, Block, and Stim
    print("\n" + "=" * 50 + "\n")
    print("Descriptive Statistics for 'RT' (grouped by Subject, Session, Block, and Stim):")

    # Use .groupby() to perform the grouping and .describe() to get the statistics
    stats = df.groupby(['Subject', 'Session', 'Block', 'Stim'])['RT'].describe()
    print(stats)
    else:
    print("No valid 'rt' data was found to create a DataFrame.")

    if __name__ == "__main__":
    main()

    Continue reading...

Compartilhe esta Página