code: def save_glm_to_bids(
      model,
      contrasts,
      contrast_types=None,
      out_dir=".",
      prefix=None,
  ):
      """Save :term:`GLM` results to :term:`BIDS`-like files.
  
      .. versionadded:: 0.9.2
  
      Parameters
      ----------
      model : :obj:`~nilearn.glm.first_level.FirstLevelModel` or \
      :obj:`~nilearn.glm.second_level.SecondLevelModel`
          First- or second-level model from which to save outputs.
  
      contrasts : :obj:`str` or array of shape (n_col) or :obj:`list` \
      of (:obj:`str` or array of shape (n_col)) or :obj:`dict`
          Contrast definitions. This may be one of the following:
  
              - A string
              - A list of strings
              - A dictionary of :term:`contrast` name: :term:`contrast`
                weight key-value pairs.
                The contrast weights may be strings, lists, or arrays.
                Arrays may be 1D or 2D, with 1D arrays typically being
                t-contrasts and 2D arrays typically being F-contrasts.
  
      contrast_types : None or :obj:`dict` of :obj:`str`, default=None
          An optional dictionary mapping some
          or all of the :term:`contrast` names to
          specific contrast types ('t' or 'F').
          If None, all :term:`contrast` types will
          be automatically inferred based on the :term:`contrast` arrays
          (1D arrays are t-contrasts, 2D arrays are F-contrasts).
          Keys in this dictionary must match the keys in the ``contrasts``
          dictionary, but only those contrasts
          for which :term:`contrast` type must be
          explicitly set need to be included.
  
      out_dir : :obj:`str`, optional
          Output directory for files. Default is current working directory.
  
      prefix : :obj:`str` or None, default=None
          String to prepend to generated filenames.
          If a string is provided, '_' will be added to the end.
  
      Warnings
      --------
      The files generated by this function are a best approximation of
      appropriate names for GLM-based BIDS derivatives.
      However, BIDS does not currently have GLM-based derivatives supported in
      the specification, and there is no guarantee that the files created by
      this function will be BIDS-compatible if and when the specification
      supports model derivatives.
  
      Notes
      -----
      This function writes files for the following:
  
          - Modeling software information (``dataset_description.json``)
          - Model-level metadata (``statmap.json``)
          - Model design matrix (``design.tsv``)
          - Model design matrix figure (``design.svg``)
          - Model error (``stat-errorts_statmap.nii.gz``)
          - Model r-squared (``stat-rSquare_statmap.nii.gz``)
          - Contrast :term:`'parameter estimates'<Parameter Estimate>`
            (``contrast-[name]_stat-effect_statmap.nii.gz``)
          - Variance of the contrast parameter estimates
            (``contrast-[name]_stat-variance_statmap.nii.gz``)
          - Contrast test statistics
            (``contrast-[name]_stat-[F|t]_statmap.nii.gz``)
          - Contrast p- and z-values
            (``contrast-[name]_stat-[p|z]_statmap.nii.gz``)
          - Contrast weights figure (``contrast-[name]_design.svg``)
  
      """
      # Import here to avoid circular imports
      from nilearn import glm
      from nilearn.plotting.matrix_plotting import (
          plot_contrast_matrix,
          plot_design_matrix,
      )
      from nilearn.reporting.glm_reporter import _make_stat_maps
  
      if isinstance(prefix, str) and not prefix.endswith("_"):
          prefix += "_"
      elif not isinstance(prefix, str):
          prefix = ""
  
      if isinstance(contrasts, list):
          contrasts = {c: c for c in contrasts}
      elif isinstance(contrasts, str):
          contrasts = {contrasts: contrasts}
  
      for k, v in contrasts.items():
          if not isinstance(k, str):
              raise ValueError(
                  f"contrast names must be strings, not {type(k)}"
              )
  
          if not isinstance(v, (str, np.ndarray, list)):
              raise ValueError(
                  "contrast definitions must be strings or array_likes, "
                  f"not {type(v)}"
              )
  
      out_dir = os.path.abspath(out_dir)
      os.makedirs(out_dir, exist_ok=True)
  
      model_level = (
          1 if isinstance(model, glm.first_level.FirstLevelModel) else 2
      )
  
      if not isinstance(contrast_types, dict):
          contrast_types = {}
  
      # Write out design matrices to files.
      if hasattr(model, "design_matrices_"):
          design_matrices = model.design_matrices_
      else:
          design_matrices = [model.design_matrix_]
  
      # TODO: Assuming that cases of multiple design matrices correspond to
      # different runs. Not sure if this is correct. Need to check.
      for i_run, design_matrix in enumerate(design_matrices):
          run_str = (
              f"run-{i_run + 1}_" if len(design_matrices) > 1 else ""
          )
  
          # Save design matrix and associated figure
          dm_file = os.path.join(
              out_dir,
              f"{prefix}{run_str}design.tsv",
          )
          design_matrix.to_csv(
              dm_file,
              sep="\t",
              index=False,
          )
  
          dm_fig_file = os.path.join(
              out_dir,
              f"{prefix}{run_str}design.svg",
          )
          dm_fig = plot_design_matrix(design_matrix)
          dm_fig.figure.savefig(dm_fig_file)
  
          # Save contrast plots as well
          for contrast_name, contrast_data in contrasts.items():
              contrast_plot = plot_contrast_matrix(
                  contrast_data,
                  design_matrix,
                  colorbar=True,
              )
              contrast_plot.set_xlabel(contrast_name)
              contrast_plot.figure.set_figheight(2)
              contrast_plot.figure.set_tight_layout(True)
              contrast_name = _clean_contrast_name(contrast_name)
              constrast_fig_file = os.path.join(
                  out_dir,
                  f"{prefix}{run_str}contrast-{contrast_name}_design.svg",
              )
              contrast_plot.figure.savefig(constrast_fig_file)
  
      statistical_maps = _make_stat_maps(
          model, contrasts, output_type="all"
      )
  
      # Model metadata
      # TODO: Determine optimal mapping of model metadata to BIDS fields.
      metadata_file = os.path.join(out_dir, f"{prefix}statmap.json")
      _generate_model_metadata(metadata_file, model)
  
      dset_desc_file = os.path.join(out_dir, "dataset_description.json")
      _generate_dataset_description(dset_desc_file, model_level)
  
      # Write out contrast-level statistical maps
      for contrast_name, contrast_maps in statistical_maps.items():
          # Extract stat_type
          contrast_matrix = contrasts[contrast_name]
          # Strings and 1D arrays are assumed to be t-contrasts
          if isinstance(contrast_matrix, str) or (
              contrast_matrix.ndim == 1
          ):
              stat_type = "t"
          else:
              stat_type = "F"
  
          # Override automatic detection with explicit type if provided
          stat_type = contrast_types.get(contrast_name, stat_type)
  
          # Convert the contrast name to camelCase
          contrast_name = _clean_contrast_name(contrast_name)
  
          # Contrast-level images
          contrast_level_mapping = {
              "effect_size": (
                  f"{prefix}contrast-{contrast_name}_stat-effect_statmap.nii.gz"
              ),
              "stat": (
                  f"{prefix}contrast-{contrast_name}_stat-{stat_type}_statmap"
                  ".nii.gz"
              ),
              "effect_variance": (
                  f"{prefix}contrast-{contrast_name}_stat-variance_statmap"
                  ".nii.gz"
              ),
              "z_score": (
                  f"{prefix}contrast-{contrast_name}_stat-z_statmap.nii.gz"
              ),
              "p_value": (
                  f"{prefix}contrast-{contrast_name}_stat-p_statmap.nii.gz"
              ),
          }
          # Rename keys
          renamed_contrast_maps = {
              contrast_level_mapping.get(k, k): v
              for k, v in contrast_maps.items()
          }
  
          for map_name, img in renamed_contrast_maps.items():
              out_file = os.path.join(out_dir, map_name)
              img.to_filename(out_file)
  
      # Write out model-level statistical maps
      model_level_mapping = {
          "residuals": f"{prefix}stat-errorts_statmap.nii.gz",
          "r_square": f"{prefix}stat-rSquare_statmap.nii.gz",
      }
      for attr, map_name in model_level_mapping.items():
          print(f"Extracting and saving {attr}")
          img = getattr(model, attr)
          if isinstance(img, list):
              img = img[0]
  
          out_file = os.path.join(out_dir, map_name)
          img.to_filename(out_file)
  
file_path: nilearn/nilearn/interfaces/bids/glm.py
imports: import json
  import os
  import warnings
  import numpy
  from nilearn import __version__
label_desc: Use image node_id 4 for calling a global function with example usage: save_glm_to_bids(model, contrasts, contrast_types, out_dir, prefix) without return types