visualization_fa_sham_vs_stroke.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Mon Oct 28 12:21:08 2024
  4. @author: arefk
  5. """
  6. import os
  7. import pandas as pd
  8. import numpy as np
  9. import seaborn as sns
  10. import matplotlib.pyplot as plt
  11. from scipy.stats import mannwhitneyu
  12. # Get the directory where the code file is located
  13. code_dir = os.path.dirname(os.path.abspath(__file__))
  14. parent_dir = os.path.dirname(code_dir)
  15. # Make sure these variables are set according to your selected settings
  16. selected_groups = ["Sham", "Stroke"] # Example: Stroke group vs. Control group
  17. selected_qtype = "fa" # Example: fa Qtype
  18. selected_mask_names = [
  19. "CC_MOp-MOp_cut",
  20. "CRuT_MOp-RN_ipsilesional_CCcut",
  21. "CRuT_MOp-RN_contralesional_mirrored_CCcut",
  22. "CReT_MOp-TRN_ipsilesional_CCcut",
  23. "CReT_MOp-TRN_contralesional_CCcut",
  24. "CST_MOp-int-py_ipsilesional_selfdrawnROA+CCcut",
  25. "CST_MOp-int-py_contralesional_selfdrawnROA+CCcut",
  26. "TC_DORsm-SSp_ll+SSp_ul_ipsilesional_END+higherstepsize_",
  27. "TC_DORsm-SSp_ll+SSp_ul_contralesional_END+higherstepsize",
  28. "OT_och-lgn_lgncut"
  29. ] # Updated list of specific masks
  30. selected_timepoints = [28] # Example: multiple selected timepoints
  31. selected_dialation_amount = 3 # Example: dilation value for the time point
  32. # Define simple anatomical names for the masks
  33. mask_name_mapping = {
  34. "CC_MOp-MOp_cut": "Corpus Callosum",
  35. "CRuT_MOp-RN_ipsilesional_CCcut": "Rubropsinal (Ipsilesional)",
  36. "CRuT_MOp-RN_contralesional_mirrored_CCcut": "Rubropsinal (Contralesional)",
  37. "TC_DORsm-SSp_ll+SSp_ul_ipsilesional_END+higherstepsize_": "Thalamocortical (Ipsilesional)",
  38. "TC_DORsm-SSp_ll+SSp_ul_contralesional_END+higherstepsize": "Thalamocortical (Contralisional)",
  39. "CReT_MOp-TRN_contralesional_CCcut": "Reticulospinal (Contralesional)",
  40. "CReT_MOp-TRN_ipsilesional_CCcut": "Reticulospinal (Ipsilesional)",
  41. "CST_MOp-int-py_contralesional_selfdrawnROA+CCcut": "Corticospinal (Contralesional)",
  42. "CST_MOp-int-py_ipsilesional_selfdrawnROA+CCcut": "Corticospinal (Ipsilesional)",
  43. "OT_och-lgn_lgncut": "Optic"
  44. }
  45. # Load the CSV data
  46. input_file_path = os.path.join(parent_dir, 'output', "Quantitative_outputs", 'Quantitative_results_from_dwi_processing_merged_with_behavior_data.csv')
  47. df = pd.read_csv(input_file_path, low_memory=False)
  48. # Define output paths
  49. figures_folder = os.path.join(parent_dir, 'output', 'Figures')
  50. pythonFigs_folder = os.path.join(figures_folder, 'pythonFigs')
  51. # Create directories if they do not exist
  52. os.makedirs(pythonFigs_folder, exist_ok=True)
  53. # Iterate over each timepoint
  54. for timepoint in selected_timepoints:
  55. # Filter the DataFrame based on selected settings for each group and mask name
  56. df_filtered_vis_list = []
  57. for mask_name in selected_mask_names:
  58. for group in selected_groups:
  59. df_filtered = df[(df['Group'] == group) &
  60. (df['Qtype'] == selected_qtype) &
  61. (df['mask_name'] == mask_name) &
  62. (df['dialation_amount'] == selected_dialation_amount) &
  63. (df['merged_timepoint'] == timepoint)].copy()
  64. df_filtered['Region'] = mask_name_mapping.get(mask_name, mask_name) # Add simplified region name for plotting
  65. df_filtered_vis_list.append(df_filtered)
  66. # Combine the filtered DataFrames
  67. df_filtered_vis = pd.concat(df_filtered_vis_list, ignore_index=True)
  68. # Remove NaNs and ensure we have enough data for analysis
  69. df_filtered_vis = df_filtered_vis.dropna(subset=['Value'])
  70. # Plot the data if available
  71. if not df_filtered_vis.empty:
  72. # Custom colors for groups (example colors)
  73. custom_colors = {"Stroke": 'red', "Sham": 'gray'}
  74. # Create the figure
  75. fig, ax = plt.subplots(figsize=(18 / 2.54, 9 / 2.54))
  76. # Set font to Times New Roman
  77. plt.rcParams['font.family'] = 'Calibri'
  78. # Perform Mann-Whitney U test for each region to determine if there's a significant difference
  79. pval_text_list = []
  80. p_values = []
  81. simple_names = []
  82. # Print p-values in the console
  83. print(f'--- Timepoint {timepoint} ---')
  84. for mask_name in selected_mask_names:
  85. simple_name = mask_name_mapping.get(mask_name, mask_name)
  86. stroke_values = df_filtered_vis[(df_filtered_vis['Group'] == 'Stroke') & (df_filtered_vis['Region'] == simple_name)]['Value']
  87. sham_values = df_filtered_vis[(df_filtered_vis['Group'] == 'Sham') & (df_filtered_vis['Region'] == simple_name)]['Value']
  88. if len(stroke_values) > 0 and len(sham_values) > 0:
  89. stat, p_value = mannwhitneyu(stroke_values, sham_values, alternative='two-sided')
  90. else:
  91. p_value = 1.0 # Default p-value when there's insufficient data
  92. print("insufficent data")
  93. p_values.append(p_value)
  94. simple_names.append(simple_name)
  95. # Apply Bonferroni correction
  96. num_comparisons = len(p_values)
  97. corrected_p_values = [p * num_comparisons for p in p_values]
  98. # Generate p-value text with Bonferroni correction
  99. for simple_name, corrected_p_value in zip(simple_names, corrected_p_values):
  100. if corrected_p_value < 0.001:
  101. pval_text_list.append(f'Region {simple_name}: p_corrected < 0.001 (***)')
  102. elif corrected_p_value < 0.01:
  103. pval_text_list.append(f'Region {simple_name}: p_corrected < 0.01 (**)')
  104. elif corrected_p_value < 0.05:
  105. pval_text_list.append(f'Region {simple_name}: p_corrected < 0.05 (*)')
  106. else:
  107. pval_text_list.append(f'Region {simple_name}: p_corrected = {corrected_p_value:.3f} (n.s.)')
  108. pval_text_list.append(f"number of comparisons corrected for: {num_comparisons}")
  109. # Print corrected p-values in a well-formatted manner
  110. pval_text_output = "\n".join(pval_text_list)
  111. print(pval_text_output)
  112. # Save the p-value results to a text file
  113. pval_filename = f'{selected_qtype}_pvalues_timepoint_{timepoint}_dialation_{selected_dialation_amount}_regions_groups_comparison_chronic.txt'
  114. pval_file_path = os.path.join(pythonFigs_folder, pval_filename)
  115. with open(pval_file_path, 'w') as pval_file:
  116. pval_file.write(pval_text_output)
  117. # Create boxplot with dots, grouping by Region and Group
  118. sns.boxplot(x='Region', y='Value', hue='Group', data=df_filtered_vis, ax=ax, palette=custom_colors, showcaps=True,
  119. whiskerprops={'color': 'black', 'linewidth': 0.8}, fliersize=0, linewidth=1.0, boxprops={'linewidth': 0.8})
  120. sns.despine(ax=ax, right=True, top=True)
  121. sns.stripplot(x='Region', y='Value', hue='Group', data=df_filtered_vis, ax=ax, dodge=True,
  122. palette={'Stroke': '#8B0000', 'Sham': '#4F4F4F'}, alpha=1.0, size=2, jitter=True, marker='o', legend=False)
  123. # Set labels and customize font settings
  124. ax.set_ylabel('Mean FA Value [a.u]', fontsize=12, fontname='Calibri')
  125. # Remove duplicate legends
  126. ax.get_legend().remove()
  127. # Adjust layout
  128. plt.xticks(rotation=20, ha='right') # Tilt x-axis ticks
  129. plt.tight_layout() # Adjust layout to make space for the main title
  130. # Save the figure, including the region name in the filename
  131. fig_filename = f'{selected_qtype}_boxplot_timepoint_{timepoint}_dialation_{selected_dialation_amount}_regions_groups_comparison_chronic.svg'
  132. plt.savefig(os.path.join(pythonFigs_folder, fig_filename), dpi=300, bbox_inches='tight', format="svg", transparent=True)
  133. # Show the figure
  134. plt.show()
  135. else:
  136. print(f"No data available for the selected settings: Groups={selected_groups}, Qtype={selected_qtype}, Timepoint={timepoint}, Dilation value={selected_dialation_amount}, Regions={selected_mask_names}.")