* fingerprint.cpp/.h: Changed the FindFingerprint method to

accept
	const string& instead of string&. This is necessary for access 
to this
	function from Python.
	
	* scripts/python/pybel.py, testpybel.py, pybelapi.html: Added a 
method
	to Molecule for calculating a molecular fingerprint, handled by 
a new
	Fingerprint class.
This commit is contained in:
Noel O'Boyle
2006-12-16 21:51:22 +00:00
parent 3aed1bbe7d
commit 7f65eec16a
6 changed files with 169 additions and 9 deletions

View File

@@ -1,3 +1,13 @@
2006-12-13 Noel O'Boyle <baoilleach@gmail.com>
* fingerprint.cpp/.h: Changed the FindFingerprint method to accept
const string& instead of string&. This is necessary for access to this
function from Python.
* scripts/python/pybel.py, testpybel.py, pybelapi.html: Added a method
to Molecule for calculating a molecular fingerprint, handled by a new
Fingerprint class.
2006-12-15 Geoffrey Hutchison <babel@geoffhutchison.net>
* configure.in, configure, Doxyfile: Bump to 2.1.0b4 for upcoming snapshot.

View File

@@ -58,7 +58,7 @@ public:
static bool GetNextFPrt(std::string& id, OBFingerprint*& pFPrt);
/// \return a pointer to a fingerprint (the default if ID is empty), or NULL if not available
static OBFingerprint* FindFingerprint(std::string& ID);
static OBFingerprint* FindFingerprint(const std::string& ID);
/// \return the Tanimoto coefficient between two vectors (vector<unsigned int>& SeekPositions)
static double Tanimoto(const std::vector<unsigned int>& vec1, const std::vector<unsigned int>& vec2);

View File

@@ -120,7 +120,7 @@ class Molecule(object):
(refer to the Open Babel library documentation for more info).
Methods:
write()
write(), calcfp()
The original Open Babel molecule can be accessed using the attribute:
OBMol
@@ -175,6 +175,23 @@ class Molecule(object):
for atom in self.atoms:
yield atom
def calcfp(self, fptype=""):
"""Calculate a molecular fingerprint.
Optional parameters:
fptype -- the name of the Open Babel fingerprint type.
If fptype is not specified, the default Open Babel fingerprint
type is used. See the Open Babel library documentation for more
details.
"""
fp = ob.vectorUnsignedInt()
fingerprinter = ob.OBFingerprint.FindFingerprint(fptype)
if fingerprinter is None:
raise ValueError, "%s is not a recognised Open Babel Fingerprint type" % fptype
fingerprinter.GetFingerprint(self.OBMol, fp)
return Fingerprint(fp)
def write(self, format="SMI", filename=None, overwrite=False):
"""Write the molecule to a file or return a string.
@@ -272,6 +289,54 @@ class Atom(object):
"""
return "Atom: %d %s" % (self.atomicnum, self.coords.__str__())
def findbits(fp, bitsperint):
"""Find which bits are set in a list/vector.
This function is used by the Fingerprint class.
>>> findbits([13, 71], 8)
[1, 3, 4, 9, 10, 11, 15]
"""
ans = []
start = 1
for x in fp:
i = start
while x > 0:
if x % 2:
ans.append(i)
x >>= 1
i += 1
start += bitsperint
return ans
class Fingerprint(object):
"""A Molecular Fingerprint.
Required parameters:
obFingerprint -- a vector calculated by OBFingerprint.FindFingerprint()
Attributes:
fp -- the original obFingerprint
bits -- a list of bits set in the Fingerprint
Methods:
The "|" operator can be used to calculate the Tanimoto coeff. For example,
given two Fingerprints 'a', and 'b', the Tanimoto coefficient is given by:
tanimoto = a | b
"""
def __init__(self, obFingerprint):
self.fp = obFingerprint
def __or__(self, other):
return ob.OBFingerprint.Tanimoto(self.fp, other.fp)
def __getattr__(self, attr):
if attr == "bits":
# Create a bits attribute on-the-fly
return findbits(self.fp, ob.OBFingerprint.Getbitsperint())
else:
raise AttributeError, "Molecule has no attribute %s" % attr
def __str__(self):
return ", ".join([str(x) for x in self.fp])
class Smarts(object):
"""A Smarts Pattern Matcher

View File

@@ -31,6 +31,7 @@
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="pybel.html#Atom">Atom</a>
</font></dt><dt><font face="helvetica, arial"><a href="pybel.html#Fingerprint">Fingerprint</a>
</font></dt><dt><font face="helvetica, arial"><a href="pybel.html#Molecule">Molecule</a>
</font></dt><dt><font face="helvetica, arial"><a href="pybel.html#Outputfile">Outputfile</a>
</font></dt><dt><font face="helvetica, arial"><a href="pybel.html#Smarts">Smarts</a>
@@ -80,6 +81,42 @@ Data and other attributes defined here:<br>
<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Atom' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="Fingerprint">class <strong>Fingerprint</strong></a>(<a href="__builtin__.html#object">__builtin__.object</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>A&nbsp;Molecular&nbsp;<a href="#Fingerprint">Fingerprint</a>.<br>
&nbsp;<br>
Required&nbsp;parameters:<br>
&nbsp;&nbsp;&nbsp;obFingerprint&nbsp;--&nbsp;a&nbsp;vector&nbsp;calculated&nbsp;by&nbsp;OBFingerprint.FindFingerprint()<br>
&nbsp;<br>
Attributes:<br>
&nbsp;&nbsp;&nbsp;fp&nbsp;--&nbsp;the&nbsp;original&nbsp;obFingerprint<br>
&nbsp;&nbsp;&nbsp;bits&nbsp;--&nbsp;a&nbsp;list&nbsp;of&nbsp;bits&nbsp;set&nbsp;in&nbsp;the&nbsp;<a href="#Fingerprint">Fingerprint</a><br>
&nbsp;<br>
Methods:<br>
&nbsp;&nbsp;&nbsp;The&nbsp;"|"&nbsp;operator&nbsp;can&nbsp;be&nbsp;used&nbsp;to&nbsp;calculate&nbsp;the&nbsp;Tanimoto&nbsp;coeff.&nbsp;For&nbsp;example,<br>
&nbsp;&nbsp;&nbsp;given&nbsp;two&nbsp;Fingerprints&nbsp;'a',&nbsp;and&nbsp;'b',&nbsp;the&nbsp;Tanimoto&nbsp;coefficient&nbsp;is&nbsp;given&nbsp;by:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tanimoto&nbsp;=&nbsp;a&nbsp;|&nbsp;b<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="Fingerprint-__getattr__"><strong>__getattr__</strong></a>(self, attr)</dt></dl>
<dl><dt><a name="Fingerprint-__init__"><strong>__init__</strong></a>(self, obFingerprint)</dt></dl>
<dl><dt><a name="Fingerprint-__or__"><strong>__or__</strong></a>(self, other)</dt></dl>
<dl><dt><a name="Fingerprint-__str__"><strong>__str__</strong></a>(self)</dt></dl>
<hr>
Data and other attributes defined here:<br>
<dl><dt><strong>__dict__</strong> = &lt;dictproxy object&gt;<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dl>
<dl><dt><strong>__weakref__</strong> = &lt;attribute '__weakref__' of 'Fingerprint' objects&gt;<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;<a href="__builtin__.html#object">object</a>&nbsp;(if&nbsp;defined)</tt></dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
@@ -100,7 +137,7 @@ Attributes:<br>
(refer&nbsp;to&nbsp;the&nbsp;Open&nbsp;Babel&nbsp;library&nbsp;documentation&nbsp;for&nbsp;more&nbsp;info).<br>
&nbsp;<br>
Methods:<br>
&nbsp;&nbsp;&nbsp;<a href="#Molecule-write">write</a>()<br>
&nbsp;&nbsp;&nbsp;<a href="#Molecule-write">write</a>(),&nbsp;<a href="#Molecule-calcfp">calcfp</a>()<br>
&nbsp;&nbsp;<br>
The&nbsp;original&nbsp;Open&nbsp;Babel&nbsp;molecule&nbsp;can&nbsp;be&nbsp;accessed&nbsp;using&nbsp;the&nbsp;attribute:<br>
&nbsp;&nbsp;&nbsp;OBMol<br>&nbsp;</tt></td></tr>
@@ -121,6 +158,15 @@ This&nbsp;allows&nbsp;constructions&nbsp;such&nbsp;as&nbsp;the&nbsp;following:<b
<dl><dt><a name="Molecule-__str__"><strong>__str__</strong></a>(self)</dt></dl>
<dl><dt><a name="Molecule-calcfp"><strong>calcfp</strong></a>(self, fptype<font color="#909090">=''</font>)</dt><dd><tt>Calculate&nbsp;a&nbsp;molecular&nbsp;fingerprint.<br>
&nbsp;<br>
Optional&nbsp;parameters:<br>
&nbsp;&nbsp;&nbsp;fptype&nbsp;--&nbsp;the&nbsp;name&nbsp;of&nbsp;the&nbsp;Open&nbsp;Babel&nbsp;fingerprint&nbsp;type.<br>
&nbsp;<br>
If&nbsp;fptype&nbsp;is&nbsp;not&nbsp;specified,&nbsp;the&nbsp;default&nbsp;Open&nbsp;Babel&nbsp;fingerprint<br>
type&nbsp;is&nbsp;used.&nbsp;See&nbsp;the&nbsp;Open&nbsp;Babel&nbsp;library&nbsp;documentation&nbsp;for&nbsp;more<br>
details.</tt></dd></dl>
<dl><dt><a name="Molecule-write"><strong>write</strong></a>(self, format<font color="#909090">='SMI'</font>, filename<font color="#909090">=None</font>, overwrite<font color="#909090">=False</font>)</dt><dd><tt>Write&nbsp;the&nbsp;molecule&nbsp;to&nbsp;a&nbsp;file&nbsp;or&nbsp;return&nbsp;a&nbsp;string.<br>
&nbsp;<br>
Optional&nbsp;parameters:<br>
@@ -160,8 +206,7 @@ Optional&nbsp;parameters:<br>
&nbsp;&nbsp;&nbsp;overwrite&nbsp;(default&nbsp;is&nbsp;False)&nbsp;--&nbsp;if&nbsp;the&nbsp;output&nbsp;file&nbsp;already&nbsp;exists,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;should&nbsp;it&nbsp;be&nbsp;overwritten?<br>
Methods:<br>
&nbsp;&nbsp;&nbsp;<a href="#Outputfile-write">write</a>(molecule)<br>
&nbsp;&nbsp;&nbsp;<a href="#Outputfile-close">close</a>()<br>&nbsp;</tt></td></tr>
&nbsp;&nbsp;&nbsp;<a href="#Outputfile-write">write</a>(molecule)<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="Outputfile-__init__"><strong>__init__</strong></a>(self, format, filename, overwrite<font color="#909090">=False</font>)</dt></dl>
@@ -221,7 +266,13 @@ Data and other attributes defined here:<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-readfile"><strong>readfile</strong></a>(format, filename)</dt><dd><tt>Iterate&nbsp;over&nbsp;the&nbsp;molecules&nbsp;in&nbsp;a&nbsp;file.<br>
<td width="100%"><dl><dt><a name="-findbits"><strong>findbits</strong></a>(fp, bitsperint)</dt><dd><tt>Find&nbsp;which&nbsp;bits&nbsp;are&nbsp;set&nbsp;in&nbsp;a&nbsp;list/vector.<br>
&nbsp;<br>
This&nbsp;function&nbsp;is&nbsp;used&nbsp;by&nbsp;the&nbsp;<a href="#Fingerprint">Fingerprint</a>&nbsp;class.<br>
&nbsp;<br>
&gt;&gt;&gt;&nbsp;<a href="#-findbits">findbits</a>([13,&nbsp;71],&nbsp;8)<br>
[1,&nbsp;3,&nbsp;4,&nbsp;9,&nbsp;10,&nbsp;11,&nbsp;15]</tt></dd></dl>
<dl><dt><a name="-readfile"><strong>readfile</strong></a>(format, filename)</dt><dd><tt>Iterate&nbsp;over&nbsp;the&nbsp;molecules&nbsp;in&nbsp;a&nbsp;file.<br>
&nbsp;<br>
Required&nbsp;parameters:<br>
&nbsp;&nbsp;&nbsp;format<br>

View File

@@ -2,6 +2,39 @@ import os
import unittest
import pybel
class Test_fingerprint(unittest.TestCase):
"""Test the Fingerprint class"""
def setUp(self):
self.mols = [pybel.readstring("smi", "CCCC"),
pybel.readstring("smi", "CCCN")]
def testTanimoto(self):
"""Test the calculation of the Tanimoto coefficient"""
fps = [x.calcfp() for x in self.mols]
self.assertEqual(fps[0] | fps[1], 1/3.)
fps = [x.calcfp("FP3") for x in self.mols]
self.assertEqual(fps[0] | fps[1], 0.)
def teststringrepr(self):
"""Test the string representation and corner cases."""
self.assertRaises(ValueError, self.mols[0].calcfp, "Nosuchname")
self.assertRaises(AttributeError, self.accesstest)
self.assertEqual(str(self.mols[0].calcfp()),
'0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1073741824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0')
def accesstest(self):
# Should raise AttributeError
return self.mols[0].calcfp().nosuchname
def testbits(self):
"""Test whether the bits are set correctly."""
bits = [x.calcfp().bits for x in self.mols]
self.assertEqual(bits[0], [261, 385, 671])
bits = [set(x) for x in bits]
# Calculate the Tanimoto coefficient the old-fashioned way
tanimoto = len(bits[0] & bits[1]) / float(len(bits[0] | bits[1]))
self.assertEqual(tanimoto, 1/3.)
class Test_readstring(unittest.TestCase):
"""Test the ability to read and write to a string"""
def setUp(self):
@@ -154,7 +187,8 @@ class Test_cornercases(unittest.TestCase):
self.assertEqual(atom.atomicnum, 0)
if __name__=="__main__":
testgroups = [Test_readstring, Test_readfile, Test_cornercases, Test_atoms, Test_smarts]
testgroups = [Test_readstring, Test_readfile, Test_atoms,
Test_smarts, Test_cornercases, Test_fingerprint]
for testgroup in testgroups:
print "\n=== %s ===" % testgroup.__doc__
suite = unittest.makeSuite(testgroup)

View File

@@ -65,7 +65,7 @@ namespace OpenBabel
return true;
}
OBFingerprint* OBFingerprint::FindFingerprint(string& ID)
OBFingerprint* OBFingerprint::FindFingerprint(const string& ID)
{
if(ID.empty())
return _pDefault;