Skip to content

Commit

Permalink
enhance Result type
Browse files Browse the repository at this point in the history
  • Loading branch information
philopon committed Sep 6, 2017
1 parent 4990211 commit 6cb5763
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 15 deletions.
128 changes: 113 additions & 15 deletions mordred/_base/result.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import numpy as np
from six import string_types, integer_types

from .util import is_missing
from .descriptor import Descriptor


class Result(list):
class Result(object):
r"""Result type."""

__slots__ = ("_values", "_descriptors", "_name_to_index")

def __init__(self, r, d):
super(Result, self).__init__(r)
self._descriptors = d
self._values = list(r)
self._descriptors = list(d)
self._name_to_index = {str(a): i for i, a in enumerate(d)}

def fillna(self, value=float("nan")):
def fill_missing(self, value=np.nan):
r"""Replace missing value to "value".
Parameters:
Expand All @@ -19,11 +26,11 @@ def fillna(self, value=float("nan")):
"""
return self.__class__(
[(value if is_missing(v) else v) for v in self],
self._descriptors,
[(value if is_missing(v) else v) for v in self.values()],
self.keys(),
)

def dropna(self):
def drop_missing(self):
r"""Delete missing value.
Returns:
Expand All @@ -32,13 +39,45 @@ def dropna(self):
"""
newvalues = []
newdescs = []
for v, d in zip(self, self._descriptors):
for d, v in self.items():
if not is_missing(v):
newvalues.append(v)
newdescs.append(d)

return self.__class__(newvalues, newdescs)

def items(self):
r"""Get items.
Returns:
Iterable[(Descriptor, value)]
"""
return ((k, v) for k, v in zip(self.keys(), self.values()))

def keys(self):
r"""Get descriptors instances.
Returns:
Iterable[Descriptor]
"""
return iter(self._descriptors)

def values(self):
r"""Get descriptor values.
Returns:
Iterable[value]
"""
return iter(self._values)

__iter__ = values

def __reversed__(self):
return reversed(self._values)

def asdict(self, rawkey=False):
r"""Convert Result to dict.
Expand All @@ -52,12 +91,71 @@ def asdict(self, rawkey=False):
"""
if rawkey:
def keyconv(k):
return k
return dict(self.items())
else:
return {
str(k): v
for k, v in self.items()
}

@property
def ix(self):
r"""Access descriptor value by index.
>>> from mordred import Calculator, Lipinski
>>> from rdkit import Chem
>>> result = Calculator(Lipinski.Lipinski)(Chem.MolFromSmiles("C1CCCCC1"))
>>> result.ix[0]
True
"""
return GetValueByIndex(self._values)

@property
def name(self):
r"""Access descriptor value by descriptor name or instance.
>>> from mordred import Calculator, descriptors
>>> from rdkit import Chem
>>> result = Calculator(descriptors)(Chem.MolFromSmiles("C1CCCCC1"))
>>> result.name["C2SP3"]
1
"""
return GetValueByName(self._values, self._name_to_index)

def __getitem__(self, key):
if isinstance(key, (integer_types, slice)):
return self.ix[key]

elif isinstance(key, (string_types, Descriptor)):
return self.name[key]

else:
keyconv = str
raise TypeError(
"Result indices must be "
"integers, slices, strings or Descriptor instance, "
"not {}".format(key.__class__.__name__))

def __len__(self):
return len(self._descriptors)


class GetValueByIndex(object):
__slots__ = ("_values",)

def __init__(self, values):
self._values = values

def __getitem__(self, key):
return self._values[key]


class GetValueByName(object):
__slots__ = ("_values", "_name_to_index")

def __init__(self, values, name_to_index):
self._values = values
self._name_to_index = name_to_index

return {
keyconv(k): v
for k, v in zip(self._descriptors, self)
}
def __getitem__(self, key):
return self._values[self._name_to_index[str(key)]]
120 changes: 120 additions & 0 deletions mordred/tests/test_result_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from collections import Iterable

import numpy as np
from nose.tools import eq_, ok_, raises

from mordred import Result, Descriptor, error, is_missing


class Dummy1(Descriptor):
def __str__(self):
return "Dummy1"

def parameters(self):
return ()

def calculate(self):
return 1


class Dummy2(Descriptor):
def __str__(self):
return "Dummy2"

def parameters(self):
return ()

def calculate(self):
raise ValueError("error")


result = Result([1, error.Error(ValueError("error"), [])], [Dummy1(), Dummy2()])


def test_length():
eq_(len(result), 2)


def test_fill_missing():
assert np.isnan(result.fill_missing()[1])


def test_drop_missing():
eq_(len(result.drop_missing()), 1)


def test_items():
i = result.items()
yield ok_, isinstance(i, Iterable)
li = list(i)
yield eq_, len(li), 2
yield eq_, len(li[0]), 2

d, v = li[0]
yield ok_, isinstance(d, Descriptor)
yield ok_, isinstance(v, int)


def test_keys():
i = result.keys()
yield ok_, isinstance(i, Iterable)
li = list(i)
yield eq_, len(li), 2

yield ok_, isinstance(li[0], Descriptor)


def test_iter():
i = iter(result)
yield ok_, isinstance(i, Iterable)
li = list(i)
yield eq_, len(li), 2

yield ok_, isinstance(li[0], int)


def test_reversed():
i = reversed(result)
yield ok_, isinstance(i, Iterable)
li = list(i)
yield eq_, len(li), 2

yield ok_, isinstance(li[1], int)


def test_asdict_non_rawkey():
d = result.asdict()
yield eq_, len(d), 2
yield ok_, isinstance(d, dict)
for k in d.keys():
yield ok_, isinstance(k, str)


def test_asdict_rawkey():
d = result.asdict(True)
yield eq_, len(d), 2
yield ok_, isinstance(d, dict)
for k in d.keys():
yield ok_, isinstance(k, Descriptor)


def test_ix():
yield eq_, 1, result.ix[0]
yield ok_, is_missing(result.ix[1])


def test_name():
yield eq_, 1, result.name["Dummy1"]
yield ok_, is_missing(result.name["Dummy2"])


def test_getitem():
yield eq_, 1, result[0]
yield ok_, is_missing(result[1])
yield eq_, 1, result["Dummy1"]
yield ok_, is_missing(result["Dummy2"])


@raises(TypeError)
def test_getitem_raise():
result[1.2]

0 comments on commit 6cb5763

Please sign in to comment.