Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
21 changes: 21 additions & 0 deletions q2_types/distance_matrix/_deferred_setup/_transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,24 @@ def _4(data: pd.Series) -> LSMatFormat:
with ff.open() as fh:
dm.write(fh, format='lsmat')
return ff


@plugin.register_transformer
def _5(ff: LSMatFormat) -> pd.DataFrame:
dm = skbio.DistanceMatrix.read(str(ff), format='lsmat', verify=False)
return dm.to_data_frame()


@plugin.register_transformer
def _6(data: pd.DataFrame) -> LSMatFormat:
# Raise if index != columns because columns will be ignored later.
if not data.index.equals(data.columns):
raise ValueError(
"Distance matrix DataFrame index and columns must contain "
"the same IDs in the same order."
)
dm = skbio.DistanceMatrix(data, ids=data.index)
ff = LSMatFormat()
with ff.open() as fh:
dm.write(fh, format='lsmat')
return ff
17 changes: 13 additions & 4 deletions q2_types/distance_matrix/_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@
# The full license is in the file LICENSE, distributed with this software.
# ----------------------------------------------------------------------------

import skbio.io
import skbio
Comment thread
VinzentRisch marked this conversation as resolved.
from skbio.io import FileFormatError
from skbio.stats.distance import PairwiseMatrixError

import qiime2.plugin.model as model
from qiime2.plugin import ValidationError


class LSMatFormat(model.TextFileFormat):
def sniff(self):
sniffer = skbio.io.io_registry.get_sniffer('lsmat')
return sniffer(str(self))[0]
"""An lsmat distance matrix with non-empty square float data, unique IDs,
symmetric values, and a zero-valued diagonal.
"""
def _validate_(self, level):
try:
skbio.DistanceMatrix.read(str(self), format='lsmat')
except (FileFormatError, PairwiseMatrixError, ValueError) as error:
Comment thread
lizgehret marked this conversation as resolved.
raise ValidationError(error) from error


DistanceMatrixDirectoryFormat = model.SingleFileDirectoryFormat(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
s1 s1
s1 0.0 42.0
s1 42.0 0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
s1 s2
s1 0.0 invalid
s2 42.0 0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
s1 s2
s1 0.0 42.0
s2 41.0 0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
s1 s2
s1 1.0 42.0
s2 42.0 0.0
48 changes: 47 additions & 1 deletion q2_types/distance_matrix/tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from q2_types.distance_matrix import LSMatFormat, DistanceMatrixDirectoryFormat
from qiime2.plugin.testing import TestPluginBase
from qiime2.plugin import ValidationError
from skbio.io import FileFormatError
from skbio.stats.distance import DistanceMatrixError, PairwiseMatrixError


class TestFormats(TestPluginBase):
Expand All @@ -32,9 +34,53 @@ def test_lsmat_format_validate_negative(self):
filepath = self.get_data_path('not-lsmat')
format = LSMatFormat(filepath, mode='r')

with self.assertRaisesRegex(ValidationError, 'LSMat'):
with self.assertRaises(ValidationError) as error:
format.validate()

self.assertIsInstance(error.exception.__cause__.__cause__,
FileFormatError)

def test_lsmat_format_validate_rejects_non_symmetrical(self):
filepath = self.get_data_path('non-symmetrical-distance-matrix.tsv')
format = LSMatFormat(filepath, mode='r')

with self.assertRaisesRegex(ValidationError, 'symmetric') as error:
format.validate()

self.assertIsInstance(error.exception.__cause__.__cause__,
DistanceMatrixError)

def test_lsmat_format_validate_rejects_nonzero_diagonal(self):
filepath = self.get_data_path('nonzero-diagonal-distance-matrix.tsv')
format = LSMatFormat(filepath, mode='r')

with self.assertRaisesRegex(ValidationError, 'hollow') as error:
format.validate()

self.assertIsInstance(error.exception.__cause__.__cause__,
DistanceMatrixError)

def test_lsmat_format_validate_rejects_non_numeric_value(self):
filepath = self.get_data_path('non-numeric-distance-matrix.tsv')
format = LSMatFormat(filepath, mode='r')

with self.assertRaisesRegex(ValidationError,
'could not be read') as error:
format.validate()

self.assertIsInstance(error.exception.__cause__.__cause__, ValueError)

def test_lsmat_format_validate_rejects_duplicate_ids(self):
filepath = self.get_data_path('duplicate-ids-distance-matrix.tsv')
format = LSMatFormat(filepath, mode='r')

with self.assertRaisesRegex(ValidationError,
'IDs must be unique') as error:
format.validate()

self.assertIsInstance(error.exception.__cause__.__cause__,
PairwiseMatrixError)

def test_distance_matrix_directory_format(self):
# This test exists mainly to assert that the single-file directory
# format is defined and functional. More extensive testing is performed
Expand Down
31 changes: 31 additions & 0 deletions q2_types/distance_matrix/tests/test_transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,37 @@ def test_pd_series_to_skbio_distance_matrix(self):

self.assertEqual(obs, exp)

def test_lsmat_format_to_pd_data_frame(self):
filenames = ('distance-matrix-1x1.tsv', 'distance-matrix-2x2.tsv',
'distance-matrix-NxN.tsv')
for filename in filenames:
input, obs = self.transform_format(LSMatFormat,
pd.DataFrame,
filename=filename)
exp = skbio.DistanceMatrix.read(str(input)).to_data_frame()
pd.testing.assert_frame_equal(obs, exp)

def test_pd_data_frame_to_skbio_distance_matrix(self):
transformer = self.get_transformer(pd.DataFrame, LSMatFormat)

filenames = ('distance-matrix-1x1.tsv', 'distance-matrix-2x2.tsv',
'distance-matrix-NxN.tsv')
for filename in filenames:
exp = skbio.DistanceMatrix.read(self.get_data_path(filename))
obs = transformer(exp.to_data_frame())
obs = skbio.DistanceMatrix.read(str(obs))
self.assertEqual(obs, exp)

def test_pd_data_frame_to_lsmat_format_rejects_misordered_columns(self):
transformer = self.get_transformer(pd.DataFrame, LSMatFormat)
dm = skbio.DistanceMatrix.read(
self.get_data_path('distance-matrix-NxN.tsv'))
data = dm.to_data_frame().reindex(columns=dm.ids[::-1])

with self.assertRaisesRegex(
ValueError, "same IDs in the same order"):
transformer(data)


if __name__ == "__main__":
unittest.main()
Loading