Completed
Push — master ( 39262f...560cc3 )
by Rich
04:48
created

ChemicalObjectView.to_list()   A

Complexity

Conditions 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.125

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 1
cts 2
cp 0.5
rs 10
cc 1
crap 1.125
1
#! /usr/bin/env python
2
#
3
# Copyright (C) 2015-2016 Rich Lewis <[email protected]>
4
# License: 3-clause BSD
5
6 1
"""
7
## skchem.core.base
8
9
Define base classes for scikit chem objects
10
"""
11
12 1
from abc import ABCMeta, abstractmethod
13 1
import warnings
14 1
import numpy as np
0 ignored issues
show
Configuration introduced by
The import numpy could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
15 1
import pandas as pd
0 ignored issues
show
Configuration introduced by
The import pandas could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
16
17
18 1
class ChemicalObject(object):
19
20
    """ A mixin for each chemical object in scikit-chem. """
21
22 1
    @classmethod
23
    def from_super(cls, obj):
24
25
        """A method that converts the class of an object of parent class to that of the child. """
26
27 1
        obj.__class__ = cls
28 1
        return obj
29
30
31 1
class ChemicalObjectView(object):
32
33
    """ Abstract iterable view of chemical objects.
34
35
    Concrete classes inheriting from it should implement `__getitem__` and `__len__`.
36
    """
37
38 1
    __metaclass__ = ABCMeta
39
40 1
    def __init__(self, owner):
41
        """ Return a view """
42 1
        self.owner = owner
43
44 1
    @abstractmethod
45
    def __getitem__(self, index):
46
        # subclass call this, then implement the actual get if None
47 1
        if isinstance(index, slice):
48
            return self.to_list()[index]
49 1
        if isinstance(index, list) \
50
                and all(isinstance(i, bool) for i in index) \
51
                and len(index) == len(self):
52
            return [self[i] for i, ix in enumerate(index) if ix]
53 1
        elif isinstance(index, (list, tuple)):
54
            return [self[ix] for ix in index]
55
        else:
56 1
            return None
57
58 1
    @abstractmethod
59
    def __len__(self):
60
        pass
61
62 1
    def __iter__(self):
63 1
        return ChemicalObjectIterator(self)
64
65 1
    def __str__(self):
66 1
        return str(list(str(obj) for obj in self))
67
68 1
    @property
69
    def props(self):
70
        """ Return a property view of the objects in the view. """
71
72 1
        return MolPropertyView(self)
73
74 1
    def to_list(self):
75
        """ Return a list of objects in the view. """
76
77
        return list(self)
78
79 1
    def __repr__(self):
80 1
        return '<{class_} values="{values}" at {address}>'.format(
81
            class_=self.__class__.__name__,
82
            values=str(self),
83
            address=hex(id(self)))
84
85
86 1
class ChemicalObjectIterator(object):
87
88
    """  Iterator for chemical object views.  """
89
90 1
    def __init__(self, view):
91
        """ Create an iterator from a chemical object view. """
92 1
        self.view = view
93 1
        self._current = 0
94 1
        self._high = len(self.view)
95
96 1
    def __next__(self):
97 1
        if self._current >= self._high:
98 1
            raise StopIteration
99
        else:
100 1
            self._current += 1
101 1
            return self.view[self._current - 1]
102
103
    # py2 compat
104 1
    next = __next__
105
106
107 1
class View(object):
108
109
    """ View wrapper interface.  Conforms to the dictionary interface.
110
111
    Objects inheriting from this should implement the `keys`, `getitem`,
112
    `setitem` and `delitem` methods."""
113
114 1
    __metaclass__ = ABCMeta
115
116 1
    @abstractmethod
117
    def keys(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
118
        return []
119
120 1
    def get(self, index, default=None):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
121 1
        if index in self.keys():
122 1
            return self[index]
123
        else:
124 1
            return default
125
126 1
    def pop(self, index, default=None):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
127 1
        if default:
128
            val = self.get(index, default)
129
        else:
130 1
            val = self[index]
131 1
        self.remove(index)
132 1
        return val
133
134 1
    def clear(self):
135
        """ Remove all properties from the object. """
136 1
        for idx in self.keys():
137 1
            self.remove(idx)
138
139 1
    def items(self):
140
        """ Return an iterable of key, value pairs. """
141
142 1
        return list((k, self[k]) for k in self.keys())
143
144 1
    def remove(self, key):
145
        """ Remove a property from the object. """
146 1
        self.__delitem__(key)
147
148 1
    @abstractmethod
149
    def __getitem__(self, key):
150
        pass
151
152 1
    @abstractmethod
153
    def __setitem__(self, key, value):
154
        pass
155
156 1
    @abstractmethod
157
    def __delitem__(self, key):
158
        pass
159
160 1
    def __iter__(self):
161
        return iter(self.keys())
162
163 1
    def __str__(self):
164
        return str(self.to_dict())
165
166 1
    def __len__(self):
167 1
        return len(self.keys())
168
169 1
    def __repr__(self):
170 1
        return '<{klass} values="{values}" at {address}>'.format(
171
            klass=self.__class__.__name__,
172
            values=str(self),
173
            address=hex(id(self)))
174
175 1
    def to_dict(self):
176
        """ Return a dict of the properties on the object. """
177 1
        return dict(self)
178
179 1
    def to_series(self):
180
        """ Return a pd.Series of the properties on the object. """
181 1
        return pd.Series(self.to_dict())
182
183
184 1
class PropertyView(View):
185
    """ Property object wrapper.
186
187
     This provides properties for rdkit objects. """
188
189 1
    def __init__(self, owner):
190
        """ Initialize a PropertyView.
191
192
        Args:
193
            owner(skchem.ChemicalObject):
194
                A chemical object with properties, such as `skchem.Atom`."""
195
196 1
        self._owner = owner
197
198 1
    def keys(self):
199
        """ The available property keys on the object. """
200
201 1
        return list(k for k in self._owner.GetPropNames() if k[:1] != '_')
202
203 1
    def __getitem__(self, key):
204
205
        # we manually work out if it was a float that was stored, as GetProp
206
        # returns floats and ints set by SetDoubleProp and SetIntProp as strings
207 1
        value = self._owner.GetProp(str(key))
208 1
        try:
209 1
            return int(value)
210 1
        except ValueError:
211 1
            try:
212 1
                return float(value)
213 1
            except ValueError:
214 1
                return value
215
216 1
    def __setitem__(self, key, value):
217
218 1
        if not isinstance(key, str):
219 1
            warnings.warn("RDKit property keys can only be of type `str`.  Using `{key}` as a `str`.".format(key=key))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (118/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
220 1
            key = str(key)
221
222 1
        if key[0] == '_':
223
            warnings.warn("`{value}` is a private RDKit property key. "
224
                          "Using this may have unintended consequences.".format(value=value))
225
226 1
        if isinstance(value, str):
227 1
            self._owner.SetProp(key, value)
228 1
        elif isinstance(value, (int, np.int64, np.int32)):
229 1
            self._owner.SetIntProp(key, value)
230 1
        elif isinstance(value, (float, np.float64, np.float32)):
231 1
            self._owner.SetDoubleProp(key, value)
232
        else:
233
            warnings.warn("RDKit property keys can only be `str`, `int` or `float`."
234
                          "Using `{value}` as a `str`.".format(value=value))
235
            self._owner.SetProp(key, str(value))
236
237 1
    def __delitem__(self, index):
238 1
        self._owner.ClearProp(index)
239
240
241 1
class MolPropertyView(View):
242
243
    """ Mol property wrapper.
244
245
    This provides properties for the atom and bond views. """
246
247 1
    def __init__(self, obj_view):
248 1
        self._obj_view = obj_view
249
250 1
    def keys(self):
251
        """ The available property keys on the object. """
252
253 1
        res = set()
254 1
        for atom in self._obj_view:
255 1
            res = res.union(set(atom.props.keys()))
256 1
        return list(res)
257
258 1
    def get(self, key, default=None):
259 1
        return pd.Series((a.props.get(key, default) for a in self._obj_view), index=self._obj_view.index)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (105/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
260
261 1
    def __getitem__(self, key):
262 1
        if key not in self.keys():
263 1
            raise KeyError('No atoms have the property set.')
264 1
        return self.get(key, None)
265
266 1
    def __setitem__(self, key, value):
267 1
        if isinstance(value, (pd.Series, dict)):
268
            for idx, val in pd.compat.iteritems(value):
269
                self._obj_view[int(idx)].props[key] = val
270
        else:
271 1
            assert len(self._obj_view) == len(value), "Must pass same number of values as atoms."
272 1
            for atom, val in zip(self._obj_view, value):
273 1
                atom.props[key] = val
274
275 1
    def __delitem__(self, key):
276 1
        for atom in self._obj_view:
277 1
            atom.props.remove(key)
278
279 1
    def to_frame(self):
280
281
        """ Return a DataFrame of the properties of the objects of the molecular view. """
282
283 1
        return pd.DataFrame(dict(self), index=self._obj_view.index)
284
285 1
    def to_dict(self):
286
287
        """ Return a dict of the properties of the objectos fo the molecular view. """
288
289 1
        return {k: v.tolist() for k, v in self.items()}
290
291 1
    def __str__(self):
292 1
        return str(self.to_dict())
293
294