Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions biolearn/dunedin_pace.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ def quantile_normalize_using_target(data, target_values):
"""
Apply quantile normalization on data using target values.
"""
# Copy to a writable float array. Inputs coming from ``DataFrame.values``

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment could be condensed significantly

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — condensed it to two lines, keeping just the why (read-only DataFrame.values under pandas 3.0 copy-on-write breaking the in-place writes). Pushed in bf51b76. Thanks for the review!

# can be read-only (e.g. pandas copy-on-write in pandas >= 3.0), which
# would otherwise raise "assignment destination is read-only" on the
# in-place updates below.
data = np.array(data, dtype=float)
sorted_target = np.sort(target_values)

for _, column_data in enumerate(data.T):
Expand Down
41 changes: 41 additions & 0 deletions biolearn/test/test_dunedin_pace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import numpy as np
import pandas as pd

from biolearn.dunedin_pace import quantile_normalize_using_target


def _target():
return list(np.linspace(0.0, 1.0, 10))


def test_quantile_normalize_accepts_read_only_array():
# Arrays from DataFrame.values can be read-only (e.g. pandas
# copy-on-write in pandas >= 3.0). Normalization must not fail with
# "assignment destination is read-only". See issue #195.
data = pd.DataFrame(np.random.rand(10, 3)).values
data.flags.writeable = False

result = quantile_normalize_using_target(data, _target())

assert result.shape == (10, 3)


def test_quantile_normalize_does_not_mutate_input():
data = pd.DataFrame(np.random.rand(10, 3)).values
original = data.copy()

quantile_normalize_using_target(data, _target())

np.testing.assert_array_equal(data, original)


def test_quantile_normalize_maps_values_onto_target_distribution():
target = _target()
data = pd.DataFrame(np.random.rand(10, 3)).values

result = quantile_normalize_using_target(data, target)

sorted_target = np.sort(target)
midpoints = 0.5 * (sorted_target[:-1] + sorted_target[1:])
allowed = np.round(np.concatenate([sorted_target, midpoints]), 6)
assert np.all(np.isin(np.round(result, 6), allowed))
Loading