Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • swain-lab/aliby/aliby-mirror
  • swain-lab/aliby/alibylite
2 results
Show changes
Commits on Source (7)
  • Arin Wongprommoon's avatar
    fix(postproc): detrend process only removes columns with all NaNs · 71150227
    Arin Wongprommoon authored
    WHY IS THIS CHANGE NEEDED?:
    - detrend process overzealously removes columns if input dataframes
      have NaNs near the beginning or end.
    - this is because by default, df.dropna() uses how="any", i.e. it drops
      columns with at least one NaN
    
    HOW DOES THE CHANGE SOLVE THE PROBLEM?:
    - drops columns with at least one NaN is not the intention -- the
      intention is to remove column filled with NaNs created by rolling
      window operations.
    - so, specified the how argument
    
    WHAT SIDE EFFECTS DOES THIS CHANGE HAVE?:
    - i honestly don't know why i didn't catch it in the 1.5 years this
      process existed -- this seems so obvious.
    - this is a deprecated signal processing post-process (i use the
      butterworth filter instead), so i don't expect it to break things much
    
    EVIDENCE THAT COMMIT WORKS:
    71150227
  • Arin Wongprommoon's avatar
    Update bug.md · 14b8dd05
    Arin Wongprommoon authored
    14b8dd05
  • Arin Wongprommoon's avatar
    Update bug.md · 9a89fb6d
    Arin Wongprommoon authored
    9a89fb6d
  • Arin Wongprommoon's avatar
    feat(postproc): crosscorr has stationary option · 97ed1cbb
    Arin Wongprommoon authored
    WHY IS THIS CHANGE NEEDED?:
    - crosscorr process does not have stationary option and therefore will
      not perform calculations correctly if all signals are in-phase.
    
    HOW DOES THE CHANGE SOLVE THE PROBLEM?:
    - Additional 'stationary' parameter that tells the process to compute
      the mean over replicates AND time points.
    
    WHAT SIDE EFFECTS DOES THIS CHANGE HAVE?:
    - crosscorr process not used anywhere else in aliby, so should not
      affect aliby.
    - crosscorr process used in multiple places in skeletons (particularly
      arin's projects).  this is an *additional* parameter, and the existing
      behaviour of crosscorr process is same as stationary=False, so i don't
      expect breaking changes.
    
    EVIDENCE THAT COMMIT WORKS:
    - new test to be run
    
    REFERENCES:
    - #72
    97ed1cbb
  • Arin Wongprommoon's avatar
    fix(postproc): crosscorr catches case where 2nd trace is defined · b88a56f1
    Arin Wongprommoon authored
    WHY IS THIS CHANGE NEEDED?:
    - https://gitlab.com/aliby/aliby/-/jobs/4743945774 complains
      "UnboundLocalError: local variable 'trace_B' referenced before
      assignment".
    - i had forgotten the case where trace_B is defined
    b88a56f1
  • Arin Wongprommoon's avatar
  • Arin Wongprommoon's avatar
    Merge branch '072-crosscorr-stationary' into 'dev' · 95958192
    Arin Wongprommoon authored
    feat(postproc): crosscorr has stationary option
    
    See merge request aliby/aliby!18
    95958192
## Summary
(Summarize the bug encountered concisely)
{Summarize the bug encountered concisely}
I confirm that I have (if relevant):
- [ ] Read the troubleshooting guide: https://gitlab.com/aliby/aliby/-/wikis/Troubleshooting-(basic)
- [ ] Updated aliby and aliby-baby.
- [ ] Tried the unit test.
- [ ] Tried a scaled-down version of my experiment (distributed=0, filter=0, tps=10)
- [ ] Tried re-postprocessing.
## Steps to reproduce
(How one can reproduce the issue - this is very important)
{How one can reproduce the issue - this is very important}
- aliby version: 0.1.{...}, or if development/unreleased version, commit SHA: {...}
- platform(s):
- [ ] Jura
- [ ] Other Linux, please specify distribution and version: {...}
- [ ] MacOS, please specify version: {...}
- [ ] Windows, please specify version: {...}
- experiment ID: {...}
- Any special things you need to know about this experiment: {...}
## What is the current bug behavior?
......@@ -19,6 +35,12 @@
(Paste any relevant logs - please use code blocks (```) to format console output, logs, and code, as
it's very hard to read otherwise.)
```
{PASTE YOUR ERROR MESSAGE HERE!!}
```
## Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
......@@ -13,6 +13,10 @@ class crosscorrParameters(ParametersABC):
Attributes
----------
stationary: boolean
If True, the underlying dynamic process is assumed to be
stationary with the mean a constant, estimated from all
data points.
normalised: boolean (optional)
If True, normalise the result for each replicate by the standard
deviation over time for that replicate.
......@@ -20,7 +24,7 @@ class crosscorrParameters(ParametersABC):
If True, return results only for positive lags.
"""
_defaults = {"normalised": True, "only_pos": False}
_defaults = {"stationary": False, "normalised": True, "only_pos": False}
class crosscorr(PostProcessABC):
......@@ -50,6 +54,10 @@ class crosscorr(PostProcessABC):
trace_dfB: dataframe (required for cross-correlation only)
An array of signal values, with each row a replicate measurement
and each column a time point.
stationary: boolean
If True, the underlying dynamic process is assumed to be
stationary with the mean a constant, estimated from all
data points.
normalised: boolean (optional)
If True, normalise the result for each replicate by the standard
deviation over time for that replicate.
......@@ -84,6 +92,12 @@ class crosscorr(PostProcessABC):
>>> plt.figure()
>>> plt.plot(ac.columns, ac.mean(axis=0, skipna=True))
>>> plt.show()
Reference
---------
Dunlop MJ, Cox RS, Levine JH, Murray RM, Elowitz MB (2008). Regulatory
activity revealed by dynamic correlations in gene expression noise.
Nat Genet, 40, 1493-1498.
"""
df = (
......@@ -97,25 +111,15 @@ class crosscorr(PostProcessABC):
n_replicates = trace_A.shape[0]
# number of time points
n_tps = trace_A.shape[1]
# deviation from the mean, where the mean is calculated over replicates
# at each time point, which allows for non-stationary behaviour
dmean_A = trace_A - np.nanmean(trace_A, axis=0).reshape((1, n_tps))
# standard deviation over time for each replicate
stdA = np.sqrt(
np.nanmean(dmean_A**2, axis=1).reshape((n_replicates, 1))
)
if trace_dfB is not None:
trace_B = trace_dfB.to_numpy()
# cross correlation
dmean_B = trace_B - np.nanmean(trace_B, axis=0).reshape((1, n_tps))
stdB = np.sqrt(
np.nanmean(dmean_B**2, axis=1).reshape((n_replicates, 1))
)
# autocorrelation if 2nd dataframe is not supplied
if trace_dfB is None:
trace_dfB = trace_dfA
trace_B = trace_A
else:
# auto correlation
dmean_B = dmean_A
stdB = stdA
# calculate correlation
trace_B = trace_dfB.to_numpy()
# find deviation from the mean
dmean_A, stdA = _dev(trace_A, n_replicates, n_tps, self.stationary)
dmean_B, stdB = _dev(trace_B, n_replicates, n_tps, self.stationary)
# lag r runs over positive lags
pos_corr = np.nan * np.ones(trace_A.shape)
for r in range(n_tps):
......@@ -148,3 +152,16 @@ class crosscorr(PostProcessABC):
)
else:
return pd.DataFrame(corr, index=df.index, columns=lags)
def _dev(y, nr, nt, stationary=False):
# calculate deviation from the mean
if stationary:
# mean calculated over time and over replicates
dy = y - np.nanmean(y)
else:
# mean calculated over replicates at each time point
dy = y - np.nanmean(y, axis=0).reshape((1, nt))
# standard deviation calculated for each replicate
stdy = np.sqrt(np.nanmean(dy**2, axis=1).reshape((nr, 1)))
return dy, stdy
......@@ -54,4 +54,6 @@ class detrend(PostProcessABC):
).mean()
# Detrend: subtract normalised time series by moving average
signal_detrend = signal.subtract(signal_movavg)
return signal_detrend.dropna(axis=1) # Remove columns with NaNs
# Rolling window operations create columns that are all NaNs at the left
# and right edges because of the maths. This line removes these columns.
return signal_detrend.dropna(axis=1, how="all")
import numpy as np
import pandas as pd
import pytest
from postprocessor.core.multisignal.crosscorr import (
crosscorr,
crosscorrParameters,
)
def generate_sinusoids_df(
time_axis,
num_replicates,
):
t = time_axis
ts = np.tile(t, num_replicates).reshape((num_replicates, len(t)))
s = 3 * np.sin(
2 * np.pi * ts + 2 * np.pi * np.random.rand(num_replicates, 1)
)
s_df = pd.DataFrame(s)
return s_df
@pytest.mark.parametrize("time_axis", [np.linspace(0, 4, 200)])
@pytest.mark.parametrize("num_replicates", [333])
def test_crosscorr(
time_axis,
num_replicates,
):
"""Tests croscorr.
Tests whether a crosscorr runner can be initialised with default
parameters and runs without errors.
"""
dummy_signal1 = generate_sinusoids_df(time_axis, num_replicates)
dummy_signal2 = generate_sinusoids_df(time_axis, num_replicates)
crosscorr_runner = crosscorr(crosscorrParameters.default())
_ = crosscorr_runner.run(dummy_signal1, dummy_signal2)
@pytest.mark.parametrize("time_axis", [np.linspace(0, 4, 200)])
@pytest.mark.parametrize("num_replicates", [333])
def test_autocorr(
time_axis,
num_replicates,
):
"""Tests croscorr.
Tests whether a crosscorr runner can be initialised with default
parameters and runs without errors, when performing autocorrelation.
"""
dummy_signal1 = generate_sinusoids_df(time_axis, num_replicates)
crosscorr_runner = crosscorr(crosscorrParameters.default())
_ = crosscorr_runner.run(dummy_signal1, dummy_signal1)