Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior.
The 11 matches
- [1] § Methods › Social maps ↔ umap/umap_.py, lines 1485–1737 · score 0.79 · low dimensional space, dimensional embedding, dimensionality reduction, UMAP embedded, uniformly, clustered
- [2] § Methods › Social maps ↔ social_behavior_umap.ipynb, lines 357–403 · score 0.78 · watershed segmentation, kernel density, UMAP embedded, stereotypic interaction, dimensional space, clustered
- [3] § Methods › Pose tracking and processing ↔ social_behavior_umap.ipynb, lines 177–241 · score 0.71 · relative orientation, relative angle, velocities, Lateral, speed, frames
- [4] § Results › Male- and female-directed courtship-like interactions exhibit different dynamics ↔ social_behavior_umap.ipynb, lines 357–403 · score 0.70 · kernel density, low density, stereotypical interaction, social modes, watershed, UMAP
- [5] § Methods › Maps of behavioral responses to song ↔ social_behavior_umap.ipynb, lines 177–241 · score 0.66 · wavelet transform, UMAP embedding, watershed, spectrogram, pose, signal
- [6] § Methods › Maps of behavioral responses to song ↔ umap/umap_.py, lines 1485–1737 · score 0.65 · dimensional space, UMAP embedding, manifold, uniformly, density, behavioral
- [7] § Methods › HMM-GLM modeling › Generalized linear model (GLM) ↔ demo/basics.ipynb, lines 100–127 · score 0.58 · raised cosine, small delays, broaden, GLM, transformation, filters
- [8] § Methods › Pose tracking and processing › Quantifying courter position during interactions ↔ social_behavior_umap.ipynb, lines 632–688 · score 0.57 · fly length, polar, histogram, angle, binned, courter
- [9] § Methods › Pose tracking and processing ↔ umap/spectral.py, lines 163–279 · score 0.55 · metrics function, relative positioning, Euclidean, vector, component, connecting
- [10] § Methods › HMM-GLM modeling ↔ ssm/transitions.py, lines 213–263 · score 0.53 · hidden Markov model, generalized linear model, transitioning, probability
- [11] § Results › Male- and female-directed courtship-like interactions exhibit different dynamics ↔ social_behavior_umap.ipynb, lines 841–898 · score 0.52 · male female, female directed, spent, edges, social, Ratio
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Jupyter notebook · 985 lines · 41 KB · no license · 6 matches
- # %% [markdown]
- # # Social UMAP
- # This notebook contains the code to generate the social UMAPs from Nair et al. 2025, "Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior" (bioRxiv, May 2025).
- # %%
- # imports and configurations
- import os
- import pandas as pd
- import numpy as np
- from scipy import signal
- from sklearn.preprocessing import MinMaxScaler, StandardScaler
- from embedding_utils import (get_freq_scales, compute_wavelet,
- umap_embedding,
- time_delay_embedding,
- SpatialClustering,
- umap_multiple_fits,
- basis_transformation,
- kernel_density_estimation)
- from glm_utils import bases
- import matplotlib.pyplot as plt
- import seaborn as sns
- import matplotlib.colors as mcolors
- from scipy.stats import mannwhitneyu, binned_statistic_2d
- plt.style.use('ncb.mplstyle')
- colors = [mcolors.CSS4_COLORS["violet"], mcolors.CSS4_COLORS["royalblue"],
- mcolors.CSS4_COLORS["thistle"], mcolors.CSS4_COLORS["lightsteelblue"]]
- color_palette = sns.set_palette(sns.color_palette(colors))
- # %% [markdown]
- # ## How to arrange the data and specify parameters
- #
- # The data should be stored in the `dat` folder as one CSV file per trial. The columns of each CSV file are used as input features for the UMAP.
- #
- # The `trial_names` indicate the trials belonging to different groups, namely male-female and male-male interactions. The `features` list indicates the input features used to fit the UMAP, and `condition` indicates the condition on which the UMAPs are compared.
- #
- # - `start_frame_idx`: index of the first frame of each trial to be used in the analysis
- #
- # - `end_frame_idx`: index of the last frame of each trial to be used in the analysis
- #
- # - `confidence_threshold`: confidence level of tracked poses below which frames are ignored in the analysis
- #
- # ### Lowpass filtering
- # - `required_sampling_rate`: sampling rate of the data
- #
- # - `filter_features`: flag indicating whether to lowpass filter UMAP input features
- #
- # - `filter_cutoff`: cutoff frequency if lowpass filtering is used
- #
- # - `filter_ord`: filter order
- #
- # ### Normalization of input features
- # - `normalize_features`: whether to normalize the input features
- #
- # - `normalization`: what type of normalization is used, "standardize", "minmax", or `None`
- #
- # - `features_range`: for minmax normalization, the feature range to be used as minimum and maximum values. If used, this should be a dictionary whose keys are any or some of the feature names (`features`) and values are a list of two elements: the minimum and maximum value for the feature
- #
- # ### Time delay embedding parameters
- # - `time_delay_poses`: whether to use time-delay embedding of input features
- #
- # - `nb_delays`: if `time_delay_poses` is `True`, specify the number of delays to embed
- #
- # - `basis_transform`: whether to transform the basis vectors of time-delay embedded features
- #
- # - `basis_function`: if `basis_transform` is `True`, the function to be used for basis transformation
- #
- # ### Wavelet transform parameters
- # - `wavelet_transform`: whether to wavelet transform the input features. Either time-delay embedding or wavelet transform is used
- #
- # - `min_freq`: minimum frequency used for wavelet transform
- #
- # - `max_freq`: maximum frequency used for wavelet transform
- #
- # - `nfreqs`: number of frequency components to be used for wavelet transform
- #
- # - `freq_spacing`: frequency spacing to be used: 'dyadic', 'log', or 'uniform'
- #
- # ### UMAP parameters
- #
- # - `train_size`: ratio of data to be used for fitting the UMAP
- #
- # - `min_dist`: UMAP hyperparameter `min_dist` (https://umap-learn.readthedocs.io/en/latest/parameters.html)
- #
- # - `n_neighbors`: UMAP hyperparameter `n_neighbors` (https://umap-learn.readthedocs.io/en/latest/parameters.html)
- #
- # - `random_state`: random seed used to fit UMAP reproducibly
- #
- # - `nan_handling`: whether to 'remove' or 'interpolate' NaN values before analysis
- #
- # ### kernel density estimation (KDE) and watershed segmentation parameters
- # - `bw`: bandwidth to be used for KDE (https://kdepy.readthedocs.io/en/latest/introduction.html#Selecting-a-suitable-bandwidth)
- #
- # - `nb_gridpoints`: number of grid points on each 2D axis to be used for KDE
- #
- # - `watershed_threshold`: threshold to be used for watershed segmentation algorithm (see `watershed_segmentation` in `embedding_utils.py`)
- #
- # - `results_save_path`: path to save social UMAP results
- #
- # The notebook reads data from the CSV files and stores them in `preprocessed_data_path`.
- # %%
- data_save_path = "dat"
- preprocessed_data_path = os.path.join(data_save_path, "preprocessed_data.npz")
- # parameters to get data
- trial_names = {}
- trial_names['male-female'] = \
- ['localhost-20200706_122314',
- 'localhost-20200708_122011',
- 'localhost-20200707_113229',
- 'localhost-20200710_120433',
- 'localhost-20200710_134643',
- 'localhost-20200709_123809',
- 'localhost-20200709_115547',
- 'localhost-20200710_131755',
- 'localhost-20220214_114900',
- 'localhost-20220214_122614',
- 'localhost-20220214_130838',
- 'localhost-20220214_132737',
- 'localhost-20220216_093349',
- 'localhost-20220216_101942',
- 'localhost-20220218_114657',
- 'localhost-20220224_120331',
- 'localhost-20220225_121823',
- 'localhost-20220302_113245',
- 'localhost-20220302_125321',
- 'localhost-20220502_115227',
- 'localhost-20210127_145608',
- 'localhost-20210210_094512',
- 'localhost-20210210_100441',
- 'localhost-20210617_121041',
- 'localhost-20210617_123045',
- 'localhost-20220502_123531',
- 'localhost-20220506_130520',
- 'localhost-20220509_115048',
- 'localhost-20220509_124215',
- 'localhost-20220511_115515',
- ]
- trial_names['male-male'] = \
- ['localhost-20200706_132556',
- 'localhost-20200708_120051',
- 'localhost-20200812_143257',
- 'localhost-20200709_111319',
- 'localhost-20200812_155855',
- 'localhost-20210222_144954',
- 'localhost-20210222_152829',
- 'localhost-20210308_120623',
- 'localhost-20210308_122507',
- 'localhost-20220214_120801',
- 'localhost-20220214_135209',
- 'localhost-20220222_122109',
- 'localhost-20220223_121135',
- 'localhost-20220224_124055',
- 'localhost-20220224_130031',
- 'localhost-20220225_123838',
- 'localhost-20220225_125855',
- 'localhost-20220302_115211',
- 'localhost-20220302_131417',
- 'localhost-20220302_133433',
- 'localhost-20220502_125611',
- 'localhost-20210201_124220',
- 'localhost-20210201_132121',
- 'localhost-20210209_114709',
- 'localhost-20210209_122417',
- 'localhost-20220304_121729',
- 'localhost-20220304_123618',
- 'localhost-20220502_133927',
- 'localhost-20220509_121352',
- 'localhost-20220509_130538'
- ]
- features = [
- 'courter_velocity_forward',
- 'target_velocity_forward',
- 'courter_abs_velocity_lateral',
- 'target_abs_velocity_lateral',
- 'courter_angles_speed',
- 'target_angles_speed',
- 'distance_target',
- 'relative_angle_abs_target',
- 'relative_angle_abs_courter',
- 'relative_orientation_abs_target_wrap',
- ]
- condition = 'current_song'
- start_frame_idx = 2000
- end_frame_idx = -2000
- # confidence values not calibrated in SLEAP
- confidence_threshold = 0.7 #None if sleap
- # filtering
- required_sampling_rate = 30#Hz
- filter_features = False
- filter_cutoff = 10#Hz
- filter_ord = 4
- assert filter_cutoff < required_sampling_rate/2, (
- "filter_cutoff should be less than %0.2f Hz"
- %(required_sampling_rate/2))
- b, a = signal.butter(filter_ord, filter_cutoff/(0.5*required_sampling_rate))
- # use normalize_features = True only for wavelet spectrogram approach
- normalize_features = True
- normalization = "standardize" #"minmax" or None
- features_range = None
- # time delay works better than wavelet spectrogram for now
- time_delay_poses = True
- nb_delays = 15
- basis_transform = False
- basis_function = bases.raised_cosine(0, 12, [0, 12], 10, nb_delays)
- wavelet_transform = not time_delay_poses
- min_freq = 1
- max_freq = 25
- nfreqs = 25
- freq_spacing = 'dyadic'
- freqs, scales_cwt = get_freq_scales(
- min_freq, max_freq, nfreqs, required_sampling_rate,
- spacing = freq_spacing)
- # umap parameters
- train_size = 0.1
- min_dist = 0.0
- n_neighbors = 100
- random_state = 42
- nan_handling = "remove" # "interpolate" to interpolate nans or "remove" to remove nans
- if wavelet_transform:
- assert nan_handling == "interpolate", "use nan_handling as interpolate if wavelet_transform is True"
- # kde and watershed parameters
- bw = 0.5
- nb_gridpoints = 256
- watershed_threshold = 0.001
- results_save_path = f"res/umap_embedding"
- if not os.path.exists(results_save_path):
- os.makedirs(results_save_path)
- # %%
- # save umap parameters
- umap_params = {}
- umap_params = dict(
- trial_names=trial_names,
- features=features,
- condition=condition,
- required_sampling_rate=required_sampling_rate,
- data_save_path=data_save_path,
- start_frame_idx=start_frame_idx,
- end_frame_idx=end_frame_idx,
- confidence_threshold=confidence_threshold,
- filter_features=filter_features,
- filter_cutoff=filter_cutoff,
- filter_ord=filter_ord,
- normalize_features=normalize_features,
- normalization=normalization,
- features_range=features_range,
- #additional_features=additional_features,
- time_delay_poses=time_delay_poses,
- nb_delays=nb_delays,
- basis_transform = basis_transform,
- basis_function = basis_function,
- wavelet_transform=wavelet_transform,
- min_freq=min_freq,
- max_freq=max_freq,
- nfreqs=nfreqs,
- freq_spacing=freq_spacing,
- train_size=train_size,
- min_dist=min_dist,
- n_neighbors=n_neighbors,
- random_state=random_state,
- nan_handling=nan_handling,
- bw=bw,
- nb_gridpoints=nb_gridpoints,
- watershed_threshold=watershed_threshold,
- results_save_path=results_save_path)
- # %% [markdown]
- # ## Prepare the data
- #
- # Read the data from `preprocessed_data_path` if available. Otherwise, read the trial-wise CSV files in the `dat` folder. Lowpass-filter the data, remove frames below the confidence threshold, and normalize.
- # %%
- # get data
- all_trial_names = list(np.concatenate(list(trial_names.values())))
- X, y = [], []
- if os.path.exists(preprocessed_data_path):
- data = np.load(preprocessed_data_path, allow_pickle=True)
- X = list(data['X'])
- y = list(data['y'])
- else:
- for trial_name in all_trial_names:
- print(trial_name)
- df_trial_data = pd.read_csv(f'{data_save_path}/{trial_name}.csv')
- trial_X = df_trial_data[features].values[start_frame_idx:end_frame_idx]
- trial_y = df_trial_data[condition].values[start_frame_idx:end_frame_idx]
- # low pass filter data
- trial_X = signal.filtfilt(b, a, trial_X, axis=0)
- # filter out non confident frames
- if confidence_threshold is not None and 'poses_confidence' in df_trial_data:
- poses_confidence = df_trial_data['poses_confidence'].values
- confident_frames = poses_confidence>confidence_threshold
- trial_X[~confident_frames] = np.nan
- trial_y[~confident_frames] = np.nan
- # normalization
- if normalize_features:
- if normalization == 'standardize':
- trial_X = StandardScaler().fit_transform(trial_X)
- elif normalization == 'minmax':
- trial_X = MinMaxScaler().fit_transform(trial_X)
- X.append(trial_X)
- y.append(trial_y)
- np.savez(preprocessed_data_path, X=X, y=y)
- # %% [markdown]
- # ## Data processing
- #
- # Extract wavelet spectrograms or perform time-delay embedding of input features for each trial. If using wavelet spectrograms, the processed data for each trial is a 2D array of shape (`N_trial`, `len(features)` x `nfreqs`), where `N_trial` is the number of samples for the trial. If using time-delay embedding, the processed data for each trial is a 2D array of shape (`N_trial`, `len(features)` x `nb_delays`). The processed data from all trials are then pooled to form `X_proc`.
- #
- # %%
- # process data
- X_proc, y_proc = [], []
- list_timestamps = []
- for trial_X, trial_y in zip(X, y):
- N = trial_X.shape[0]
- t0=0
- dt=1/required_sampling_rate
- timestamps = np.arange(0, N, dtype="float32") * dt + t0
- if wavelet_transform:
- # Compute wavelet spectrogram
- print('Computing wavelet spectrograms ...')
- trial_X_cwt = []
- for feat_idx in range(trial_X.shape[1]):
- [P, t, f] = compute_wavelet(timestamps, trial_X[:, feat_idx],
- scales_cwt)
- trial_X_cwt.extend((P))
- trial_X_cwt = np.array(trial_X_cwt, dtype='float32').T
- X_proc.append(trial_X_cwt)
- y_proc.append(trial_y)
- print('done')
- elif time_delay_poses:
- # time delay embedding
- X_td, y_td = time_delay_embedding(
- trial_X, trial_y, nb_delays = nb_delays, multi_features=True,
- padding='same', remove_nans=True if nan_handling == "remove" else False)
- if basis_transform:
- X_td, basis_projection = basis_transformation(
- X_td, nb_delays, basis_function, multi_features=True, nb_stim=len(features))
- X_proc.append(np.array(X_td, dtype="float32"))
- y_proc.append(y_td)
- list_timestamps.append(timestamps)
- # %% [markdown]
- # ## UMAP embedding and spatial clustering using KDE and watershed segmentation
- #
- # First, the time-delayed inputs `X_proc` are embedded into a two-dimensional manifold `X_embedded`. A kernel density estimation is performed on the two-dimensional space, followed by watershed segmentation, which creates spatial clusters centered at local peaks of the kernel density estimates. From a behavioral perspective, the local peaks in the KDE correspond to stereotyped interactions between the organisms, and regions of low density correspond to transitions between stereotyped interactions. Thus, each spatial cluster is assigned to a particular interaction prototype called a social mode.
- # %%
- if os.path.exists(f'{results_save_path}/embedding_results.npz'):
- # load data
- results = np.load(f'{results_save_path}/embedding_results.npz', allow_pickle=True)
- X_proc = results['X_proc']
- y_proc = results['y_proc']
- X_embedded = results['X_embedded']
- X_embedded_kde = results['X_embedded_kde']
- X_embedded_kde_positions = results['X_embedded_kde_positions']
- X_embedded_segments = results['X_embedded_segments']
- labels = results['labels']
- positions = results['positions']
- labels_edge_positions = results['labels_edge_positions']
- else:
- #%% umap embedding
- X_embedded, reducer = umap_embedding(
- X_proc, min_dist, n_neighbors,
- n_components=2,
- train_size=train_size,
- random_state=random_state,
- )
- # kernel density estimation and watershed segmentation
- spatial_clustering = SpatialClustering(bw, nb_gridpoints, watershed_threshold)
- embedded_features_kde, positions, labels, labels_edge, labels_edge_positions = spatial_clustering.fit(X_embedded)
- X_embedded_kde, X_embedded_kde_positions, X_embedded_segments = spatial_clustering.transform(
- X_embedded, positions, labels)
- # save results
- results = {}
- results['X_proc'] = X_proc
- results['y_proc'] = y_proc
- results['timestamps'] = list_timestamps
- results['reducer'] = reducer
- results['X_embedded'] = X_embedded
- results['X_embedded_kde'] = X_embedded_kde
- results['X_embedded_kde_positions'] = X_embedded_kde_positions
- results['X_embedded_segments'] = X_embedded_segments
- results['labels'] = labels
- results['positions'] = positions
- results['labels_edge_positions'] = labels_edge_positions
- results['umap_params'] = umap_params
- np.savez(f'{results_save_path}/embedding_results.npz', results)
- # %% [markdown]
- # ## Meaning of each spatial segment (social modes)
- #
- # To understand what each spatial segment means, we first plot the time-delay embedded features for each social mode separately for male-female and male-male interactions.
- # %%
- # time-delay embedded feature values for each segment
- X_proc_mean = {}
- n_samples_label = {}
- for analysis_group in trial_names:
- X_proc_mean[analysis_group] = {}
- n_samples_label[analysis_group] = {}
- for label in np.unique(labels):
- if label == 0:
- continue
- X_proc_mean[analysis_group][label] = []
- n_samples_label[analysis_group][label] = 0
- for x_proc, x_seg, trial_id in zip(X_proc, X_embedded_segments, all_trial_names):
- if trial_id not in trial_names[analysis_group]: continue
- X_proc_mean[analysis_group][label].append(
- np.sum(x_proc[x_seg==label], axis=0)/\
- np.sum(x_seg==label))
- n_samples_label[analysis_group][label] += np.sum(x_seg==label)
- X_proc_mean[analysis_group][label] = np.array(
- X_proc_mean[analysis_group][label])
- if time_delay_poses:
- X_proc_mean[analysis_group][label] = np.reshape(
- X_proc_mean[analysis_group][label],
- (X_proc_mean[analysis_group][label].shape[0], -1, nb_delays))
- elif wavelet_transform:
- X_proc_mean[analysis_group][label] = np.reshape(
- X_proc_mean[analysis_group][label],
- (X_proc_mean[analysis_group][label].shape[0], -1, nfreqs))
- num_clusters = 12
- fig, ax = plt.subplots(
- len(X_proc_mean),
- num_clusters,
- sharex=True, sharey=True,
- num="cluster_means", figsize=(num_clusters*2, 2*len(X_proc_mean)))
- for g, group in enumerate(X_proc_mean):
- for i, (label, features_mean) in enumerate(X_proc_mean[group].items()):
- ax[g, i].set_title(f"{label} ({n_samples_label[group][label]})")
- ax[g, i].matshow(np.nanmean(features_mean, 0), vmin=-1, vmax=1, cmap="bwr")
- ax[g, i].set_yticks(np.arange(features_mean.shape[1]))
- ax[g, i].set_yticklabels(features)
- ax[g, 0].set_ylabel(group)
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # Next, we plot the average values of each input feature within each segment. Based on the speed components of each fly and their relative positioning with respect to each other, we name the modes:
- # 1. `Behind and idle`: the courter is behind the target and idle
- # 2. `Behind and close`: the courter is behind the target and close
- # 3. `Behind and chasing`: the courter is behind the target and chasing
- # 4. `Behind and circling`: the courter is behind the target and circling
- # 5. `Uninterested`: the courter and target are distant and facing away from each other
- # 6. `Front and circling`: the courter is in front of the target and circling
- # 7. `Front and close`: the courter is in front of the target and close
- # 8. `Front and idle`: the courter is in front of the target and idle
- #
- # Some segments contain only a few frames or are noisy, which we ignore.
- # %%
- # feature means for each segment
- filter_by_song = False
- ignore_states = [0, 1, 5, 9, 12]
- state_names = [
- 'Behind idle',
- 'Behind close',
- 'Behind chasing',
- 'Behind circling',
- 'Uninterested',
- 'Front circling',
- 'Front close',
- 'Front idle'
- ]
- rows = []
- for analysis_group in trial_names:
- for trial_name in trial_names[analysis_group]:
- trial_idx = trial_names[analysis_group].index(trial_name)
- trial_feature_values = StandardScaler().fit_transform(X[trial_idx])
- trial_segments = X_embedded_segments[trial_idx]
- for state in np.unique(trial_segments):
- if state in ignore_states: continue
- for f, feat in enumerate(features):
- trial_feat_segment_mean = np.nanmean(
- trial_feature_values[trial_segments==state, f])
- rows.append(
- {"trial_name": trial_name,
- "analysis_group": analysis_group,
- "state": state,
- "feature": feat,
- "zscore": trial_feat_segment_mean}
- )
- df_mean_feature_values_segments = pd.DataFrame(rows)
- # plots
- figname = 'cluster_feature_means'
- fig, ax = plt.subplots(
- 1, len(np.unique(df_mean_feature_values_segments.state)),
- figsize=(3*(len(np.unique(df_mean_feature_values_segments.state))),
- len(np.unique(df_mean_feature_values_segments.feature))),
- sharey=True,
- num=figname)
- for s, state in enumerate(np.unique(df_mean_feature_values_segments.state)):
- if state in ignore_states: continue
- df_mean_feature_values = df_mean_feature_values_segments[
- df_mean_feature_values_segments.state==state]
- sns.barplot(data=df_mean_feature_values,
- y="feature",
- x="zscore",
- ax=ax[s],
- dodge=True,
- #palette=["k"],
- orient='h')
- #sns.pointplot(data=df_mean_feature_values,
- # y="feature",
- # x="zscore",
- # dodge=0.5,
- # ax=ax[s],
- # orient='h',
- # join=False, palette=['k'])
- ax[s].set_title(f"{state_names[s]}")
- ax[s].set_yticklabels(features)
- ax[s].legend([])
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # Next, we visualize the feature values directly on the social UMAP embedding.
- # %%
- # color code embedding by feature values
- binned = True
- filter_song = False
- groupwise=False
- figname = "embedding_feat_values"
- if binned: figname = figname + "_binned"
- if groupwise: figname = figname + "_groupwise"
- if groupwise:
- feat_values_binned = {}
- fig, ax = plt.subplots(
- len(trial_names), len(features), sharex=True, sharey=True,
- num='embedding_feature_values'+("_binned" if binned else "_"),
- figsize=(len(features)*2.5, 6))
- for g, analysis_group in enumerate(trial_names):
- feat_values_binned[analysis_group] = {}
- X_embedded_concat_group = np.concatenate(
- [X_embedded[i] for i in range(len(X_embedded))
- if all_trial_names[i] in trial_names[analysis_group]], axis=0)
- X_raw_concat_group = np.concatenate(
- [X[i] for i in range(len(X))
- if all_trial_names[i] in trial_names[analysis_group]], axis=0)
- X_raw_concat_group = StandardScaler().fit_transform(X_raw_concat_group)
- for i, feature in enumerate(features):
- if binned:
- ret = binned_statistic_2d(
- X_embedded_concat_group[:, 1],
- X_embedded_concat_group[:, 0],
- X_raw_concat_group[:, i],
- np.nanmean, bins=128,
- range=[[0, 18], [0, 18]])
- feat_values_binned[analysis_group][feature] = ret
- im = ax[g, i].imshow(
- ret.statistic,
- extent=[ret.x_edge[0], ret.x_edge[-1],
- ret.y_edge[0], ret.y_edge[-1]],
- cmap='bwr', zorder=1, vmin=-1, vmax=1,)
- else:
- ax[g, i].scatter(
- *X_embedded_concat_group[::10].T, s=0.1, vmin=-1, vmax=1,
- alpha=0.25, c=X_raw_concat_group[::10, i], cmap='bwr', zorder=1)
- ax[g, i].scatter(*labels_edge_positions.T, s=0.2, c='k', zorder=2)
- ax[g, i].set_title(feature)
- ax[g, i].set_xlim(0, 18)
- ax[g, i].set_ylim(0, 18)
- ax[g, 0].set_ylabel(analysis_group)
- ax[g, i].axis("off")
- fig.colorbar(
- im, ax=ax[-1, -1], shrink=0.25, aspect=10, ticks=[-1, 0, 1])
- # both groups together
- else:
- fig, ax = plt.subplots(
- 1, len(features), sharex=True, sharey=True,
- num='embedding_feature_values'+("_binned" if binned else "_"),
- figsize=(len(features)*2.5, 3))
- feat_values_binned = {}
- if not filter_song:
- X_embedded_concat_group = np.concatenate(
- [X_embedded[i] for i in range(len(X_embedded))], axis=0)
- X_raw_concat_group = np.concatenate(
- [X[i] for i in range(len(X))], axis=0)
- else:
- X_embedded_concat_group = np.concatenate(
- [X_embedded[i][y[i].ravel()!=0]
- for i in range(len(X_embedded))], axis=0)
- X_raw_concat_group = np.concatenate(
- [X[i][y[i].ravel()!=0] for i in range(len(X))], axis=0)
- X_raw_concat_group = StandardScaler().fit_transform(X_raw_concat_group)
- for i, feature in enumerate(features):
- if binned:
- ret = binned_statistic_2d(
- X_embedded_concat_group[:, 1],
- X_embedded_concat_group[:, 0],
- X_raw_concat_group[:, i],
- np.nanmean, bins=128,
- range=[[-18, 18], [-18, 18]])
- feat_values_binned[feature] = ret
- im = ax[i].imshow(
- ret.statistic,
- extent=[ret.x_edge[0], ret.x_edge[-1],
- ret.y_edge[0], ret.y_edge[-1]],
- cmap='bwr', zorder=1, vmin=-1, vmax=1,)
- else:
- im = ax[i].scatter(
- *X_embedded_concat_group[::10].T, s=0.1, vmin=-1, vmax=1,
- alpha=0.25, c=X_raw_concat_group[::10, i], cmap='bwr', zorder=1)
- ax[i].scatter(*labels_edge_positions.T, s=0.2, c='k', zorder=2)
- ax[i].set_title(feature, fontsize=10)
- ax[i].set_xlim(0, 20)
- ax[i].set_ylim(0, 20)
- ax[i].axis("off")
- fig.colorbar(im, ax=ax[-1], shrink=0.25, aspect=10, ticks=[-1, 0, 1])
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # Finally, we plot the courting male's position around the partner during each social mode. We normalize the distance between flies by the length of the target and limit the visualization to three fly lengths.
- # %%
- if os.path.exists(f'{results_save_path}/courter_positions.npz'):
- with np.load(f'{results_save_path}/courter_positions.npz', allow_pickle=True) as data:
- dict_courter_positions = data['arr_0'].item()
- else:
- dict_courter_positions = {}
- for trial_idx, trial_name in enumerate(all_trial_names):
- df_trial_data = pd.read_csv(f'{data_save_path}/{trial_name}.csv')
- courter_position = df_trial_data[['relative_angle_courter', 'distance_target']].values
- # normalize distance by target length
- courter_position[:, 1] = courter_position[:, 1]/df_trial_data["target_length"].values
- trial_segments = X_embedded_segments[trial_idx]
- for state in np.unique(trial_segments):
- if state in ignore_states: continue
- else:
- if state not in dict_courter_positions:
- dict_courter_positions[state] = []
- dict_courter_positions[state].extend(courter_position[:len(trial_segments)][trial_segments==state])
- np.savez(f'{results_save_path}/courter_positions.npz', dict_courter_positions)
- rbins = np.linspace(0, 3, 31)
- abins = np.linspace(-np.pi, np.pi, 73)
- courter_position_hist = {}
- for state in dict_courter_positions:
- dict_courter_positions[state]=np.array(dict_courter_positions[state])
- azimut = np.array(dict_courter_positions[state][:, 0])
- azimut_rad = np.deg2rad(azimut)
- radius = np.array(dict_courter_positions[state][:, 1])
- #calculate histogram
- hist, _, _ = np.histogram2d(azimut_rad, radius, bins=(abins, rbins),
- density=True)
- courter_position_hist[state] = hist
- # plot
- A, R = np.meshgrid(abins, rbins)
- figname = 'courter_position_states'
- fig, ax = plt.subplots(
- 1, len(courter_position_hist), subplot_kw=dict(projection="polar"),
- figsize=(3*len(courter_position_hist), 3), num=figname)
- for s, (segment, hist) in enumerate(courter_position_hist.items()):
- pc = ax[s].pcolormesh(
- A, R, (hist).T,
- cmap="Reds",
- vmin=0, vmax=1)
- ax[s].set_title(state_names[s], fontsize=15)
- ax[s].set_xticks([-np.pi*(3/4), -np.pi/2, -np.pi/4,
- 0, np.pi/4, np.pi/2, np.pi*(3/4), np.pi])
- ax[s].set_xlim(-np.pi, np.pi)
- ax[s].set_theta_zero_location("N")
- plt.subplots_adjust(bottom=0.4, right=0.9, top=0.6)
- cax = plt.axes([0.85, 0.1, 0.01, 0.2])
- plt.colorbar(pc, cax=cax)
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # ## Comparison between male-female and male-male interactions
- #
- # Once we have the social UMAP embedding, we can compare them across different experimental conditions to understand differences in their interactions. Here we plot the UMAPs for male-female and male-male interactions and visualize their differences.
- # %%
- # comparison between groups
- kde_mean_group = {}
- X_embedded_kde_group = {}
- groups = list(trial_names.keys())
- for group in groups:
- X_embedded_kde_group[group] = []
- for trial_name in trial_names[group]:
- trial_idx = all_trial_names.index(trial_name)
- X_embedded_kde_group[group].append(X_embedded_kde[trial_idx])
- group_kde_mean = np.mean(X_embedded_kde_group[group], 0)
- group_kde_mean[group_kde_mean<1e-3]=0
- kde_mean_group[group] = group_kde_mean
- fig, ax = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(15, 5))
- xmin = positions[:,0].min()
- xmax = positions[:,0].max()
- ymin = positions[:,1].min()
- ymax = positions[:,1].max()
- for g, group in enumerate(kde_mean_group):
- vmin = 0 #np.round(np.nanpercentile(kde_mean_group[group], 2.5), 2)
- vmax = 0.03 #np.round(np.nanpercentile(kde_mean_group[group], 97.5), 2)
- kde_plot = ax[g].imshow(
- kde_mean_group[group],
- origin='lower',
- extent=[xmin, xmax, ymin, ymax],
- vmin=vmin, vmax=vmax,
- cmap="Reds")
- ax[g].scatter(*labels_edge_positions.T, s=0.2, c='k')
- ax[g].set_title(group)
- ax[g].axis('off')
- ax[g].set_xlim(0, 20)
- ax[g].set_ylim(0, 20)
- fig.colorbar(kde_plot, ax=ax[g], shrink=0.25, aspect=10, ticks=[vmin, vmax])
- # difference
- statistical_test = True
- X_embedded_kde_diff = kde_mean_group[groups[0]] - kde_mean_group[groups[1]]
- vmin=-0.03
- vmax=0.03
- if statistical_test:
- statistic, p_value = mannwhitneyu(X_embedded_kde_group[groups[0]], X_embedded_kde_group[groups[1]])
- X_embedded_kde_diff = X_embedded_kde_diff * (p_value<0.05)
- diff_lim = np.max(np.abs(X_embedded_kde_diff))
- kde_diff = ax[2].imshow(
- X_embedded_kde_diff,
- cmap='bwr', origin='lower',
- extent=[xmin, xmax, ymin, ymax],
- vmin=vmin, vmax=vmax)
- ax[2].scatter(*labels_edge_positions.T, s=0.2, c='k')
- ax[2].set_title("difference")
- ax[2].set_xlim(0, 20)
- ax[2].set_ylim(0, 20)
- ax[2].axis('off')
- fig.colorbar(kde_diff, ax=ax[2], shrink=0.25, aspect=10, ticks=[vmin, 0, vmax])
- plt.tight_layout()
- plt.show()
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # Next, we quantify the differences in social modes during male-female and male-male interactions.
- # %%
- rows = []
- for analysis_group in trial_names:
- for trial_name in trial_names[analysis_group]:
- trial_idx = all_trial_names.index(trial_name)
- trial_segments = X_embedded_segments[trial_idx]
- for s, state in enumerate(np.unique(trial_segments)):
- if s in ignore_states: continue
- state_ratio = np.sum(trial_segments==state)/len(trial_segments)
- rows.append(
- {"trial_name": trial_name,
- "analysis_group": analysis_group,
- "state": s,
- "ratio": state_ratio}
- )
- df_states_time_spent = pd.DataFrame(rows)
- fig, ax = plt.subplots(1, 1, figsize=(2*len(np.unique(df_states_time_spent.state)), 4))
- sns.barplot(data=df_states_time_spent, x="state", y="ratio", hue="analysis_group")
- sns.stripplot(data=df_states_time_spent, x="state", y="ratio", hue="analysis_group", dodge=True, alpha=0.25, palette=["k"])
- sns.despine()
- ax.set_ylim(0, 1)
- ax.set_xticklabels(state_names)
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # ## Comparisons between different song contexts
- #
- # To compare interactions in different singing conditions, we condition our UMAPs on different song contexts and compare song versus silence and pulse versus sine.
- # %%
- unique_segments = np.unique(np.concatenate(X_embedded_segments))
- song_conditioned_kde = {}
- rows = []
- for analysis_group in trial_names:
- song_conditioned_kde[analysis_group] = {}
- for song_type in ['song', 'silence', 'pulse', 'sine']:
- song_conditioned_kde[analysis_group][song_type] = []
- for datename in trial_names[analysis_group]:
- trial_idx = all_trial_names.index(datename)
- X_embedded_trial = X_embedded[trial_idx][nb_delays:]
- X_embedded_segments_trail = X_embedded_segments[trial_idx][nb_delays:]
- y_trial = y_proc[trial_idx].ravel()
- # song
- if song_type == 'song':
- trial_song_embedding = X_embedded_trial[(y_trial!=0)]
- trial_song_segments = X_embedded_segments_trail[y_trial!=0]
- elif song_type == 'silence':
- trial_song_embedding = X_embedded_trial[(y_trial==0)]
- trial_song_segments = X_embedded_segments_trail[y_trial==0]
- elif song_type == 'pulse':
- trial_song_embedding = X_embedded_trial[(y_trial==1)]
- trial_song_segments = X_embedded_segments_trail[y_trial==1]
- elif song_type == 'sine':
- trial_song_embedding = X_embedded_trial[(y_trial==2)]
- trial_song_segments = X_embedded_segments_trail[y_trial==2]
- _, trial_song_kde = kernel_density_estimation(
- trial_song_embedding, bw, positions)
- song_conditioned_kde[analysis_group][song_type].append(trial_song_kde)
- for state in unique_segments:
- if state not in ignore_states:
- song_state_time_trial = (
- np.sum(trial_song_segments==state) / len(trial_song_segments)
- )
- rows.append(
- {"analysis_group": analysis_group,
- "song": song_type,
- "trial": datename,
- "state": state,
- "time_spent": np.sum(
- trial_song_segments==state),
- "time_spent_ratio": song_state_time_trial}
- )
- song_conditioned_kde[analysis_group][song_type] = np.array(
- song_conditioned_kde[analysis_group][song_type])
- df_song_states_time = pd.DataFrame(rows)
- # %%
- # song vs silence
- rows = []
- for analysis_group in np.unique(df_song_states_time.analysis_group):
- for trial in np.unique(df_song_states_time.trial):
- for state in np.unique(df_song_states_time.state):
- trial_song_states_time = df_song_states_time[
- (df_song_states_time.analysis_group==analysis_group)&
- (df_song_states_time.trial==trial)&
- (df_song_states_time.state==state)
- ]
- if not len(trial_song_states_time): continue
- song_silence_diff = trial_song_states_time[
- (trial_song_states_time.song=="song")].time_spent_ratio.values[0] - trial_song_states_time[
- (trial_song_states_time.song=="silence")].time_spent_ratio.values[0]
- pulse_sine_diff = trial_song_states_time[
- (trial_song_states_time.song=="pulse")].time_spent_ratio.values[0] - trial_song_states_time[
- (trial_song_states_time.song=="sine")].time_spent_ratio.values[0]
- rows.append(
- {"analysis_group": analysis_group,
- "trial": trial,
- "state": state,
- "p(song)-p(silence)": song_silence_diff,
- "p(pulse)-p(sine)": pulse_sine_diff}
- )
- df_song_diff = pd.DataFrame(rows)
- fig, ax = plt.subplots(1, 3, figsize=(15, 3), gridspec_kw={'width_ratios': [1, 1, 3]})
- mean_kde_mf_diff = np.mean(song_conditioned_kde['male-female']['song'], 0) - \
- np.mean(song_conditioned_kde['male-female']['silence'], 0)
- mean_kde_mm_diff = np.mean(song_conditioned_kde['male-male']['song'], 0) - \
- np.mean(song_conditioned_kde['male-male']['silence'], 0)
- mf_kde = ax[0].matshow(mean_kde_mf_diff,
- cmap='bwr', origin='lower',
- extent=[xmin, xmax, ymin, ymax],
- vmin=-0.03, vmax=0.03)
- ax[0].set_xlim(0, 20)
- ax[0].set_ylim(0, 20)
- ax[0].set_ylabel("song - silence")
- ax[0].set_title("female-directed")
- ax[0].axis("off")
- ax[0].scatter(*labels_edge_positions.T, s=0.2, c='k')
- fig.colorbar(mf_kde, ax=ax[0], shrink=0.25, aspect=10, ticks=[-0.03, 0, 0.03])
- mm_kde = ax[1].matshow(mean_kde_mm_diff,
- cmap='bwr', origin='lower',
- extent=[xmin, xmax, ymin, ymax],
- vmin=-0.03, vmax=0.03)
- ax[1].set_xlim(0, 20)
- ax[1].set_ylim(0, 20)
- ax[1].axis("off")
- ax[1].set_title("male-directed")
- ax[1].scatter(*labels_edge_positions.T, s=0.2, c='k')
- #fig.colorbar(mm_kde, ax=ax[1], shrink=0.25, aspect=10, ticks=[-0.03, 0, 0.03])
- sns.barplot(data=df_song_diff, x="state", y="p(song)-p(silence)", hue="analysis_group", ax=ax[2])
- sns.stripplot(data=df_song_diff, x="state", y="p(song)-p(silence)", hue="analysis_group", ax=ax[2], alpha=0.25, dodge=True, palette=["k"])
- ax[2].set_xticklabels(state_names, rotation=45)
- plt.suptitle("song - silence")
- #plt.tight_layout()
- # %%
- # pulse vs sine
- fig, ax = plt.subplots(1, 3, figsize=(15, 3), gridspec_kw={'width_ratios': [1, 1, 3]})
- mean_kde_mf_diff = np.mean(song_conditioned_kde['male-female']['pulse'], 0) - \
- np.mean(song_conditioned_kde['male-female']['sine'], 0)
- mean_kde_mm_diff = np.mean(song_conditioned_kde['male-male']['pulse'], 0) - \
- np.mean(song_conditioned_kde['male-male']['sine'], 0)
- mf_kde = ax[0].matshow(mean_kde_mf_diff,
- cmap='bwr', origin='lower',
- extent=[xmin, xmax, ymin, ymax],
- vmin=-0.03, vmax=0.03)
- ax[0].set_xlim(0, 20)
- ax[0].set_ylim(0, 20)
- ax[0].set_title("female-directed")
- ax[0].axis("off")
- ax[0].scatter(*labels_edge_positions.T, s=0.2, c='k')
- fig.colorbar(mf_kde, ax=ax[0], shrink=0.25, aspect=10, ticks=[-0.03, 0, 0.03])
- mm_kde = ax[1].matshow(mean_kde_mm_diff,
- cmap='bwr', origin='lower',
- extent=[xmin, xmax, ymin, ymax],
- vmin=-0.03, vmax=0.03)
- ax[1].set_xlim(0, 20)
- ax[1].set_ylim(0, 20)
- ax[1].axis("off")
- ax[1].set_title("male-directed")
- ax[1].scatter(*labels_edge_positions.T, s=0.2, c='k')
- #fig.colorbar(mm_kde, ax=ax[1], shrink=0.25, aspect=10, ticks=[-0.03, 0, 0.03])
- sns.barplot(data=df_song_diff, x="state", y="p(pulse)-p(sine)", hue="analysis_group", ax=ax[2])
- sns.stripplot(data=df_song_diff, x="state", y="p(pulse)-p(sine)", hue="analysis_group", ax=ax[2], alpha=0.25, dodge=True, palette=["k"])
- ax[2].set_xticklabels(state_names, rotation=45)
- plt.suptitle("pulse - sine")
- #plt.tight_layout()
- # %% [markdown]
- # ## Hyperparameter tuning
- #
- # We used three important hyperparameters: two for fitting the UMAP, `n_neighbors` and `min_dist`, and one for time-delay embedding of features, `nb_delays`.
- #
- # The `n_neighbors` parameter specifies the size of the local neighborhood UMAP looks at when fitting the manifold. This parameter controls how UMAP balances local versus global structure in the data. Low values make the UMAP fit the local structure well, whereas large values focus on global structure but can lose fine details.
- #
- # `min_dist` specifies the minimum distance between the embedded points in the low-dimensional manifold. Thus, low values result in points being embedded densely together.
- #
- # `nb_delays` specifies the history information to be included when embedding the features into low dimensions. Small values focus on immediate history.
- #
- # To find out the optimal values of these hyperparameters for our data, we performed a hyperparameter optimization. This was done by fitting the UMAP with different hyperparameter combinations and validating performance by the reconstruction error. From the fitted UMAP, an `inverse_transform` is applied on an embedded validation set (not used for fitting), and the mean squared error between the original validation data and reconstructed validation data is quantified.
- # %%
- #UMAP hyperparameter tuning
- n_neighbors_grid=[10, 50, 100, 200]
- min_dist_grid=[0.0, 0.1, 0.2, 0.5]
- nb_delays_grid=[15, 30, 60, 120]#s
- fit_results = umap_multiple_fits(
- X,
- n_neighbors_grid=n_neighbors_grid,
- min_dist_grid=min_dist_grid,
- nb_delays_grid=nb_delays_grid)
- # plots
- val_score_matrices = {}
- for nb_delays in nb_delays_grid:
- val_score_matrices[nb_delays] = np.zeros((len(min_dist_grid), len(n_neighbors_grid)))
- for m, min_dist in enumerate(min_dist_grid):
- for n, n_neighbors in enumerate(n_neighbors_grid):
- val_score_matrices[nb_delays][m, n] = fit_results[
- f'delay: {nb_delays}, '+\
- f'n_components: 2, ' +\
- f'n_neighbors: {n_neighbors}, ' +\
- f'min_dist: {min_dist}, '+\
- f'metric: euclidean'
- ]['val_score']
- fig, ax = plt.subplots(1, len(nb_delays_grid), figsize=(len(nb_delays_grid)*3, 3))
- for i, nb_delays in enumerate(nb_delays_grid):
- im = ax[i].matshow(val_score_matrices[nb_delays])
- ax[i].set_xlabel("n_neighbors")
- ax[i].set_xticklabels(n_neighbors_grid)
- ax[i].set_ylabel("min_dist")
- ax[i].set_yticklabels(min_dist_grid)
- fig.colorbar(im, ax=ax[i], shrink=0.25, aspect=10)
- plt.tight_layout()
- plt.show()
- # %%
social_behavior_umap.ipynb at commit db64ff6, no license · at the source
Overview
- ENI-G, a Joint Initiative of the University Medical Center Göttingen and the Max Planck Institute for Multidisciplinary Sciences, Göttingen, Germany
- IMPRS Neuroscience, Göttingen, Germany
- Present Address: Charité Universitätsmedizin Berlin, Berlin, Germany
- Institute of Computer Science, University of Göttingen, Göttingen, Germany
- CERVO Brain Research Centre, Québec City, QC Canada
- Faculty of Medicine, Université Laval, Québec City, QC Canada
- Department of Neuroscience, Faculty VI, University of Oldenburg, Oldenburg, Germany
Abstract
How the brain enables individuals to adapt behavior to their partner is key to understanding social exchange. For example, courtship behavior involves sensorimotor processing of signals that can result in behavioral dialog between partners, such as stereotyped movements and singing. The courtship behavior of Drosophila melanogaster males with their partners, which are usually female but can also be male, involves singing. To investigate how behavioral feedback and sensorimotor processing contribute to flexible social interactions, we compared the courtship behavior and singing of male D. melanogaster towards males and females. Quantitative analysis of their interactions revealed that while underlying courtship and song rules are unaffected by the sex of the partner, the behavioral dynamics and song sequences differ by partner sex. This divergence stems from sex-specific behavioral feedback: females decelerate to song, while males orient towards the singer. Moreover, optogenetic manipulations reveal that the partners’ responses are driven by sex-specific neural circuits that link song detection with arousal and social decisions. Our findings demonstrate that flexible social behaviors can arise from fixed sensorimotor rules through a context-dependent selection facilitated by the partner’s behavioral feedback. More broadly, our results reveal compositionality as a key mechanism for achieving behavioral flexibility during complex social interactions such as courtship.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 11 matches between paragraphs and lines of code.
janclemenslab/socialUMAP
db64ff6a2513c17db493d9c7d931daea4bce087b, 26 January 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
3 files
- embedding_utils.py, Python, 1,298 lines
- social_behavior_umap.ipy
nb , Jupyter, 985 lines, 6 matches - README.md, Text, 11 lines
jgraving/DeepPoseKit
cecdb0c8c364ea049a3b705275ae71a2f366d4da, 9 June 2020Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
61 files
- deepposekit/
__init__.py , Python, 31 lines - deepposekit/
annotate/ , Python, 164 linesKMeansSampler.py - deepposekit/
annotate/ , Python, 21 lines__init__.py - deepposekit/
annotate/ , Python, 333 linesgui/ Annotator.py - deepposekit/
annotate/ , Python, 661 linesgui/ GUI.py - deepposekit/
annotate/ , Python, 137 linesgui/ Skeleton.py - deepposekit/
annotate/ , Python, 19 linesgui/ __init__.py - deepposekit/
annotate/ , Python, 16 linesutils/ __init__.py - deepposekit/
annotate/ , Python, 44 linesutils/ hotkeys.py - deepposekit/
annotate/ , Python, 32 linesutils/ image.py - deepposekit/
augment/ , Python, 118 linesFlipAxis.py - deepposekit/
augment/ , Python, 18 lines__init__.py - deepposekit/
callbacks.py , Python, 267 lines - deepposekit/
io/ , Python, 179 linesBaseGenerator.py - deepposekit/
io/ , Python, 102 linesDLCDataGenerator.py - deepposekit/
io/ , Python, 170 linesDataGenerator.py - deepposekit/
io/ , Python, 61 linesImageGenerator.py - deepposekit/
io/ , Python, 332 linesTrainingGenerator.py - deepposekit/
io/ , Python, 26 lines__init__.py - deepposekit/
io/ , Python, 310 linesutils.py - deepposekit/
io/ , Python, 191 linesvideo.py - deepposekit/
models/ , Python, 165 linesDeepLabCut.py - deepposekit/
models/ , Python, 211 linesLEAP.py - deepposekit/
models/ , Python, 234 linesStackedDenseNet.py - deepposekit/
models/ , Python, 157 linesStackedHourglass.py - deepposekit/
models/ , Python, 24 lines__init__.py - deepposekit/
models/ , Python, 16 linesbackend/ __init__.py - deepposekit/
models/ , Python, 283 linesbackend/ backend.py - deepposekit/
models/ , Python, 173 linesbackend/ registration.py - deepposekit/
models/ , Python, 106 linesbackend/ utils.py - deepposekit/
models/ , Python, 241 linesengine.py - deepposekit/
models/ , Python, 20 lineslayers/ __init__.py - deepposekit/
models/ , Python, 261 lineslayers/ convolutional.py - deepposekit/
models/ , Python, 176 lineslayers/ deeplabcut.py - deepposekit/
models/ , Python, 406 lineslayers/ densenet.py - deepposekit/
models/ , Python, 263 lineslayers/ hourglass.py - deepposekit/
models/ , Python, 402 lineslayers/ imagenet_densenet.py - deepposekit/
models/ , Python, 534 lineslayers/ imagenet_mobile.py - deepposekit/
models/ , Python, 386 lineslayers/ imagenet_resnet.py - deepposekit/
models/ , Python, 340 lineslayers/ imagenet_utils.py - deepposekit/
models/ , Python, 348 lineslayers/ imagenet_xception.py - deepposekit/
models/ , Python, 108 lineslayers/ leap.py - deepposekit/
models/ , Python, 74 lineslayers/ squeeze_excitation.py - deepposekit/
models/ , Python, 111 lineslayers/ subpixel.py - deepposekit/
models/ , Python, 40 lineslayers/ util.py - deepposekit/
models/ , Python, 133 linesloading.py - deepposekit/
models/ , Python, 49 linessaving.py - deepposekit/
utils/ , Python, 20 lines__init__.py - deepposekit/
utils/ , Python, 43 linesimage.py - deepposekit/
utils/ , Python, 48 linesio.py - deepposekit/
utils/ , Python, 195 lineskeypoints.py - examples/
custom_data_generator.ip , Jupyter, 242 linesynb - examples/
deeplabcut_data_example. , Jupyter, 378 linesipynb - examples/
step1_create_annotation_ , Jupyter, 173 linesset.ipynb - examples/
step2_annotate_data.ipyn , Jupyter, 55 linesb - examples/
step3_train_model.ipynb , Jupyter, 342 lines - examples/
step4a_initialize_annota , Jupyter, 122 linestions.ipynb - examples/
step4b_predict_new_data. , Jupyter, 318 linesipynb - setup.py, Python, 67 lines
- LICENSE, License, 201 lines
- README.md, Text, 158 lines
janclemenslab/das
9a7b5b5ac850a7f85d05bd0f88477ea2991f46d2, 15 September 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
61 files
- docs/
conf.py , Python, 106 lines - docs/
make_api_doctree.py , Python, 59 lines - docs/
technical/ , Jupyter, 26 linescli.ipynb - docs/
tutorials/ , Jupyter, 124 linescolab.ipynb - docs/
tutorials/ , Jupyter, 216 linesevaluate_bird.ipynb - docs/
tutorials/ , Jupyter, 427 linesevaluate_fly.ipynb - docs/
tutorials/ , Jupyter, 288 linesmake_ds_notebook.ipynb - docs/
tutorials/ , Jupyter, 105 linespredict.ipynb - docs/
tutorials/ , Jupyter, 170 linesrealtime.ipynb - docs/
tutorials/ , Jupyter, 91 linestrain.ipynb - docs/
unsupervised/ , Jupyter, 169 linesbirds.ipynb - docs/
unsupervised/ , Jupyter, 111 linesflies.ipynb - docs/
unsupervised/ , Jupyter, 151 linesmice.ipynb - src/
das/ , Python, 35 lines__init__.py - src/
das/ , Python, 431 linesannot.py - src/
das/ , Python, 438 linesaugmentation.py - src/
das/ , Python, 201 linesblock_stratify.py - src/
das/ , Python, 98 linescli.py - src/
das/ , Python, 203 linesevaluate.py - src/
das/ , Python, 164 linesevent_utils.py - src/
das/ , Python, 24 linesio/ __init__.py - src/
das/ , Python, 313 linesio/ _core.py - src/
das/ , Python, 41 linesio/ data_hash.py - src/
das/ , Python, 70 linesio/ npy_dir.py - src/
das/ , Python, 32 linesloss.py - src/
das/ , Python, 285 linesmake_dataset.py - src/
das/ , Python, 20 linesmodels/ __init__.py - src/
das/ , Python, 195 linesmodels/ architectures.py - src/
das/ , Python, 12 linesmodels/ kapre/ __init__.py - src/
das/ , Python, 31 linesmodels/ kapre/ augmentation.py - src/
das/ , Python, 147 linesmodels/ kapre/ backend.py - src/
das/ , Python, 30 linesmodels/ kapre/ backend_keras.py - src/
das/ , Python, 149 linesmodels/ kapre/ filterbank.py - src/
das/ , Python, 350 linesmodels/ kapre/ time_frequency.py - src/
das/ , Python, 138 linesmodels/ kapre/ utils.py - src/
das/ , Python, 245 linesmodels/ loading.py - src/
das/ , Python, 70 linesmodels/ menagerie.py - src/
das/ , Python, 4 linesmodels/ tcn/ __init__.py - src/
das/ , Python, 189 linesmodels/ tcn/ tcn.py - src/
das/ , Python, 383 linesmodels/ tcn/ tcn_new.py - src/
das/ , Python, 758 linesmodels_legacy.py - src/
das/ , Python, 17 linesnpy_dir.py - src/
das/ , Python, 144 linespostprocessing.py - src/
das/ , Python, 750 linespredict.py - src/
das/ , Python, 107 linespulse_utils.py - src/
das/ , Python, 134 linessegment_utils.py - src/
das/ , Python, 54 linesspec_utils.py - src/
das/ , Python, 90 linestracking.py - src/
das/ , Python, 509 linestrain.py - src/
das/ , Python, 237 linesutils.py - src/
das/ , Python, 451 linesutils_plot.py - tests/
test_augmentation.py , Python, 84 lines - tests/
test_cli.py , Python, 147 lines - tests/
test_import.py , Python, 67 lines - tests/
test_io.py , Python, 64 lines - tests/
test_legacy_api.py , Python, 23 lines - tests/
test_model_loading.py , Python, 167 lines - tests/
test_models.py , Python, 24 lines - tests/
test_utils.py , Python, 33 lines - LICENSE, License, 201 lines
- README.md, Text, 41 lines
janclemenslab/glm_utils
65b048a1d9f2aa0a8f21be2d78ab154ecd05f241, 8 June 2021Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
15 files
- demo/
basics.ipynb , Jupyter, 244 lines, 1 match - demo/
basis_functions.ipynb , Jupyter, 104 lines - demo/
demo_utils.py , Python, 33 lines - demo/
gam.ipynb , Jupyter, 101 lines - demo/
group_lasso.ipynb , Jupyter, 191 lines - demo/
multiple_inputs.ipynb , Jupyter, 448 lines - demo/
pipeline.ipynb , Jupyter, 34 lines - demo/
quadratic_filter.ipynb , Jupyter, 121 lines - setup.py, Python, 43 lines
- src/
glm_utils/ , Python, 2 lines__init__.py - src/
glm_utils/ , Python, 218 linesbases.py - src/
glm_utils/ , Python, 61 linespostprocessing.py - src/
glm_utils/ , Python, 177 linespreprocessing.py - LICENSE, License, 21 lines
- README.md, Text, 22 lines
lindermanlab/ssm
eb6c8aa33e5311d3564075807dec340759dd8081, 9 May 2025Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
67 files
- doc/
conf.py , Python, 61 lines - examples/
constrained-arhmm.ipynb , Jupyter, 321 lines - examples/
constrained-arhmm.py , Python, 371 lines - examples/
em-for-students-t.ipynb , Jupyter, 236 lines - examples/
em-for-students-t.py , Python, 257 lines - examples/
hmm.py , Python, 137 lines - examples/
hmm_exponential.py , Python, 139 lines - examples/
hsmm.py , Python, 118 lines - examples/
lds.py , Python, 115 lines - examples/
rslds.py , Python, 205 lines - examples/
slds.py , Python, 134 lines - notebooks/
1-Simple-HMM-Demo.ipynb , Jupyter, 344 lines - notebooks/
1-Simple-HMM-Demo.py , Python, 365 lines - notebooks/
1b-Simple-Linear-Dynamic , Jupyter, 427 linesal-System.ipynb - notebooks/
1b-Simple-Linear-Dynamic , Python, 434 linesal-System.py - notebooks/
2-Input-Driven-HMM.ipynb , Jupyter, 242 lines - notebooks/
2-Input-Driven-HMM.py , Python, 255 lines - notebooks/
2b-Input-Driven-Observat , Jupyter, 681 linesions-(GLM-HMM).ipynb - notebooks/
2b-Input-Driven-Observat , Python, 652 linesions-(GLM-HMM).py - notebooks/
3-Switching-Linear-Dynam , Jupyter, 446 linesical-System.ipynb - notebooks/
3-Switching-Linear-Dynam , Python, 458 linesical-System.py - notebooks/
4-Recurrent-SLDS.ipynb , Jupyter, 366 lines - notebooks/
4-Recurrent-SLDS.py , Python, 386 lines - notebooks/
5-Poisson-SLDS.ipynb , Jupyter, 134 lines - notebooks/
5-Poisson-SLDS.py , Python, 152 lines - notebooks/
6-Poisson-fLDS.ipynb , Jupyter, 132 lines - notebooks/
6-Poisson-fLDS.py , Python, 146 lines - notebooks/
7-Variatonal-Laplace-EM- , Jupyter, 183 linesfor-SLDS-Tutorial.ipynb - notebooks/
7-Variatonal-Laplace-EM- , Python, 196 linesfor-SLDS-Tutorial.py - notebooks/
HMM-State-Clustering.ipy , Jupyter, 290 linesnb - notebooks/
HMM-State-Clustering.py , Python, 307 lines - notebooks/
Multi-Population-rSLDS.i , Jupyter, 598 linespynb - notebooks/
Multi-Population-rSLDS.p , Python, 619 linesy - notebooks/
Poisson-HMM-Demo.ipynb , Jupyter, 331 lines - notebooks/
Poisson-HMM-Demo.py , Python, 344 lines - setup.py, Python, 65 lines
- ssm/
__init__.py , Python, 4 lines - ssm/
emissions.py , Python, 891 lines - ssm/
extensions/ , Python, 1 line__init__.py - ssm/
extensions/ , Python, 1 linemp_srslds/ __init__.py - ssm/
extensions/ , Python, 626 linesmp_srslds/ emissions_ext.py - ssm/
extensions/ , Python, 218 linesmp_srslds/ initializations.py - ssm/
extensions/ , Python, 420 linesmp_srslds/ observations_ext.py - ssm/
extensions/ , Python, 214 linesmp_srslds/ transitions_ext.py - ssm/
hierarchical.py , Python, 137 lines - ssm/
hmm.py , Python, 816 lines - ssm/
init_state_distns.py , Python, 62 lines - ssm/
lds.py , Python, 934 lines - ssm/
messages.py , Python, 1,408 lines - ssm/
model_selection.py , Python, 93 lines - ssm/
observations.py , Python, 1,910 lines - ssm/
optimizers.py , Python, 220 lines - ssm/
plots.py , Python, 111 lines - ssm/
preprocessing.py , Python, 164 lines - ssm/
primitives.py , Python, 431 lines - ssm/
regression.py , Python, 604 lines - ssm/
stats.py , Python, 673 lines - ssm/
transitions.py , Python, 669 lines, 1 match - ssm/
util.py , Python, 278 lines - ssm/
variational.py , Python, 477 lines - tests/
lds_benchmarks.py , Python, 122 lines - tests/
test_basics.py , Python, 458 lines - tests/
test_hmm_gradients.py , Python, 75 lines - tests/
test_lds.py , Python, 706 lines - tests/
test_stats.py , Python, 278 lines - LICENSE, License, 22 lines
- README.md, Text, 64 lines
lmcinnes/umap
1180b785023ff8e8eb071de56c581d1dc8bec04e, 24 September 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
64 files
- benchmarks/
bench_adjmat_vectorize.p , Python, 98 linesy - benchmarks/
bench_fastmath_removal.p , Python, 301 linesy - ci_scripts/
install.sh , Shell, 86 lines - ci_scripts/
success.sh , Shell, 13 lines - ci_scripts/
test.sh , Shell, 12 lines - doc/
bokeh_digits_plot.py , Python, 73 lines - doc/
conf.py , Python, 239 lines - doc/
plotting_example_interac , Python, 32 linestive.py - examples/
digits/ , Python, 34 linesdigits.py - examples/
galaxy10sdss.py , Python, 275 lines - examples/
inverse_transform_exampl , Python, 60 linese.py - examples/
iris/ , Python, 37 linesiris.py - examples/
mnist_torus_sphere_examp , Python, 120 linesle.py - examples/
mnist_transform_new_data , Python, 48 lines.py - examples/
plot_algorithm_compariso , Python, 140 linesn.py - examples/
plot_fashion-mnist_examp , Python, 74 linesle.py - examples/
plot_feature_extraction_ , Python, 72 linesclassification.py - examples/
plot_mnist_example.py , Python, 35 lines - notebooks/
AnimatingUMAP.ipynb , Jupyter, 168 lines - notebooks/
Document embedding using UMAP.ipynb , Jupyter, 163 lines - notebooks/
MNIST_Landmarks.ipynb , Jupyter, 177 lines - notebooks/
Parametric_UMAP/ , Jupyter, 92 lines01.0-parametric-umap-mni st-embedding-basic.ipynb - notebooks/
Parametric_UMAP/ , Jupyter, 89 lines02.0-parametric-umap-mni st-embedding-convnet.ipy nb - notebooks/
Parametric_UMAP/ , Jupyter, 164 lines03.0-parametric-umap-mni st-embedding-convnet-wit h-reconstruction.ipynb - notebooks/
Parametric_UMAP/ , Jupyter, 144 lines04.0-parametric-umap-mni st-embedding-convnet-wit h-autoencoder-loss.ipynb - notebooks/
Parametric_UMAP/ , Jupyter, 77 lines05.0-parametric-umap-wit h-callback.ipynb - notebooks/
Parametric_UMAP/ , Jupyter, 60 lines06.0-nonparametric-umap. ipynb - notebooks/
Parametric_UMAP/ , Jupyter, 225 lines07.0-parametric-umap-glo bal-loss.ipynb - notebooks/
UMAP usage and parameters.ipynb , Jupyter, 260 lines - setup.py, Python, 5 lines
- umap/
__init__.py , Python, 42 lines - umap/
aligned_umap.py , Python, 607 lines - umap/
distances.py , Python, 1,561 lines - umap/
layouts.py , Python, 1,100 lines - umap/
parametric_umap.py , Python, 1,402 lines - umap/
plot.py , Python, 1,692 lines - umap/
sparse.py , Python, 624 lines - umap/
spectral.py , Python, 573 lines, 1 match - umap/
tests/ , Python, 50 lines__init__.py - umap/
tests/ , Python, 232 linesconftest.py - umap/
tests/ , Python, 155 linestest_aligned_umap.py - umap/
tests/ , Python, 333 linestest_chunked_parallel_sp atial_metric.py - umap/
tests/ , Python, 117 linestest_composite_models.py - umap/
tests/ , Python, 85 linestest_data_input.py - umap/
tests/ , Python, 80 linestest_densmap.py - umap/
tests/ , Python, 109 linestest_numba_aware_pairwis e_distances.py - umap/
tests/ , Python, 190 linestest_parametric_umap.py - umap/
tests/ , Python, 50 linestest_plot.py - umap/
tests/ , Python, 51 linestest_spectral.py - umap/
tests/ , Python, 1 linetest_umap.py - umap/
tests/ , Python, 82 linestest_umap_get_feature_na mes_out.py - umap/
tests/ , Python, 288 linestest_umap_grads.py - umap/
tests/ , Python, 742 linestest_umap_metrics.py - umap/
tests/ , Python, 198 linestest_umap_nn.py - umap/
tests/ , Python, 316 linestest_umap_on_iris.py - umap/
tests/ , Python, 381 linestest_umap_ops.py - umap/
tests/ , Python, 94 linestest_umap_repeated_data. py - umap/
tests/ , Python, 158 linestest_umap_trustworthines s.py - umap/
tests/ , Python, 322 linestest_umap_validation_par ams.py - umap/
umap_.py , Python, 3,762 lines, 2 matches - umap/
utils.py , Python, 228 lines - umap/
validation.py , Python, 86 lines - LICENSE.txt, License, 29 lines
- README.rst, Text, 571 lines
tommyod/KDEpy
cdeca713299f17ab0a55365c761c1999dc0421a0, 6 May 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
43 files
- KDEpy/
BaseKDE.py , Python, 242 lines - KDEpy/
FFTKDE.py , Python, 217 lines - KDEpy/
NaiveKDE.py , Python, 147 lines - KDEpy/
TreeKDE.py , Python, 189 lines - KDEpy/
__init__.py , Python, 14 lines - KDEpy/
binning.py , Python, 445 lines - KDEpy/
bw_selection.py , Python, 299 lines - KDEpy/
kernel_funcs.py , Python, 377 lines - KDEpy/
sandbox/ , MATLAB, 71 linesakde1d.m - KDEpy/
sandbox/ , MATLAB, 13 linesdct1d.m - KDEpy/
sandbox/ , MATLAB, 25 linesfixed_point.m - KDEpy/
sandbox/ , MATLAB, 18 linesidct1d.m - KDEpy/
sandbox/ , MATLAB, 85 lineskde.m - KDEpy/
sandbox/ , MATLAB, 10 linesprobfun.m - KDEpy/
sandbox/ , MATLAB, 32 linesregEM.m - KDEpy/
sandbox/ , MATLAB, 18 linesroot.m - KDEpy/
sandbox/ , MATLAB, 23 linestest.m - KDEpy/
tests/ , Python, 108 linestest_BaseKDE.py - KDEpy/
tests/ , Python, 159 linestest_FFTKDE.py - KDEpy/
tests/ , Python, 190 linestest_NaiveKDE.py - KDEpy/
tests/ , Python, 165 linestest_TreeKDE.py - KDEpy/
tests/ , Python, 210 linestest_api.py - KDEpy/
tests/ , Python, 172 linestest_binning.py - KDEpy/
tests/ , Python, 50 linestest_bw_selection.py - KDEpy/
tests/ , Python, 79 linestest_estimator_vs_estima tor.py - KDEpy/
tests/ , Python, 228 linestest_kernel_funcs.py - KDEpy/
tests/ , Python, 355 linestest_sorted_grid.py - KDEpy/
utils.py , Python, 140 lines - docs/
presentation/ , Jupyter, 725 linesfigs/ create_presentation_figs .ipynb - docs/
source/ , Python, 71 linesREADME_examples.py - docs/
source/ , Jupyter, 60 lines_static/ img/ create_images.ipynb - docs/
source/ , Python, 213 linesconf.py - docs/
source/ , Python, 91 linesexample_stocks_kde.py - docs/
source/ , Python, 225 linesexamples.py - docs/
source/ , Jupyter, 238 linesintroduction.ipynb - docs/
source/ , Python, 71 lineskde_animations.py - docs/
source/ , Python, 265 linesprofiling.py - sandbox/
Existing implementations.ipynb , Jupyter, 163 lines - sandbox/
Numba testing.ipynb , Jupyter, 64 lines - sandbox/
Testing.ipynb , Jupyter, 315 lines - setup.py, Python, 23 lines
- LICENSE, License, 28 lines
- README.md, Text, 80 lines
trevismd/statannotations
3f020ae631ca88a091b6ee3e9a9fd32158920879, 22 June 2025Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
51 files
- coverage.sh, Shell, 1 line
- docs/
build/ , JavaScript, 149 lineshtml/ _static/ doctools.js - docs/
build/ , JavaScript, 13 lineshtml/ _static/ documentation_options.js - docs/
build/ , JavaScript, 7,429 lineshtml/ _static/ jquery-3.5.1.js - docs/
build/ , JavaScript, 2 lineshtml/ _static/ jquery.js - docs/
build/ , JavaScript, 192 lineshtml/ _static/ language_data.js - docs/
build/ , JavaScript, 632 lineshtml/ _static/ searchtools.js - docs/
build/ , JavaScript, 2,042 lineshtml/ _static/ underscore-1.13.1.js - docs/
build/ , JavaScript, 6 lineshtml/ _static/ underscore.js - docs/
build/ , JavaScript, 1 linehtml/ searchindex.js - docs/
source/ , Python, 57 linesconf.py - make_doc.sh, Shell, 3 lines
- setup.py, Python, 41 lines
- statannotations/
Annotation.py , Python, 56 lines - statannotations/
Annotator.py , Python, 940 lines - statannotations/
PValueFormat.py , Python, 251 lines - statannotations/
_GroupsPositions.py , Python, 152 lines - statannotations/
_Plotter.py , Python, 346 lines - statannotations/
__init__.py , Python, 3 lines - statannotations/
_version.py , Python, 1 line - statannotations/
compat.py , Python, 991 lines - statannotations/
format_annotations.py , Python, 71 lines - statannotations/
stats/ , Python, 223 linesComparisonsCorrection.py - statannotations/
stats/ , Python, 73 linesStatResult.py - statannotations/
stats/ , Python, 127 linesStatTest.py - statannotations/
stats/ , Python, 1 line__init__.py - statannotations/
stats/ , Python, 83 linestest.py - statannotations/
stats/ , Python, 57 linesutils.py - statannotations/
utils.py , Python, 195 lines - tests/
test_annotation.py , Python, 31 lines - tests/
test_annotator.py , Python, 375 lines - tests/
test_comparisons_correct , Python, 297 linesions.py - tests/
test_format_annotations. , Python, 37 linespy - tests/
test_integrate_annotator , Python, 91 lines.py - tests/
test_integrate_format_mu , Python, 107 linesltiple.py - tests/
test_integrate_plotter.p , Python, 79 linesy - tests/
test_missing_group.py , Python, 54 lines - tests/
test_native_scale.py , Python, 116 lines - tests/
test_order_group.py , Python, 63 lines - tests/
test_plotter.py , Python, 86 lines - tests/
test_positions.py , Python, 64 lines - tests/
test_pvalue_format.py , Python, 196 lines - tests/
test_stat_result.py , Python, 42 lines - tests/
test_stats_test.py , Python, 38 lines - tests/
test_stats_utils.py , Python, 58 lines - tests/
test_stattest.py , Python, 62 lines - tests/
test_utils.py , Python, 57 lines - usage/
example.ipynb , Jupyter, 456 lines - usage/
test_script.py , Python, 433 lines - LICENSE, License, 48 lines
- README.md, Text, 190 lines
pywavelets.readthedocs.io
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
Code availability
All data analyses were performed using the software listed in Table 2. Code for generating the social maps is deposited at https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 9 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 350 scripts, each with its path and the digest of its content;
- 11 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data availability
All data supporting the findings of this study are available within the paper, its Supplementary Information or a public repository. Source data are provided with this paper as a Source Data file. Raw experimental data generated in this study have been deposited in the Göttingen Research Online database (10.25625/
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 3 keywords, 10 MeSH terms, 2 funders, 113 references.
Cite
This paper
Ravindran Nair, S., Palacios-Muñoz, A., Martineau, S., Nasr, M., & Clemens, J. (2026). Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior. Nature communications, 17(1), 4026. https://
BibTeX
@article{ravindrannair20
author = {Ravindran Nair, Sarath and Palacios-Muñoz, Adrián and Martineau, Sage and Nasr, Malak and Clemens, Jan},
title = {{Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior}},
journal = {Nature communications},
year = {2026},
month = may,
volume = {17},
number = {1},
pages = {4026},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {42082469},
pmcid = {PMC13139495}
}
RIS
TY - JOUR
AU - Ravindran Nair, Sarath
AU - Palacios-Muñoz, Adrián
AU - Martineau, Sage
AU - Nasr, Malak
AU - Clemens, Jan
TI - Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 4026
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior",
"container-title": "Nature communications",
"author": [
{
"family": "Ravindran Nair",
"given": "Sarath"
},
{
"family": "Palacios-Muñoz",
"given": "Adrián"
},
{
"family": "Martineau",
"given": "Sage"
},
{
"family": "Nasr",
"given": "Malak"
},
{
"family": "Clemens",
"given": "Jan"
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "4026",
"DOI": "10.1038/
"PMID": "42082469",
"PMCID": "PMC13139495",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
4
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.3389/fnsys.2026.1822122 [code]
- Convergence-divergence circuits for multimodal integration of innate and learned opponent valences.Journal: Frontiers in systems neuroscienceIn common: Keras, UMAP, Numba, 11 other tools, systems, 13 references
- [2] doi:10.1038/s41586-026-10735-w [code]
- Distributed control circuits across a brain-and-cord connectome.Journal: NatureIn common: UMAP, scikit-image, PyTorch, 6 other tools, drosophila, 9 references
- [3] doi:10.1093/g3journal/jkag037 [code]
- Visual recognition of the anteroposterior female body axis drives spatial elements of male courtship in Drosophila.Journal: G3 (Bethesda, Md.)In common: scikit-image, h5py, scikit-learn, 4 other tools, drosophila, 8 references
- [4] doi:10.1038/s41592-026-03057-2 [code]
- CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species.Journal: Nature methodsIn common: PyWavelets, Keras, UMAP, 12 other tools
- [5] doi: [code]
- Real-time closed-loop feedback system for mouse mesoscale cortical signal and movement controlJournal: eLifeIn common: statannotations, Keras, Numba, 12 other tools
- [6] doi:10.1371/journal.pcbi.1014571 [code]
- SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification.Journal: PLoS computational biologyIn common: Keras, Numba, TensorFlow, 11 other tools, 2 references
- [7] doi:10.1038/s41467-026-72437-1 [code]
- High-speed whole-brain imaging in Drosophila.Journal: Nature communicationsIn common: OpenCV, scikit-image, h5py, 7 other tools, drosophila, systems, 4 references
- [8] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: Keras, UMAP, Numba, 12 other tools
- [9] doi:10.1038/s41467-026-72152-x [code]
- Centralized brain networks controlling antennal grooming coordination.Journal: Nature communicationsIn common: TensorFlow, OpenCV, h5py, 7 other tools, drosophila, systems, 4 references
- [10] doi:10.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: xarray, Numba, OpenCV, 11 other tools, systems
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 9 repositories of the authors' code, each at its verified commit and with its license, 350 scripts, and 11 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:1852e1bbcd99e256…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
- janclemenslab/socialUMAP: README.md on GitHub
- jgraving/DeepPoseKit: README.md on GitHub
- janclemenslab/das: README.md on GitHub
- janclemenslab/glm_utils: README.md on GitHub
- lindermanlab/ssm: README.md on GitHub
- lmcinnes/umap: README.rst on GitHub
- tommyod/KDEpy: README.md on GitHub
- trevismd/statannotations: README.md on GitHub
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
