Source code for rsatoolbox.rdm.transform
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" transforms, which can be applied to RDMs
"""
from __future__ import annotations
from copy import deepcopy
import numpy as np
import networkx as nx
from scipy.stats import rankdata
from scipy.spatial.distance import squareform
from .rdms import RDMs
[docs]def rank_transform(rdms: RDMs, method: str = 'average') -> RDMs:
""" applies a rank_transform and generates a new RDMs object
This assigns a rank to each dissimilarity estimate in the RDM,
deals with rank ties and saves ranks as new dissimilarity estimates.
As an effect, all non-diagonal entries of the RDM will
range from 1 to (n_dim²-n_dim)/2, if the RDM has the dimensions
n_dim x n_dim.
Args:
rdms(RDMs): RDMs object
method(String):
controls how ranks are assigned to equal values
options are: ‘average’, ‘min’, ‘max’, ‘dense’, ‘ordinal’
Returns:
rdms_new(RDMs): RDMs object with rank transformed dissimilarities
"""
dissimilarities = rdms.get_vectors()
cfg = dict(method=method, nan_policy='omit')
dissimilarities = np.array(
[rankdata(dissimilarities[i], **cfg) for i in range(rdms.n_rdm)]
)
measure = rdms.dissimilarity_measure or ''
if '(ranks)' not in measure:
measure = (measure + ' (ranks)').strip()
return RDMs(
dissimilarities,
dissimilarity_measure=measure,
descriptors=deepcopy(rdms.descriptors),
rdm_descriptors=deepcopy(rdms.rdm_descriptors),
pattern_descriptors=deepcopy(rdms.pattern_descriptors)
)
[docs]def sqrt_transform(rdms: RDMs) -> RDMs:
""" applies a square root transform and generates a new RDMs object
This sets values blow 0 to 0 and takes a square root of each entry.
It also adds a sqrt to the dissimilarity_measure entry.
Args:
rdms(RDMs): RDMs object
Returns:
rdms_new(RDMs): RDMs object with sqrt transformed dissimilarities
"""
dissimilarities = rdms.get_vectors()
dissimilarities[dissimilarities < 0] = 0
dissimilarities = np.sqrt(dissimilarities)
if rdms.dissimilarity_measure is None:
dissimilarity_measure = 'sqrt of unknown measure'
elif rdms.dissimilarity_measure == 'squared euclidean':
dissimilarity_measure = 'euclidean'
elif rdms.dissimilarity_measure == 'squared mahalanobis':
dissimilarity_measure = 'mahalanobis'
else:
dissimilarity_measure = 'sqrt of' + rdms.dissimilarity_measure
rdms_new = RDMs(dissimilarities,
dissimilarity_measure=dissimilarity_measure,
descriptors=deepcopy(rdms.descriptors),
rdm_descriptors=deepcopy(rdms.rdm_descriptors),
pattern_descriptors=deepcopy(rdms.pattern_descriptors))
return rdms_new
[docs]def positive_transform(rdms: RDMs) -> RDMs:
""" sets all negative entries in an RDM to zero and returns a new RDMs
Args:
rdms(RDMs): RDMs object
Returns:
rdms_new(RDMs): RDMs object with sqrt transformed dissimilarities
"""
dissimilarities = rdms.get_vectors()
dissimilarities[dissimilarities < 0] = 0
rdms_new = RDMs(dissimilarities,
dissimilarity_measure=rdms.dissimilarity_measure,
descriptors=deepcopy(rdms.descriptors),
rdm_descriptors=deepcopy(rdms.rdm_descriptors),
pattern_descriptors=deepcopy(rdms.pattern_descriptors))
return rdms_new
[docs]def transform(rdms: RDMs, fun) -> RDMs:
""" applies an arbitray function ``fun`` to the dissimilarities and
returns a new RDMs object.
Args:
rdms(RDMs): RDMs object
Returns:
rdms_new(RDMs): RDMs object with sqrt transformed dissimilarities
"""
dissimilarities = rdms.get_vectors()
dissimilarities = fun(dissimilarities)
if rdms.dissimilarity_measure is None:
meas = 'transformed unknown measure'
else:
meas = 'transformed ' + rdms.dissimilarity_measure
rdms_new = RDMs(dissimilarities,
dissimilarity_measure=meas,
descriptors=deepcopy(rdms.descriptors),
rdm_descriptors=deepcopy(rdms.rdm_descriptors),
pattern_descriptors=deepcopy(rdms.pattern_descriptors))
return rdms_new
[docs]def minmax_transform(rdms: RDMs) -> RDMs:
'''applies a minmax transform to the dissimilarities and returns a new
RDMs object.
Args:
rdms(RDMs): RDMs object
Returns:
rdms_new(RDMs): RDMs object with minmax transformed dissimilarities
'''
dissimilarities = rdms.get_vectors()
for i in range(rdms.n_rdm):
d_max = dissimilarities[i].max()
d_min = dissimilarities[i].min()
dissimilarities[i] = (dissimilarities[i] - d_min) / (d_max - d_min)
if rdms.dissimilarity_measure is None:
meas = 'minmax transformed unknown measure'
else:
meas = 'minmax transformed ' + rdms.dissimilarity_measure
rdms_new = RDMs(dissimilarities,
dissimilarity_measure=meas,
descriptors=deepcopy(rdms.descriptors),
rdm_descriptors=deepcopy(rdms.rdm_descriptors),
pattern_descriptors=deepcopy(rdms.pattern_descriptors))
return rdms_new
[docs]def geotopological_transform(rdms: RDMs, low: float, up: float) -> RDMs:
'''applies a geo-topological transform to the dissimilarities and returns
a new RDMs object.
Reference: Lin, B., & Kriegeskorte, N. (2023). The Topology and Geometry
of Neural Representations. arXiv preprint arXiv:2309.11028.
Args:
rdms(RDMs): RDMs object
low(float): lower quantile
up(float): upper quantile
Returns:
rdms_new(RDMs): RDMs object with geotopological transformed dissimilarities
'''
dissimilarities = rdms.get_vectors()
gt_min = np.quantile(dissimilarities, low)
gt_max = np.quantile(dissimilarities, up)
dissimilarities[dissimilarities < gt_min] = 0
dissimilarities[(dissimilarities >= gt_min) & (dissimilarities <= gt_max)] = (
dissimilarities[(dissimilarities >= gt_min) & (dissimilarities <= gt_max)] - gt_min
) / (gt_max - gt_min)
dissimilarities[dissimilarities > gt_max] = 1
if rdms.dissimilarity_measure is None:
meas = 'geo-topological transformed unknown measure'
else:
meas = 'geo-topological transformed ' + rdms.dissimilarity_measure
rdms_new = RDMs(dissimilarities,
dissimilarity_measure=meas,
descriptors=deepcopy(rdms.descriptors),
rdm_descriptors=deepcopy(rdms.rdm_descriptors),
pattern_descriptors=deepcopy(rdms.pattern_descriptors))
return rdms_new
[docs]def geodesic_transform(rdms: RDMs) -> RDMs:
'''applies a geodesic transform to the dissimilarities and returns a
new RDMs object.
Reference: Lin, B., & Kriegeskorte, N. (2023). The Topology and Geometry
of Neural Representations. arXiv preprint arXiv:2309.11028.
Args:
rdms(RDMs): RDMs object
Returns:
rdms_new(RDMs): RDMs object with geodesic transformed dissimilarities
'''
dissimilarities = minmax_transform(rdms).get_vectors()
for i in range(rdms.n_rdm):
G = nx.from_numpy_array(squareform(dissimilarities[i]))
long_edges = []
long_edges = list(
filter(lambda e: e[2] == 1, (e for e in G.edges.data("weight"))))
le_ids = list(e[:2] for e in long_edges)
G.remove_edges_from(le_ids)
dissimilarities[i] = squareform(np.array(nx.floyd_warshall_numpy(G)))
if rdms.dissimilarity_measure is None:
meas = 'geodesic transformed unknown measure'
else:
meas = 'geodesic transformed ' + rdms.dissimilarity_measure
rdms_new = RDMs(dissimilarities,
dissimilarity_measure=meas,
descriptors=deepcopy(rdms.descriptors),
rdm_descriptors=deepcopy(rdms.rdm_descriptors),
pattern_descriptors=deepcopy(rdms.pattern_descriptors))
return rdms_new