graphomotor.plot.feature_plots
Feature visualization functions for Graphomotor.
This module provides plotting functions for visualizing extracted features from spiral
drawing data. The plotting functions expect CSV files with the first 5 columns reserved
for metadata (source_file
, participant_id
, task
, hand
, start_time
), and treat
all subsequent columns as numerical features.
Available Features
The graphomotor toolkit extracts 25 features from spiral drawing data. For a complete list of all available features, see the features module documentation.
Custom Features
Users can add custom feature columns to their CSV files alongside the standard graphomotor features. Any additional columns after the first 5 metadata columns will be automatically detected and available for plotting.
Plot Types
- Distribution plots: Kernel density estimation plots showing feature distributions grouped by task type and hand
- Trend plots: Line plots displaying feature progression across task sequences
- Box plots: Box-and-whisker plots comparing distributions across conditions
- Cluster heatmaps: Hierarchically clustered heatmaps of standardized features
1"""Feature visualization functions for Graphomotor. 2 3This module provides plotting functions for visualizing extracted features from spiral 4drawing data. The plotting functions expect CSV files with the first 5 columns reserved 5for metadata (`source_file`, `participant_id`, `task`, `hand`, `start_time`), and treat 6all subsequent columns as numerical features. 7 8Available Features 9------------------ 10The graphomotor toolkit extracts 25 features from spiral drawing data. 11For a complete list of all available features, see the 12[features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 13 14Custom Features 15--------------- 16Users can add custom feature columns to their CSV files alongside the standard 17graphomotor features. Any additional columns after the first 5 metadata columns 18will be automatically detected and available for plotting. 19 20Plot Types 21---------- 22- **Distribution plots**: Kernel density estimation plots showing feature distributions 23 grouped by task type and hand 24- **Trend plots**: Line plots displaying feature progression across task sequences 25- **Box plots**: Box-and-whisker plots comparing distributions across conditions 26- **Cluster heatmaps**: Hierarchically clustered heatmaps of standardized features 27""" 28 29import pathlib 30import warnings 31 32import matplotlib 33import pandas as pd 34import seaborn as sns 35from matplotlib import figure 36from matplotlib import pyplot as plt 37 38from graphomotor.core import config 39from graphomotor.utils import plotting 40 41matplotlib.use("agg") # prevent interactive matplotlib 42logger = config.get_logger() 43 44 45def plot_feature_distributions( 46 data: str | pathlib.Path | pd.DataFrame, 47 output_path: str | pathlib.Path | None = None, 48 features: list[str] | None = None, 49) -> figure.Figure: 50 """Plot histograms for each feature grouped by task type and hand. 51 52 This function creates kernel density estimation plots showing feature distributions 53 grouped by task type (trace/recall) and hand (Dom/NonDom). The input data should 54 be a CSV file with the first 5 columns reserved for metadata (`source_file`, 55 `participant_id`, `task`, `hand`, `start_time`), with all subsequent columns treated 56 as numerical features. 57 58 Both standard graphomotor features and custom feature columns added by users 59 are supported. For a complete list of the 25 standard features available from 60 the graphomotor extraction pipeline, see the 61 [features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 62 63 Args: 64 data: Path to CSV file containing features or pandas DataFrame. Input data 65 should have the first 5 columns as metadata (`source_file`, 66 `participant_id`, `task`, `hand`, `start_time`) followed by numerical 67 feature columns. 68 output_path: Optional directory where the figure will be saved. If None, 69 the function only returns the figure without saving. 70 features: List of specific features to plot, if None plots all features. 71 Can include any of the 25 standard graphomotor features (see module 72 docstring) or custom feature columns added to the CSV file. 73 74 Returns: 75 The matplotlib Figure. 76 """ 77 logger.debug("Starting feature distributions plot generation") 78 79 plot_data, features, _ = plotting.prepare_feature_plot_data(data, features) 80 81 hands = plot_data["hand"].unique() 82 task_types = plot_data["task_type"].unique() 83 84 colors = { 85 (hand, task_type): plt.get_cmap("tab20")(i) 86 for i, (hand, task_type) in enumerate( 87 [(h, t) for h in hands for t in task_types] 88 ) 89 } 90 91 fig, axes = plotting.init_feature_subplots(len(features)) 92 for i, feature in enumerate(features): 93 ax = axes[i] 94 95 for hand in hands: 96 for task_type in task_types: 97 subset = plot_data[ 98 (plot_data["hand"] == hand) & (plot_data["task_type"] == task_type) 99 ] 100 sns.kdeplot( 101 data=subset, 102 x=feature, 103 fill=True, 104 cut=0, 105 alpha=0.6, 106 color=colors[(hand, task_type)], 107 label=f"{hand} - {task_type.capitalize()}", 108 ax=ax, 109 ) 110 111 display_name = plotting.format_feature_name(feature) 112 ax.set_title(display_name) 113 ax.set_xlabel(display_name) 114 ax.set_ylabel("Density") 115 ax.legend(title="Hand - Task Type") 116 ax.grid(alpha=0.3) 117 118 plotting.hide_extra_axes(axes, len(features)) 119 120 plt.tight_layout() 121 plt.suptitle( 122 "Feature Distributions across Task Types and Hands", 123 y=1.01, 124 fontsize=10 + len(axes) // 2, 125 ) 126 127 if output_path: 128 plotting.save_figure(output_path, "feature_distributions") 129 else: 130 logger.debug("Feature distributions plot generated but not saved") 131 132 return fig 133 134 135def plot_feature_trends( 136 data: str | pathlib.Path | pd.DataFrame, 137 output_path: str | pathlib.Path | None = None, 138 features: list[str] | None = None, 139) -> figure.Figure: 140 """Plot lineplots to compare feature values across conditions per participant. 141 142 This function creates line plots displaying feature progression across task 143 sequences with individual participant trajectories and group means. The input 144 data should be a CSV file with the first 5 columns reserved for metadata 145 (`source_file`, `participant_id`, `task`, `hand`, `start_time`), with all subsequent 146 columns treated as numerical features. 147 148 Both standard graphomotor features and custom feature columns added by users 149 are supported. For a complete list of the 25 standard features available from 150 the graphomotor extraction pipeline, see the 151 [features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 152 153 Args: 154 data: Path to CSV file containing features or pandas DataFrame. Input data 155 should have the first 5 columns as metadata (`source_file`, 156 `participant_id`, `task`, `hand`, `start_time`) followed by numerical 157 feature columns. 158 output_path: Optional directory where the figure will be saved. If None, 159 the function only returns the figure without saving. 160 features: List of specific features to plot, if None plots all features. 161 Can include any of the 25 standard graphomotor features (see module 162 docstring) or custom feature columns added to the CSV file. 163 164 Returns: 165 The matplotlib Figure. 166 """ 167 logger.debug("Starting feature trends plot generation") 168 169 plot_data, features, tasks = plotting.prepare_feature_plot_data(data, features) 170 logger.debug(f"Plotting trends across {len(tasks)} tasks") 171 172 fig, axes = plotting.init_feature_subplots(len(features)) 173 for i, feature in enumerate(features): 174 ax = axes[i] 175 sns.lineplot( 176 data=plot_data, 177 x="task_order", 178 y=feature, 179 hue="hand", 180 units="participant_id", 181 estimator=None, 182 alpha=0.2, 183 linewidth=0.5, 184 legend=False, 185 ax=ax, 186 ) 187 sns.lineplot( 188 data=plot_data, 189 x="task_order", 190 y=feature, 191 hue="hand", 192 estimator="mean", 193 errorbar=None, 194 linewidth=2, 195 marker="o", 196 markersize=4, 197 ax=ax, 198 ) 199 display_name = plotting.format_feature_name(feature) 200 ax.set_title(display_name) 201 ax.set_ylabel(display_name) 202 ax.set_xlabel("Task") 203 ax.set_xticks(list(range(1, len(tasks) + 1))) 204 ax.set_xticklabels(tasks, rotation=45, ha="right") 205 ax.legend(title="Hand") 206 ax.grid(alpha=0.3) 207 208 plotting.hide_extra_axes(axes, len(features)) 209 210 plt.tight_layout() 211 plt.suptitle( 212 "Feature Trends across Tasks and Hands", y=1.01, fontsize=10 + len(axes) // 2 213 ) 214 215 if output_path: 216 plotting.save_figure(output_path, "feature_trends") 217 else: 218 logger.debug("Feature trends plot generated but not saved") 219 220 return fig 221 222 223def plot_feature_boxplots( 224 data: str | pathlib.Path | pd.DataFrame, 225 output_path: str | pathlib.Path | None = None, 226 features: list[str] | None = None, 227) -> figure.Figure: 228 """Plot boxplots to compare feature distributions across conditions. 229 230 This function creates box-and-whisker plots comparing feature distributions 231 across different tasks and hand conditions. The input data should be a CSV 232 file with the first 5 columns reserved for metadata (`source_file`, 233 `participant_id`, `task`, `hand`, `start_time`), with all subsequent columns treated 234 as numerical features. 235 236 Both standard graphomotor features and custom feature columns added by users 237 are supported. For a complete list of the 25 standard features available from 238 the graphomotor extraction pipeline, see the 239 [features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 240 241 Args: 242 data: Path to CSV file containing features or pandas DataFrame. Input data 243 should have the first 5 columns as metadata (`source_file`, 244 `participant_id`, `task`, `hand`, `start_time`) followed by numerical 245 feature columns. 246 output_path: Optional directory where the figure will be saved. If None, 247 the function only returns the figure without saving. 248 features: List of specific features to plot, if None plots all features. 249 Can include any of the 25 standard graphomotor features (see module 250 docstring) or custom feature columns added to the CSV file. 251 252 Returns: 253 The matplotlib Figure. 254 """ 255 logger.debug("Starting feature boxplots generation") 256 257 plot_data, features, tasks = plotting.prepare_feature_plot_data(data, features) 258 logger.debug(f"Creating boxplots across {len(tasks)} tasks") 259 260 fig, axes = plotting.init_feature_subplots(len(features)) 261 for i, feature in enumerate(features): 262 ax = axes[i] 263 264 # Suppress seaborn's internal deprecation warning about 'vert' parameter 265 with warnings.catch_warnings(): 266 warnings.filterwarnings( 267 "ignore", 268 category=PendingDeprecationWarning, 269 message="vert: bool will be deprecated.*", 270 ) 271 sns.boxplot( 272 data=plot_data, 273 x="task", 274 y=feature, 275 hue="hand", 276 order=tasks, 277 ax=ax, 278 ) 279 280 display_name = plotting.format_feature_name(feature) 281 ax.set_title(display_name) 282 ax.set_ylabel(display_name) 283 ax.set_xlabel("Task") 284 ax.set_xticks(list(range(len(tasks)))) 285 ax.set_xticklabels(tasks, rotation=45, ha="right") 286 ax.legend(title="Hand") 287 ax.grid(alpha=0.3) 288 289 plotting.hide_extra_axes(axes, len(features)) 290 291 plt.tight_layout() 292 plt.suptitle( 293 "Feature Boxplots across Tasks and Hands", y=1.01, fontsize=10 + len(axes) // 2 294 ) 295 296 if output_path: 297 plotting.save_figure(output_path, "feature_boxplots") 298 else: 299 logger.debug("Feature boxplots generated but not saved") 300 301 return fig 302 303 304def plot_feature_clusters( 305 data: str | pathlib.Path | pd.DataFrame, 306 output_path: str | pathlib.Path | None = None, 307 features: list[str] | None = None, 308) -> figure.Figure: 309 """Plot clustered heatmap of standardized feature values across conditions. 310 311 This function creates a hierarchically clustered heatmap that visualizes the median 312 feature values across conditions. Values are z-score standardized across features to 313 allow comparison when features are on different scales. Both features and 314 conditions are hierarchically clustered to highlight groups of similar feature 315 response patterns and conditions that elicit similar profiles. 316 317 The input data should be a CSV file with the first 5 columns reserved for metadata 318 (`source_file`, `participant_id`, `task`, `hand`, `start_time`), with all subsequent 319 columns treated as numerical features. Both standard graphomotor features and custom 320 feature columns added by users are supported. For a complete list of the 25 321 standard features available from the graphomotor extraction pipeline, see the 322 [features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 323 324 Args: 325 data: Path to CSV file containing features or pandas DataFrame. Input data 326 should have the first 5 columns as metadata (`source_file`, 327 `participant_id`, `task`, `hand`, `start_time`) followed by numerical 328 feature columns. 329 output_path: Optional directory where the figure will be saved. If None, 330 the function only returns the figure without saving. 331 features: List of specific features to plot, if None plots all features. 332 Can include any of the 25 standard graphomotor features (see module 333 docstring) or custom feature columns added to the CSV file. 334 335 Returns: 336 The matplotlib Figure. 337 338 Raises: 339 ValueError: If less than 2 features are provided. 340 """ 341 logger.debug("Starting feature clusters heatmap generation") 342 343 plot_data, features, _ = plotting.prepare_feature_plot_data(data, features) 344 345 if len(features) < 2: 346 error_msg = ( 347 f"At least 2 features required for clustered heatmap, got {len(features)}" 348 ) 349 logger.error(error_msg) 350 raise ValueError(error_msg) 351 352 plot_data["condition"] = plot_data["task"] + "_" + plot_data["hand"] 353 354 condition_medians = plot_data.groupby("condition")[features].median() 355 356 heatmap_data = condition_medians.T 357 logger.debug(f"Heatmap data shape: {heatmap_data.shape} for (features, conditions)") 358 359 width = max(10, len(heatmap_data.columns) * 0.8) 360 height = max(6, len(heatmap_data.index) * 0.3) 361 362 grid = sns.clustermap( 363 heatmap_data, 364 z_score=0, 365 figsize=(width, height), 366 cbar_kws={ 367 "label": "z-score", 368 "location": "bottom", 369 "orientation": "horizontal", 370 }, 371 cbar_pos=(0.025, 0.93, 0.1 + 0.001 * width, 0.02 + 0.001 * height), 372 center=0, 373 cmap="coolwarm", 374 linewidths=0.1, 375 linecolor="black", 376 ) 377 378 grid.figure.suptitle( 379 "Feature Clusters Across Conditions", 380 fontsize=14, 381 y=1.01, 382 ) 383 grid.ax_heatmap.set_xlabel("Condition") 384 grid.ax_heatmap.set_ylabel("Feature") 385 grid.ax_heatmap.set_yticklabels(grid.ax_heatmap.get_yticklabels(), rotation=0) 386 grid.ax_heatmap.set_xticklabels( 387 grid.ax_heatmap.get_xticklabels(), rotation=45, ha="right" 388 ) 389 390 if output_path: 391 plotting.save_figure(output_path, "feature_clusters") 392 else: 393 logger.debug("Feature clusters heatmap generated but not saved") 394 395 return grid.figure
46def plot_feature_distributions( 47 data: str | pathlib.Path | pd.DataFrame, 48 output_path: str | pathlib.Path | None = None, 49 features: list[str] | None = None, 50) -> figure.Figure: 51 """Plot histograms for each feature grouped by task type and hand. 52 53 This function creates kernel density estimation plots showing feature distributions 54 grouped by task type (trace/recall) and hand (Dom/NonDom). The input data should 55 be a CSV file with the first 5 columns reserved for metadata (`source_file`, 56 `participant_id`, `task`, `hand`, `start_time`), with all subsequent columns treated 57 as numerical features. 58 59 Both standard graphomotor features and custom feature columns added by users 60 are supported. For a complete list of the 25 standard features available from 61 the graphomotor extraction pipeline, see the 62 [features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 63 64 Args: 65 data: Path to CSV file containing features or pandas DataFrame. Input data 66 should have the first 5 columns as metadata (`source_file`, 67 `participant_id`, `task`, `hand`, `start_time`) followed by numerical 68 feature columns. 69 output_path: Optional directory where the figure will be saved. If None, 70 the function only returns the figure without saving. 71 features: List of specific features to plot, if None plots all features. 72 Can include any of the 25 standard graphomotor features (see module 73 docstring) or custom feature columns added to the CSV file. 74 75 Returns: 76 The matplotlib Figure. 77 """ 78 logger.debug("Starting feature distributions plot generation") 79 80 plot_data, features, _ = plotting.prepare_feature_plot_data(data, features) 81 82 hands = plot_data["hand"].unique() 83 task_types = plot_data["task_type"].unique() 84 85 colors = { 86 (hand, task_type): plt.get_cmap("tab20")(i) 87 for i, (hand, task_type) in enumerate( 88 [(h, t) for h in hands for t in task_types] 89 ) 90 } 91 92 fig, axes = plotting.init_feature_subplots(len(features)) 93 for i, feature in enumerate(features): 94 ax = axes[i] 95 96 for hand in hands: 97 for task_type in task_types: 98 subset = plot_data[ 99 (plot_data["hand"] == hand) & (plot_data["task_type"] == task_type) 100 ] 101 sns.kdeplot( 102 data=subset, 103 x=feature, 104 fill=True, 105 cut=0, 106 alpha=0.6, 107 color=colors[(hand, task_type)], 108 label=f"{hand} - {task_type.capitalize()}", 109 ax=ax, 110 ) 111 112 display_name = plotting.format_feature_name(feature) 113 ax.set_title(display_name) 114 ax.set_xlabel(display_name) 115 ax.set_ylabel("Density") 116 ax.legend(title="Hand - Task Type") 117 ax.grid(alpha=0.3) 118 119 plotting.hide_extra_axes(axes, len(features)) 120 121 plt.tight_layout() 122 plt.suptitle( 123 "Feature Distributions across Task Types and Hands", 124 y=1.01, 125 fontsize=10 + len(axes) // 2, 126 ) 127 128 if output_path: 129 plotting.save_figure(output_path, "feature_distributions") 130 else: 131 logger.debug("Feature distributions plot generated but not saved") 132 133 return fig
Plot histograms for each feature grouped by task type and hand.
This function creates kernel density estimation plots showing feature distributions
grouped by task type (trace/recall) and hand (Dom/NonDom). The input data should
be a CSV file with the first 5 columns reserved for metadata (source_file
,
participant_id
, task
, hand
, start_time
), with all subsequent columns treated
as numerical features.
Both standard graphomotor features and custom feature columns added by users are supported. For a complete list of the 25 standard features available from the graphomotor extraction pipeline, see the features module documentation.
Arguments:
- data: Path to CSV file containing features or pandas DataFrame. Input data
should have the first 5 columns as metadata (
source_file
,participant_id
,task
,hand
,start_time
) followed by numerical feature columns. - output_path: Optional directory where the figure will be saved. If None, the function only returns the figure without saving.
- features: List of specific features to plot, if None plots all features. Can include any of the 25 standard graphomotor features (see module docstring) or custom feature columns added to the CSV file.
Returns:
The matplotlib Figure.
136def plot_feature_trends( 137 data: str | pathlib.Path | pd.DataFrame, 138 output_path: str | pathlib.Path | None = None, 139 features: list[str] | None = None, 140) -> figure.Figure: 141 """Plot lineplots to compare feature values across conditions per participant. 142 143 This function creates line plots displaying feature progression across task 144 sequences with individual participant trajectories and group means. The input 145 data should be a CSV file with the first 5 columns reserved for metadata 146 (`source_file`, `participant_id`, `task`, `hand`, `start_time`), with all subsequent 147 columns treated as numerical features. 148 149 Both standard graphomotor features and custom feature columns added by users 150 are supported. For a complete list of the 25 standard features available from 151 the graphomotor extraction pipeline, see the 152 [features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 153 154 Args: 155 data: Path to CSV file containing features or pandas DataFrame. Input data 156 should have the first 5 columns as metadata (`source_file`, 157 `participant_id`, `task`, `hand`, `start_time`) followed by numerical 158 feature columns. 159 output_path: Optional directory where the figure will be saved. If None, 160 the function only returns the figure without saving. 161 features: List of specific features to plot, if None plots all features. 162 Can include any of the 25 standard graphomotor features (see module 163 docstring) or custom feature columns added to the CSV file. 164 165 Returns: 166 The matplotlib Figure. 167 """ 168 logger.debug("Starting feature trends plot generation") 169 170 plot_data, features, tasks = plotting.prepare_feature_plot_data(data, features) 171 logger.debug(f"Plotting trends across {len(tasks)} tasks") 172 173 fig, axes = plotting.init_feature_subplots(len(features)) 174 for i, feature in enumerate(features): 175 ax = axes[i] 176 sns.lineplot( 177 data=plot_data, 178 x="task_order", 179 y=feature, 180 hue="hand", 181 units="participant_id", 182 estimator=None, 183 alpha=0.2, 184 linewidth=0.5, 185 legend=False, 186 ax=ax, 187 ) 188 sns.lineplot( 189 data=plot_data, 190 x="task_order", 191 y=feature, 192 hue="hand", 193 estimator="mean", 194 errorbar=None, 195 linewidth=2, 196 marker="o", 197 markersize=4, 198 ax=ax, 199 ) 200 display_name = plotting.format_feature_name(feature) 201 ax.set_title(display_name) 202 ax.set_ylabel(display_name) 203 ax.set_xlabel("Task") 204 ax.set_xticks(list(range(1, len(tasks) + 1))) 205 ax.set_xticklabels(tasks, rotation=45, ha="right") 206 ax.legend(title="Hand") 207 ax.grid(alpha=0.3) 208 209 plotting.hide_extra_axes(axes, len(features)) 210 211 plt.tight_layout() 212 plt.suptitle( 213 "Feature Trends across Tasks and Hands", y=1.01, fontsize=10 + len(axes) // 2 214 ) 215 216 if output_path: 217 plotting.save_figure(output_path, "feature_trends") 218 else: 219 logger.debug("Feature trends plot generated but not saved") 220 221 return fig
Plot lineplots to compare feature values across conditions per participant.
This function creates line plots displaying feature progression across task
sequences with individual participant trajectories and group means. The input
data should be a CSV file with the first 5 columns reserved for metadata
(source_file
, participant_id
, task
, hand
, start_time
), with all subsequent
columns treated as numerical features.
Both standard graphomotor features and custom feature columns added by users are supported. For a complete list of the 25 standard features available from the graphomotor extraction pipeline, see the features module documentation.
Arguments:
- data: Path to CSV file containing features or pandas DataFrame. Input data
should have the first 5 columns as metadata (
source_file
,participant_id
,task
,hand
,start_time
) followed by numerical feature columns. - output_path: Optional directory where the figure will be saved. If None, the function only returns the figure without saving.
- features: List of specific features to plot, if None plots all features. Can include any of the 25 standard graphomotor features (see module docstring) or custom feature columns added to the CSV file.
Returns:
The matplotlib Figure.
224def plot_feature_boxplots( 225 data: str | pathlib.Path | pd.DataFrame, 226 output_path: str | pathlib.Path | None = None, 227 features: list[str] | None = None, 228) -> figure.Figure: 229 """Plot boxplots to compare feature distributions across conditions. 230 231 This function creates box-and-whisker plots comparing feature distributions 232 across different tasks and hand conditions. The input data should be a CSV 233 file with the first 5 columns reserved for metadata (`source_file`, 234 `participant_id`, `task`, `hand`, `start_time`), with all subsequent columns treated 235 as numerical features. 236 237 Both standard graphomotor features and custom feature columns added by users 238 are supported. For a complete list of the 25 standard features available from 239 the graphomotor extraction pipeline, see the 240 [features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 241 242 Args: 243 data: Path to CSV file containing features or pandas DataFrame. Input data 244 should have the first 5 columns as metadata (`source_file`, 245 `participant_id`, `task`, `hand`, `start_time`) followed by numerical 246 feature columns. 247 output_path: Optional directory where the figure will be saved. If None, 248 the function only returns the figure without saving. 249 features: List of specific features to plot, if None plots all features. 250 Can include any of the 25 standard graphomotor features (see module 251 docstring) or custom feature columns added to the CSV file. 252 253 Returns: 254 The matplotlib Figure. 255 """ 256 logger.debug("Starting feature boxplots generation") 257 258 plot_data, features, tasks = plotting.prepare_feature_plot_data(data, features) 259 logger.debug(f"Creating boxplots across {len(tasks)} tasks") 260 261 fig, axes = plotting.init_feature_subplots(len(features)) 262 for i, feature in enumerate(features): 263 ax = axes[i] 264 265 # Suppress seaborn's internal deprecation warning about 'vert' parameter 266 with warnings.catch_warnings(): 267 warnings.filterwarnings( 268 "ignore", 269 category=PendingDeprecationWarning, 270 message="vert: bool will be deprecated.*", 271 ) 272 sns.boxplot( 273 data=plot_data, 274 x="task", 275 y=feature, 276 hue="hand", 277 order=tasks, 278 ax=ax, 279 ) 280 281 display_name = plotting.format_feature_name(feature) 282 ax.set_title(display_name) 283 ax.set_ylabel(display_name) 284 ax.set_xlabel("Task") 285 ax.set_xticks(list(range(len(tasks)))) 286 ax.set_xticklabels(tasks, rotation=45, ha="right") 287 ax.legend(title="Hand") 288 ax.grid(alpha=0.3) 289 290 plotting.hide_extra_axes(axes, len(features)) 291 292 plt.tight_layout() 293 plt.suptitle( 294 "Feature Boxplots across Tasks and Hands", y=1.01, fontsize=10 + len(axes) // 2 295 ) 296 297 if output_path: 298 plotting.save_figure(output_path, "feature_boxplots") 299 else: 300 logger.debug("Feature boxplots generated but not saved") 301 302 return fig
Plot boxplots to compare feature distributions across conditions.
This function creates box-and-whisker plots comparing feature distributions
across different tasks and hand conditions. The input data should be a CSV
file with the first 5 columns reserved for metadata (source_file
,
participant_id
, task
, hand
, start_time
), with all subsequent columns treated
as numerical features.
Both standard graphomotor features and custom feature columns added by users are supported. For a complete list of the 25 standard features available from the graphomotor extraction pipeline, see the features module documentation.
Arguments:
- data: Path to CSV file containing features or pandas DataFrame. Input data
should have the first 5 columns as metadata (
source_file
,participant_id
,task
,hand
,start_time
) followed by numerical feature columns. - output_path: Optional directory where the figure will be saved. If None, the function only returns the figure without saving.
- features: List of specific features to plot, if None plots all features. Can include any of the 25 standard graphomotor features (see module docstring) or custom feature columns added to the CSV file.
Returns:
The matplotlib Figure.
305def plot_feature_clusters( 306 data: str | pathlib.Path | pd.DataFrame, 307 output_path: str | pathlib.Path | None = None, 308 features: list[str] | None = None, 309) -> figure.Figure: 310 """Plot clustered heatmap of standardized feature values across conditions. 311 312 This function creates a hierarchically clustered heatmap that visualizes the median 313 feature values across conditions. Values are z-score standardized across features to 314 allow comparison when features are on different scales. Both features and 315 conditions are hierarchically clustered to highlight groups of similar feature 316 response patterns and conditions that elicit similar profiles. 317 318 The input data should be a CSV file with the first 5 columns reserved for metadata 319 (`source_file`, `participant_id`, `task`, `hand`, `start_time`), with all subsequent 320 columns treated as numerical features. Both standard graphomotor features and custom 321 feature columns added by users are supported. For a complete list of the 25 322 standard features available from the graphomotor extraction pipeline, see the 323 [features module documentation](https://childmindresearch.github.io/graphomotor/graphomotor/features.html). 324 325 Args: 326 data: Path to CSV file containing features or pandas DataFrame. Input data 327 should have the first 5 columns as metadata (`source_file`, 328 `participant_id`, `task`, `hand`, `start_time`) followed by numerical 329 feature columns. 330 output_path: Optional directory where the figure will be saved. If None, 331 the function only returns the figure without saving. 332 features: List of specific features to plot, if None plots all features. 333 Can include any of the 25 standard graphomotor features (see module 334 docstring) or custom feature columns added to the CSV file. 335 336 Returns: 337 The matplotlib Figure. 338 339 Raises: 340 ValueError: If less than 2 features are provided. 341 """ 342 logger.debug("Starting feature clusters heatmap generation") 343 344 plot_data, features, _ = plotting.prepare_feature_plot_data(data, features) 345 346 if len(features) < 2: 347 error_msg = ( 348 f"At least 2 features required for clustered heatmap, got {len(features)}" 349 ) 350 logger.error(error_msg) 351 raise ValueError(error_msg) 352 353 plot_data["condition"] = plot_data["task"] + "_" + plot_data["hand"] 354 355 condition_medians = plot_data.groupby("condition")[features].median() 356 357 heatmap_data = condition_medians.T 358 logger.debug(f"Heatmap data shape: {heatmap_data.shape} for (features, conditions)") 359 360 width = max(10, len(heatmap_data.columns) * 0.8) 361 height = max(6, len(heatmap_data.index) * 0.3) 362 363 grid = sns.clustermap( 364 heatmap_data, 365 z_score=0, 366 figsize=(width, height), 367 cbar_kws={ 368 "label": "z-score", 369 "location": "bottom", 370 "orientation": "horizontal", 371 }, 372 cbar_pos=(0.025, 0.93, 0.1 + 0.001 * width, 0.02 + 0.001 * height), 373 center=0, 374 cmap="coolwarm", 375 linewidths=0.1, 376 linecolor="black", 377 ) 378 379 grid.figure.suptitle( 380 "Feature Clusters Across Conditions", 381 fontsize=14, 382 y=1.01, 383 ) 384 grid.ax_heatmap.set_xlabel("Condition") 385 grid.ax_heatmap.set_ylabel("Feature") 386 grid.ax_heatmap.set_yticklabels(grid.ax_heatmap.get_yticklabels(), rotation=0) 387 grid.ax_heatmap.set_xticklabels( 388 grid.ax_heatmap.get_xticklabels(), rotation=45, ha="right" 389 ) 390 391 if output_path: 392 plotting.save_figure(output_path, "feature_clusters") 393 else: 394 logger.debug("Feature clusters heatmap generated but not saved") 395 396 return grid.figure
Plot clustered heatmap of standardized feature values across conditions.
This function creates a hierarchically clustered heatmap that visualizes the median feature values across conditions. Values are z-score standardized across features to allow comparison when features are on different scales. Both features and conditions are hierarchically clustered to highlight groups of similar feature response patterns and conditions that elicit similar profiles.
The input data should be a CSV file with the first 5 columns reserved for metadata
(source_file
, participant_id
, task
, hand
, start_time
), with all subsequent
columns treated as numerical features. Both standard graphomotor features and custom
feature columns added by users are supported. For a complete list of the 25
standard features available from the graphomotor extraction pipeline, see the
features module documentation.
Arguments:
- data: Path to CSV file containing features or pandas DataFrame. Input data
should have the first 5 columns as metadata (
source_file
,participant_id
,task
,hand
,start_time
) followed by numerical feature columns. - output_path: Optional directory where the figure will be saved. If None, the function only returns the figure without saving.
- features: List of specific features to plot, if None plots all features. Can include any of the 25 standard graphomotor features (see module docstring) or custom feature columns added to the CSV file.
Returns:
The matplotlib Figure.
Raises:
- ValueError: If less than 2 features are provided.