source: orange-bioinformatics/Orange/bioinformatics/obiDicty.py @ 1625:cefeb35cbfc9

Revision 1625:cefeb35cbfc9, 47.3 KB checked in by mitar, 2 years ago (diff)

Moving files around.

Line 
1import sys, pprint, time, re
2from itertools import *
3import urllib
4import urllib2
5import orange
6import socket
7import os
8from collections import defaultdict
9import orngServerFiles
10import pickle
11
12defaddress = "http://bcm.fri.uni-lj.si/microarray/api/index.php?"
13#defaddresspipa = "https://pipa.fri.uni-lj.si/pipa/script/api/orange.py?action="
14defaddresspipa = "https://pipa.fri.uni-lj.si/PIPA/PIPAapi/PIPAorange.py"
15defaddresspipa_coverage = "https://pipa.biolab.si/tbrowse/api/pipa.py"
16
17pipaparuser = "pipa_username"
18pipaparpass = "pipa_password"
19
20#utility functions - from Marko's mMisc.py
21
22def splitN(origsize, maxchunk):
23    """
24    Splits an integer into chunks of given size. Each created chunk
25    except possibly the last one is of maximum allowed size.
26    Chunks are returned in list.
27    """
28    l = [maxchunk]*(origsize/maxchunk)
29    a = origsize % maxchunk
30    if a > 0: 
31        l.append(a)
32    return l
33
34def split(l, maxchunk):
35    """
36    Splits list l into chunks of size maxchunk. Each created chunk
37    except possibly the last one is of maximum allowed size.
38    """
39    sizes = splitN(len(l), maxchunk)
40    out = []
41    tillNow = 0
42    for s in sizes:
43        out.append(l[tillNow:tillNow+s])
44        tillNow += s
45    return out       
46
47def lloc(l,n):
48    """
49    List location in list of list structure.
50    Enable the use of negative locations:
51    -1 is the last element, -2 second last...
52    """
53    if n < 0:
54        return len(l[0])+n
55    else:
56        return n
57
58def loc(l,n):
59    """
60    List location.
61    Enable the use of negative locations:
62    -1 is the last element, -2 second last...
63    """
64    if n < 0:
65        return len(l)+n
66    else:
67        return n
68
69def nth(l,n):
70    """
71    Returns only nth elemnt in a list.
72    """
73    n = lloc(l,n)
74    return [ a[n] for a in l ]
75
76def imnth(l, ns):
77    """
78    Return only columns as specified in ns. Returns an generator.
79    """
80    ns = [ lloc(l,n) for n in ns ]
81    for a in l:
82        yield [ a[n] for n in ns ]
83
84def flatten(l,r=0):
85    """
86    Flatten a python structure into a list. Leave strings alone.
87    """
88    if type(l) == type("a"):
89        return [ l ]
90    try: #if enumerable then flatten it's elements
91        rec = [ flatten(a,r=r+1) for a in l ]
92        ret = []
93        for a in rec:
94            ret = ret + a
95        return ret
96    except:
97        return [ l ]
98
99def mxrange(lr):
100    """
101    Multiple xranges. Can be used to traverse matrices.
102    This function is very slow due to unknown number of
103    parameters.
104
105    >>> mxrange([3,5])
106    [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
107    >>> mxrange([[3,5,1],[9,0,-3]])
108    [(3, 9), (3, 6), (3, 3), (4, 9), (4, 6), (4, 3)]
109    """
110    if len(lr) == 0:
111        yield ()
112    else:
113        #it can work with single numbers
114        index = lr[0]
115        if type(1) == type(index):
116            index = [ index ]
117        for a in range(*index):
118            for b in mxrange(lr[1:]):
119                yield tuple([a] + list(b))
120
121
122def issequencens(x):
123    """
124    Is x a sequence and not string ? We say it is if it has a __getitem__
125    method and it is not an instance of basestring.
126    """
127    return hasattr(x, '__getitem__') and not isinstance(x, basestring)
128
129#end utility functions
130
131import statc
132#median = statc.median
133
134def median(l):
135    if len(l) == 0:
136        return None
137    else:
138        return statc.median(l)
139
140
141
142socket.setdefaulttimeout(60)
143
144verbose = 0
145
146class HttpGetException(Exception): pass
147
148def replaceChars(address):
149    return address.replace(" ", "%20")
150
151def httpGet(address, *args, **kwargs):
152    if verbose: 
153        print address, args, kwargs,  " "
154    address = replaceChars(address)
155    t1 = time.time()
156    f = urllib2.urlopen(address, *args, **kwargs)
157    read = f.read()
158    if verbose:
159        print "bytes", len(read),
160    if verbose:
161        print time.time() - t1
162    return read
163
164def txt2ll(s, separ=' ', lineSepar='\n'):
165    return [ a.split(separ) for a in s.split(lineSepar) ]
166
167class AuthenticationError(Exception):
168    pass
169
170class DBInterface(object):
171 
172    def __init__(self, address):
173        self.address = address
174
175    def raw(self, request, data=None, tryN=3):
176        if verbose:
177            print "tryN", tryN
178
179        def try_down():
180            try:
181                if data == None:
182                    return httpGet(self.address + request)
183                else:
184                    return httpGet(self.address + request, data=urllib.urlencode(data))
185            except urllib2.HTTPError, e:
186                if e.code == 403:
187                    raise AuthenticationError()
188                else:
189                    raise e
190
191        if tryN > 1:
192            try:
193                return try_down()
194            except IOError:
195                return self.raw(request, data=data, tryN=tryN-1)
196        else:
197            return try_down()
198
199    def get(self, request, data=None, tryN=3):
200        rawf = self.raw(request, data)
201        if rawf == None:
202            print "rawf == None!"
203            raise Exception("Connection error when contacting " + self.address + request)
204        if rawf.startswith("error: authentication failed"):
205            raise AuthenticationError()
206        elif rawf[:1] == "<" or rawf[:5] == "error" or rawf.startswith("MOD_PYTHON ERROR"): #an error occurred - starting some html input
207            #TODO are there any other kinds of errors?
208            if tryN > 0:
209                if verbose:
210                    print "trying again"
211                return self.get(request, data=data, tryN=tryN-1)
212            else:
213                if verbose:
214                    print rafw[:1000]
215                raise Exception("Error with the database")
216
217        a = txt2ll(rawf, separ='\t')
218       
219        if a[-1][0] == "": #remove empty line on end
220            a = a[:-1]
221        return a
222
223def _test():
224    import doctest
225    doctest.testmod()
226
227def splitTableOnColumn(ll,n):
228    omap = {}
229    n = lloc(ll,n)
230    for l in ll:
231        cell = omap.get(l[n], [])
232        cell.append(l)
233        omap[l[n]] = cell
234    return omap
235
236def neededColumns(legend, want):
237    return [ legend.index(a) for a in want ]
238
239def onlyColumns(ll, legend, want):
240    return list(imnth(ll, neededColumns(legend, want)))
241
242def ll2dic(ll, key=0, value=1):
243    """
244    Converts LL to map. Key is key position, value value position
245    """
246    names = nth(ll, lloc(ll, key))
247    if len(names) == len(set(names)):
248        return dict(list(imnth(ll, [ lloc(ll, key),  lloc(ll, value) ] )))
249    else:
250        raise Exception("all keys are not unique")
251
252def dic2ll(dic):
253
254    columns = sorted(dic.values()[0].keys())
255    ll = []
256    for key, d in dic.items():
257        ll.append([ key ] + [ d[a] for a in columns ])
258    return columns,ll
259
260def allUnique(els):
261    return len(els) == len(set(els))
262
263def reverseDic(d):
264    """
265    Create a reverse dictionary if a unique reverse is possible.
266    """
267    if allUnique(d.values()):
268        return dict([ (b,a) for a,b in d.items() ])
269
270def chainLookup(a, dics, force=[]):
271    """
272    Goes through a list of dictionaries. On each step try to
273    transform current key to value. The values becomes the key
274    for next search If unsuccessfull, query the next
275    dictionary with current key. Force last translation.
276    """
277    force = [ loc(dics, i) for i in force ]
278    for i,dic in enumerate(dics):
279        if i in force: #force specified translations
280            a = dic[a]
281        else:
282            if a in dic:
283                a = dic[a]
284    return a
285
286class DBCommon(object):
287
288    def fromBuffer(self, addr):
289        return self.buffer.get(self.address + addr)
290
291    def toBuffer(self, addr, cont, version, autocommit=True):
292        if self.buffer:
293            return self.buffer.add(self.address + addr, cont, version=version, autocommit=autocommit)
294
295    def bufferCommit(self):
296        if self.buffer:
297            self.buffer.commit()
298
299    def bufferFun(self, bufkey, bufver, reload, fn, *args, **kwargs):
300        """
301        If bufkey is already present in buffer, return its contents.
302        If not, run function with arguments and save its result
303        into the buffer.
304        """
305        if self.inBuffer(bufkey) == bufver and reload == False:
306            res = self.fromBuffer(bufkey)
307        else:
308            res = fn(*args, **kwargs)
309            self.toBuffer(bufkey, res, bufver)
310        return res
311
312    def sq(self, s1, data=None, buffer=True, bufadd="", bufname=None, bufver="0", reload=False, bufferkey=None, db=None):
313        if db == None:
314            db = self.db
315        if buffer:
316            bufkey = bufadd + (bufname if bufname != None else s1)
317            if bufferkey != None:
318                bufkey = bufferkey(bufkey, data)
319            res = self.bufferFun(bufkey, bufver, reload, db.get, s1, data=data)
320        else:
321            res = db.get(s1, data=data)
322        return res[1:],res[0]
323
324    def inBuffer(self, addr):
325        if self.buffer:
326            return self.buffer.contains(self.address + addr)
327        else:
328            return False
329
330    def dictionarize(self, ids, fn, *args, **kwargs):
331        """
332        Creates a dictionary from id: function result.
333        Callback for each part done.
334        """
335        callback = kwargs.pop("callback", None)
336        odic = {}
337        for a,b in izip(ids, fn(*args, **kwargs)):
338            odic[a] = b
339            if callback: callback()
340        return odic
341
342    def downloadMulti_bufcommand_replace_multi(self, command, data=None, chunk=100, bufferkey=None, transformfn=None):
343        """
344        Get function which gives buffer address for an id and a function
345        which replaces $MULTI$.
346        """
347
348        def bufferkey1(command, data):
349            if transformfn:
350                return "TRANS " + command
351            else:
352                return command
353
354        def replace_multi(command, data, repl):
355            return command.replace("$MULTI$", repl),\
356                dict((a,b.replace("$MULTI$", repl)) for a,b in sorted(data.items())) if data != None else None
357
358        if bufferkey == None:
359            bufferkey=bufferkey1
360
361        bufcommand = lambda x, c=command, d=data: bufferkey(*replace_multi(c, d, x))
362        return bufcommand, replace_multi
363
364    def downloadMulti(self, command, ids, data=None, chunk=100, transformfn=None, bufferkey=None, separatefn=None, bufreload=False, bufver="0"):
365        """
366        Downloads multiple results at once.
367        Results in the same order as in ids.
368
369        Bufferkey transforms command and data into buffer key.
370        bufver is a function returning buffer version for a given id. if
371            a string is given, use it for all ids
372        """
373
374        sids = split(ids,chunk)
375   
376        bufverfn = None
377        if isinstance(bufver, basestring):
378            bufverfn = lambda x: bufver
379        else:
380            bufverfn = bufver
381
382        bufcommand, replace_multi = self.downloadMulti_bufcommand_replace_multi(command, data=data, chunk=chunk, bufferkey=bufferkey, transformfn=transformfn)
383
384        for i,sidp in enumerate(sids):
385
386            buffered = []
387            unbuffered = []
388       
389            for a in sidp:
390                if self.inBuffer(bufcommand(a)) == bufverfn(a) and bufreload == False:
391                    buffered.append(a)
392                else:
393                    unbuffered.append(a)
394
395            res = []
396            legend = []
397
398            if len(unbuffered) > 0:
399                com1, d1 = replace_multi(command, data, ",".join(unbuffered))
400                res, legend = self.sq(com1, data=d1, buffer=False) #get unbuffered part
401            else:
402                # get legend from buffer also
403                legend = self.fromBuffer(bufcommand(buffered[0]))[0]
404
405            #split on different values of the first column - first attribute
406
407            if not separatefn:
408                antss = splitTableOnColumn(res, 0)
409            else:
410                legend, antss = separatefn([legend]+res)
411
412            #if transform before saving is requested, do it
413            if transformfn:
414                nantss = {}
415                nlegend = None
416                for a,b in antss.items():
417                    nb, nlegend = transformfn(b, legend)
418                    nantss[a] = nb
419                legend = nlegend
420                antss = nantss
421 
422            #here save buffer
423            for a,b in antss.items():
424                self.toBuffer(bufcommand(a), [ legend ] + b, bufverfn(a), autocommit=False)
425            self.bufferCommit()
426           
427
428            #get buffered from the buffer
429            antssb = dict([ (b, self.fromBuffer(bufcommand(b))[1:]) for b in buffered ])
430            antss.update(antssb)
431
432            #put results in order
433            tl = []
434            for ci in sidp:
435                yield antss[ci], legend
436
437    def exampleTables(self, ids, chipsm=None, spotmap={}, callback=None, exclude_constant_labels=False, annots={}, chipfn=None, allowed_labels=None):
438        """
439        Create example tables from chip readings, spot mappings and
440        group specifications.
441
442        group is the output from "sortAnnotations" function.
443        spotmap is a dictionary of { spotid: gene }
444        chipsm is a dictionary of chip readings
445
446        Callback: number of chipids + 2
447        """
448
449        if verbose:
450            print "Creating example table"
451
452        if callback: callback()
453
454        amap = {}
455        amapnext = 0
456
457        togen = []
458
459        groupnames = []
460        groupvals = []
461        groupannots = []
462
463        if chipsm == None:
464            chipdl = chipfn(ids)
465
466        for chipid in ids:
467
468            if chipsm != None:
469                chipdata = chipsm[chipid]
470            else:
471                chipdata = chipdl.next()
472
473            if callback: callback()
474
475            #add to current position mapping
476            repeats = {}
477            for id,_ in chipdata:
478                rep = repeats.get(id, 0)
479                repeats[id] = rep+1
480                key = (id, rep)
481                if key not in amap:
482                    amap[key] = amapnext
483                    amapnext += 1
484
485            vals = [ None ] * len(amap)
486
487            repeats = {}
488            for id,v in chipdata:
489                rep = repeats.get(id, 0)
490                repeats[id] = rep+1
491                key = (id, rep)
492                putind = amap[key]
493                vals[putind] = v
494            groupvals.append(vals)
495
496            groupnames.append(chipid) 
497
498            newannots = [['id', str(chipid)]] #add chipid to annotations
499            if annots:
500                newannots += annots[chipid]
501            groupannots.append(newannots)
502
503        togen = (groupnames, groupvals, groupannots)
504       
505        if callback: callback()
506
507        ddb = [ None ]*len(amap)
508        for (a,rep),pos in amap.items():
509            if len(spotmap):
510                ddb[pos] = spotmap.get(a, "#"+a)
511            else:
512                ddb[pos] = a
513
514        #this is sorted position mapping: key -> sortedind
515        posMap = dict( (k,i) for i,k in enumerate(sorted(amap.keys())) )
516        revmap = dict( ( (i,k) for k,i in amap.items() ) )
517        #permutation[i] holds target of current [i]
518        permutation = [ posMap[revmap[i]] for i in range(len(amap)) ]
519
520        def enlength(a, tlen):
521            """ Adds Nones to the end of the list """
522            if len(a) < tlen:
523                return a + [ "None" ]*(tlen-len(a))
524            else:
525                return a
526
527        def enlengthl(l, tlen):
528            return [ enlength(a, tlen) for a in l ]
529   
530        groupnames, groupvals, groupannots = togen
531
532        et = createExampleTable(groupnames, 
533            enlengthl(groupvals, len(ddb)),
534            groupannots, ddb, exclude_constant_labels=exclude_constant_labels, permutation=permutation, allowed_labels=allowed_labels)
535
536        if callback: callback()
537
538        return et
539
540def bufferkeypipa(command, data):
541    """ Do not save password to the buffer! """
542    command = command + " v5" #add version
543    if data != None:
544        data = data.copy()
545        if pipaparpass in data:
546            data.pop(pipaparpass)
547        return command + " " +  urllib.urlencode(sorted(data.items()))
548    else:
549        return command
550
551class PIPA(DBCommon):
552
553    def __init__(self, address=defaddresspipa, buffer=None, username=None, password=None):
554        self.address = address
555        self.db=DBInterface(address)
556        self.db_coverage=DBInterface(defaddresspipa_coverage) #temporary
557        self.buffer = buffer
558        self.username = None
559        if username != None:
560            self.username = username
561            self.password = password
562
563    def add_auth(self, data=None):
564        if self.username == None:
565            return data
566        authdic = { pipaparuser: self.username, pipaparpass: self.password }
567        if data != None:
568            authdic.update(data)
569        return authdic
570
571    def annotations(self, reload=False, bufver="0"):
572        """
573        Returns a dictionary of (id: dictionary of annotations).
574        """
575        #res, legend = self.sq("mapping_list", data=self.add_auth(), reload=reload, bufferkey=bufferkeypipa, bufver=bufver)
576        res, legend = self.sq("", data=self.add_auth({"action": "expression_list"}), reload=reload, bufferkey=bufferkeypipa, bufver=bufver)
577        return dict( (sa[0], dict(zip(legend[1:], sa[1:]))) for sa in res )
578
579    def chips(self, ids, ctype, reload=False, bufver="0"):
580        def separatefn(res):
581            #each one is own rown
582            #genes are in the first row
583            #remove unknown
584            genes = res[0][1:]
585            cids = nth(res,0)[1:]
586
587            antss = {}
588            for i,cid in enumerate(cids):
589                row = i+1
590                vals = res[row][1:]
591                antss[cid] = [ list(a) for a in zip(genes, vals) if a[1] != "?" ]
592            return ['gene_id', 'value'], antss
593
594        download_command = "gene_expression"
595        datadict = {"action": download_command, "ids":"$MULTI$", 'transpose':'1'}
596        if ctype != None:
597            datadict["type"] = ctype
598        antss = self.downloadMulti("", ids, data=self.add_auth(datadict), chunk=10, separatefn=separatefn, bufferkey=bufferkeypipa, bufreload=reload, bufver=bufver)
599        for a,legend in antss:
600            yield a
601
602    def coverage(self, id_, genome, chromosome, reload=False, bufver="0", raw=False):
603        data = self.add_auth({"action": "coverage_strand", "chr_name": str(chromosome), 
604            "pixel_size": "1", "genome": genome, "sample_id": str(id_), "raw": str(raw).lower()})
605
606        def comp():
607
608            d = dict(data)
609
610            def get():
611                #sq returns [ "all lines after first", "firstline"] - take first column!
612                #do not use buffering on this stage!
613                a = self.sq("", data=d, reload=reload, buffer=False, bufferkey=bufferkeypipa, bufver=bufver, db=self.db_coverage)[1][0].strip("[]")
614                return map(int, a.split(", "))
615               
616            d["strand"] = "plus"
617            plus = get()
618            d["strand"] = "minus"
619            minus = get()
620
621            import numpy
622            return numpy.array([plus, minus])
623       
624        bufkey =  bufferkeypipa("", data)
625        return self.bufferFun(bufkey, bufver, reload, comp)
626
627
628    def gene_expression_types(self, reload=False, bufver="0"):
629        #res, legend = self.sq("gene_expression_type", data={self.add_auth(), reload=reload, bufferkey=bufferkeypipa, bufver=bufver)
630        res, legend = self.sq("", data=self.add_auth({"action":"gene_expression_type"}), reload=reload, bufferkey=bufferkeypipa, bufver=bufver)
631        return sorted(tuple(a) for a in res)
632
633    def chips_keynaming(self, ctype):
634        keynamingfn,_ = self.downloadMulti_bufcommand_replace_multi("", data=self.add_auth({"action": "gene_expression", "ids":"$MULTI$", 'transpose':'1', 'type':ctype}), chunk=100, bufferkey=bufferkeypipa, transformfn=None)
635        return keynamingfn
636
637    def get_data(self, exclude_constant_labels=False, average=median, 
638        ids=None, callback=None, bufver="0", transform=None, ctype=None, allowed_labels=None):
639        """
640        Get data in a single example table with labels of individual attributes
641        set to annotations for query and post-processing
642        instructions.
643
644        Parameters:
645            average: function used for combining multiple reading of the same spot on
646                a chip. If None, no averaging is done. Fuction should take a list
647                of floats and return an "averaged" float.
648            ids: a list of chip ids. If absent, make a search
649            exclude_constant_labels: if a label has the same value in whole
650                example table, remove it
651            format: if short, use short format for chip download
652            ctype: expression type, from gene_expression_types
653
654        Defaults: Median averaging.
655        """
656
657        def optcb():
658            if callback: callback()
659
660        cbc = CallBack(1, optcb, callbacks=10)
661
662        if not ids:
663            return None
664
665        cbc.end()
666
667        #downloads annotations
668        cbc = CallBack(len(ids), optcb, callbacks=10)
669
670        readall = self.annotations()
671
672        read = {}
673        for a,b in readall.items():
674            read[a] = b.items()
675
676        cbc.end()
677
678        #till now downloads were small
679
680        import time
681        tstart = time.time()
682
683        #here download actually happens
684        chipfn = None
685
686        chipfn = lambda x: self.chips(x, ctype, bufver=bufver)
687       
688        if verbose:
689            print "DOWNLOAD TIME", time.time() - tstart
690
691        cbc = CallBack(len(ids)*2+len(ids)+1, optcb, callbacks=999-30)
692        et = self.exampleTables(ids, spotmap={}, callback=cbc, annots=read, exclude_constant_labels=exclude_constant_labels, chipfn=chipfn, allowed_labels=allowed_labels)
693        cbc.end()
694
695        cbc = CallBack(1, optcb, callbacks=10)
696
697        #transformation is performed prior to averaging
698        if transform != None:
699            transformValues(et, fn=transform) #in place transform
700            cbc()
701
702        #if average function is given, use it to join same spotids
703        if average != None:
704            et = averageAttributes(et, fn=average)
705            cbc()
706
707        cbc.end()
708
709        return et
710
711class DictyExpress(DBCommon):
712    """
713    Type is object id
714    """
715   
716    aoidPairs = txt2ll("""time extractions.developmental_time_point
717sample biological_samples.sample
718growthCond biological_samples.growth_condition
719treatment biological_samples.treatment
720replicate biological_sample_replicates.replicate
721techReplicate chips.replicate
722platform chips.chip_platform
723isTimeSeries biological_sample_replicates.is_time_series""")
724
725    obidPairs = txt2ll("""norms normalizations
726samples biological_samples
727replicates biological_sample_replicates
728analysis analysis
729experiments experiments
730extractions extractions
731chips chips""")
732
733    def __init__(self, address=defaddress, buffer=None):
734        self.address = address
735        self.db = DBInterface(address)
736        self.buffer = buffer
737        self.preload()
738
739    def preload(self):
740
741        # aoids are mappings from annotation name to annotation id
742        self.aoids = ll2dic(self.__annotationTypes(), 1, 0)
743        self.saoids = ll2dic(self.aoidPairs, 0, 1)
744        self.aoidsr = reverseDic(self.aoids)
745        self.saoidsr = reverseDic(self.saoids)
746
747        # obids are mappings from object id to annotation id
748        self.obids = ll2dic(self.__objects(), 1, 0)
749        self.sobids = ll2dic(self.obidPairs, 0, 1)
750        self.obidsr = reverseDic(self.obids)
751        self.sobidsr = reverseDic(self.sobids)
752
753    def aoidt(self, s):
754        return chainLookup(s, [self.saoids, self.aoids], force=[-1])
755
756    def obidt(self, s):
757        return chainLookup(s, [self.sobids, self.obids], force=[-1])
758 
759    def aoidtr(self, s, **kwargs):
760        return chainLookup(s, [self.aoidsr, self.saoidsr], **kwargs)
761
762    def obidtr(self, s):
763        return chainLookup(s, [self.obidsr, self.sobidsr])
764
765    def pq(self, q):
766        """
767        Prepare query.
768        ||| separator between conditions,
769        *** denotes equivalence
770        """
771        o =  "|||".join([ self.aoidt(a) + "***" + b for a,b in q.items()])
772        return o
773
774    def geneInfo(self):
775        res,legend = self.sq("action=gene_info")
776        return res, legend
777
778    def annotationOptions(self, ao=None, onlyDiff=False, **kwargs):
779        """
780        Returns annotation options for given query. Returns all possible
781        annotations if the query is omitted.
782
783        If ao is choosen, only result
784        """
785        params = ""
786        if len(kwargs) > 0: params += "&query=" + self.pq(kwargs)
787        if ao: params += "&annotation_object_id=" +  self.aoidt(ao)
788        res,legend = self.sq("action=get_annotation_options%s" % (params), bufadd=self.address)
789        res = onlyColumns(res, legend, ['annotation_object_id', 'value' ])
790
791        #join columns with the annotation object id
792        joined = {}
793        for key,v in res:
794            key = self.aoidtr(key)
795            cur = joined.get(key, [])
796            cur.append(v)
797            joined[key] = cur
798
799        if onlyDiff:
800            joined = dict([ (a,b) for a,b in joined.items() if len(b)>1 ])
801
802        return dict([ (a, sorted(b)) for a,b in joined.items() ])
803
804    def annotation(self, type, id):
805        return list(self.annotations(type, [ id ]))[0]
806
807    def meaningfulAnnot(self, name):
808        if name in self.saoids:
809            return True
810        else:
811            return False
812
813    def keepOnlyMeaningful(self, annot):
814        """
815        Keep only meaningful annotations
816        """
817        if type(annot) == type({}):
818            return dict( [ (a,b) for a,b in annot.items() \
819                if self.meaningfulAnnot(a) ] )
820        else:
821            return [ [ a,b ] for a,b in annot \
822                if self.meaningfulAnnot(a) ]
823
824
825    def annotations(self, type, ids=None, all=False):
826        """
827        Returns a generator returning annotations for specified type and ids.
828        If ids are left blank, all annotations are outputed. Annotations are in the same order
829        as input ids.
830        If all is True, all annotations are kept, else keep only "meaningful".
831        """
832       
833        inputids = False
834        if ids != None:
835            inputids = True
836            antss = self.downloadMulti(
837                "action=get_annotations&ids=$MULTI$&object_id=%s" 
838                % (self.obidt(type)), ids)
839        else:
840            res,legend = self.sq(
841                "action=get_annotations&object_id=%s"
842                % (self.obidt(type)))
843            antss = splitTableOnColumn(res, 0)
844            ids = nth(antss.items(),0)
845            antss = zip(nth(antss.items(),1), [ legend ]*len(antss))
846
847        for ants in izip(antss,ids):
848            (res, legend), id = ants
849            res2 = onlyColumns(res, legend, ['name', 'value'])
850            res2 = [ [ self.aoidtr(a),b ] for a,b in res2 ]
851            if not all:
852                res2 = self.keepOnlyMeaningful(res2)
853            if inputids:
854                yield res2
855            else:
856                yield (id, res2)
857
858    def search(self, type, **kwargs):
859        """
860        Break search for multiple values of one attribute to independant searches.
861        Search is case insensitive.
862       
863        List of searchable annotation types: self.saoids.keys()
864
865        example usage:
866        search("norms", platform='minichip', sample='abcC3-')
867            finds all ids of normalized entries where platform is minchip and
868            sample is abcC3-
869        search("norms", platform='minichip', sample=[ 'abcC3-', 'abcG15-'])
870            finds all ids of normalized entries where platform is minichip and
871            sample is abcC3- or those where platform is minichip and sample
872            is abcG15-
873        """
874       
875        #search for all combinations of values - this is slow!
876
877        l = []
878        for k,v in kwargs.items():
879            if not issequencens(v):
880                v = [ v ]
881            l.append((k,v))
882
883        ares = []
884
885        for r in mxrange([ len(v) for k,v in l ]):
886            dico = {}
887            for i,a in enumerate(r):
888                dico[l[i][0]] = l[i][1][a]
889
890            res,_ = self.sq("action=search&object_id=%s&query=%s" \
891                % (self.obidt(type), self.pq(dico)), bufadd=self.address)
892
893            ares += res
894
895        return sorted(set(nth(ares, 0)))
896
897
898    def chipN(self, id):
899        return list(self.chipNs([id]))[0]
900
901    def chipR(self, id):
902        return list(self.chipRs([id]))[0]
903 
904    def chipNs(self, ids, remove_bad=True):
905        """
906        If removebad = True removes those with weights 0.
907        """
908         
909        def sel(res, legend):
910            #Drop unwanted columns - for efficiency
911            res = onlyColumns(res, legend, ["spot_id", 'M', 'weights'])
912            legend = onlyColumns( [ legend ], legend, ["spot_id", 'M', 'weights'])[0]
913
914            def transf(a):
915                if a[2] == 0:
916                    return a[0], '', a[2]
917                else:
918                    return a[0], a[1], a[2]
919
920            res = [ transf(a) for a in res ]
921            res = onlyColumns(res, legend, ["spot_id", 'M'])
922            legend = onlyColumns( [ legend ], legend, ["spot_id", 'M'])[0]
923            return res, legend
924
925        antss = self.downloadMulti("action=get_normalized_data&ids=$MULTI$", ids, chunk=2, transformfn=sel)
926        for a,legend in antss:
927            yield a   
928
929    def chipNsN(self, ids, annots):
930        """
931        Download chips using new shorter format.
932        """
933        chip_map_ids = zip(ids,[ dict(a)['chips.chip_map_id'] for a in annots ])
934
935        def separateByChipsMaps(l):
936            begin = 0
937            cm = l[0][1]
938            cp = 0
939            for id,m in l[1:]:
940                cp += 1
941                if m != cm:
942                    yield l[begin:cp]
943                    cm = m
944                    begin = cp
945            yield l[begin:cp+1]
946       
947        sep = list(separateByChipsMaps(chip_map_ids))
948     
949        def sel(res, legend):
950            #Drop unwanted columns - for efficiency
951            res = onlyColumns(res, legend, ["spot_id", 'M'])
952            legend = onlyColumns( [ legend ], legend, ["spot_id", 'M'])[0]
953            return res, legend
954
955        def separatefn(res):
956            #each one is own rown
957            #genes are in the first row
958            genes = res[0][1:]
959            cids = nth(res,0)[1:]
960
961            antss = {}
962            for i,cid in enumerate(cids):
963                row = i+1
964                vals = res[row][1:]
965                antss[cid] = [ list(a) for a in zip(genes, vals) ]
966            return ['spot_id', 'M'], antss
967
968        for part in sep:
969            pids = nth(part,0)
970            antss = self.downloadMulti("action=get_normalized_data&mergeexperiments=1&ids=$MULTI$", pids, chunk=10, transformfn=sel, separatefn=separatefn)
971            for a, legend in antss:
972                yield a
973
974    def chipRs(self, id):
975        antss = self.downloadMulti("action=get_raw_data&ids=$MULTI$", ids, chunk=2)
976        for a,legend in antss:
977            yield a
978 
979    def spotId(self):
980        res,legend = self.sq("action=spot_id_mapping")
981        res2 = onlyColumns(res, legend, ["spot_id", 'ddb_g', 'genename'])
982        return res2
983
984    def annotationTypes(self):
985        """
986        Returns all annotation types.
987        """
988        return self.aoids.keys()
989
990    def objects(self):
991        """
992        Returns all objects.
993        """
994        return self.obids.keys()
995
996    def __annotationTypes(self):
997        """
998        Returns list of [ annotation_object_id, name ]
999        """
1000        res, legend = self.sq("action=get_annotation_types")
1001        res2 = onlyColumns(res, legend, ["annotation_object_id", 'name'])
1002        return res2
1003
1004    def __objects(self):
1005        res, legend = self.sq("action=get_objects")
1006        res2 = onlyColumns(res, legend, ["object_id", 'object_name'])
1007        return res2
1008
1009    def spotMap(self):
1010        spotids = self.spotId()
1011        spotmap = [ (a[0],a[1]) for a in spotids ]
1012
1013        spotmapd = {}
1014
1015        for a,b in spotmap:
1016            if a in spotmapd:
1017                spotmapd[a] = spotmapd[a] + "-" + b
1018            else:
1019                spotmapd[a] = b
1020
1021        return spotmapd
1022
1023    def getData(self, *args, **kwargs):
1024        deprecatedError("Use get_single_data instead")
1025
1026    def get_data(self, type="norms", exclude_constant_labels=False, average=median, 
1027        ids=None, callback=None, format="short", transform=None, allowed_labels=None, **kwargs):
1028        """
1029        Get data in a single example table with labels of individual attributes
1030        set to annotations for query and post-processing
1031        instructions.
1032
1033        Parameters:
1034            average: function used for combining multiple reading of the same spot on
1035                a chip. If None, no averaging is done. Fuction should take a list
1036                of floats and return an "averaged" float.
1037            ids: a list of chip ids. If absent, make a search
1038            exclude_constant_labels: if a label has the same value in whole
1039                example table, remove it
1040            format: if short, use short format for chip download
1041
1042        Defaults: Median averaging.
1043        """
1044
1045        def optcb():
1046            if callback: callback()
1047
1048        cbc = CallBack(1, optcb, callbacks=10)
1049
1050        if not ids:
1051            #returns ids of elements that match the search function
1052            ids = self.search(type, **kwargs)
1053
1054        cbc.end()
1055
1056        #downloads annotations
1057        cbc = CallBack(len(ids), optcb, callbacks=10)
1058
1059        readall = self.dictionarize(ids, self.annotations, type, ids, all=True, callback=cbc)
1060
1061        read = {}
1062        for a,b in readall.items():
1063            read[a] = self.keepOnlyMeaningful(b)
1064
1065        annotsinlist = [] #annotations in the same order
1066        for id in ids:
1067            annotsinlist.append(readall[id])
1068
1069        if verbose:
1070            print zip(ids,[ dict(a)['chips.chip_map_id'] for a in annotsinlist ])
1071
1072        cbc.end()
1073
1074        #till now downloads were small
1075
1076        import time
1077        tstart = time.time()
1078
1079        #here download actually happens
1080        chipfn = None
1081
1082        if type == "norms":
1083            if format == "short":
1084                chipfn = lambda x, al=annotsinlist: self.chipNsN(x, al)
1085            else:
1086                chipfn = self.chipNs
1087        else:
1088            chipfn = self.chipRs
1089       
1090        if verbose:
1091            print "DOWNLOAD TIME", time.time() - tstart
1092
1093        cbc = CallBack(len(ids)*2+len(ids)+1, optcb, callbacks=999-30)
1094        et = self.exampleTables(ids, spotmap=self.spotMap(), callback=cbc, annots=read, exclude_constant_labels=exclude_constant_labels, chipfn=chipfn, allowed_labels=allowed_labels)
1095        cbc.end()
1096
1097        cbc = CallBack(1, optcb, callbacks=10)
1098
1099        #transformation is performed prior to averaging
1100        if transform != None:
1101            transformValues(et, fn=transform) #in place transform
1102            cbc()
1103
1104        #if average function is given, use it to join same spotids
1105        if average != None:
1106            et = averageAttributes(et, fn=average)
1107            cbc()
1108
1109        cbc.end()
1110
1111        return et
1112   
1113    def get_single_data(self, *args, **kwargs):
1114        return self.get_data(*args, **kwargs)
1115
1116class DatabaseConnection(DictyExpress):
1117    pass
1118
1119def allAnnotationVals(annots):
1120    """
1121    All annotation valuess for given annotations
1122    in a dict of { name: set of possible values } pairs.
1123    """
1124    av = defaultdict(set)
1125    for a in annots:
1126        for name,val in a:
1127            av[name].add(val)
1128    return av
1129
1130def createExampleTable(names, vals, annots, ddb, cname="DDB", \
1131        exclude_constant_labels=False, permutation=None, always_include=["id"], allowed_labels=None):
1132    """
1133    Create an ExampleTable for this group. Attributes are those in
1134    names.
1135    """
1136    attributes = [ orange.FloatVariable(n, numberOfDecimals=3) \
1137        for n in names ]
1138
1139    #exclusion of names with constant values
1140    annotsvals = allAnnotationVals(annots)
1141    oknames = set(annotsvals.keys())
1142    if exclude_constant_labels:
1143        oknames = set(nth(filter(lambda x: len(x[1]) > 1 or x[0] in always_include, 
1144            annotsvals.items()), 0))
1145
1146    if allowed_labels != None:
1147        oknames = set(filter(lambda x: x in allowed_labels, oknames))
1148
1149    #print oknames
1150
1151    for a,an in zip(attributes, annots):
1152        a.setattr("attributes", dict([(name,str(val)) for name,val in an if name in oknames]))
1153
1154    domain = orange.Domain(attributes, False)
1155    ddbv = orange.StringVariable(cname)
1156    id = orange.newmetaid()
1157    domain.addmeta(id, ddbv)
1158
1159    examples = [ None ]*len(ddb)
1160    for i,(v,d) in enumerate(izip(izip(*vals), ddb)):
1161        ex = orange.Example(domain, [ floatOrUnknown(a) for a in v ])
1162        ex[cname] = d
1163
1164        if permutation:
1165            examples[permutation[i]] = ex
1166        else:
1167            examples[i] = ex
1168
1169    return orange.ExampleTable(domain,examples)
1170
1171def transformValues(data, fn):
1172    """
1173    In place transformation.
1174    """
1175    for ex in data:
1176        for at in data.domain.attributes:
1177            if not ex[at].isSpecial():
1178                ex[at] = fn(ex[at])
1179
1180def averageAttributes(data, joinc="DDB", fn=median):
1181    """
1182    Averages attributes with the same "join" parameter using
1183    specified function. Builds a now dataset. Order of
1184    "join" parameter stays the same with regard to first
1185    appearance.
1186    """
1187
1188    if verbose:
1189        print "Averaging attributes"
1190
1191    valueso = []
1192    valuess = set(valueso)
1193
1194    attributes = [ a for a in data.domain.attributes ]
1195    domain = data.domain
1196   
1197    #accumulate values
1198
1199    for ex in data:
1200        j = str(ex[joinc])
1201
1202        if j not in valuess:
1203            valueso.append(j)
1204            valuess.add(j)
1205
1206    valuesddb = [ dict( (n,[]) for n in valueso) for at in attributes ]
1207   
1208    for ex in data:
1209        j = str(ex[joinc])
1210
1211        for a in range(len(attributes)):
1212            val = ex[a]
1213            if not val.isSpecial():
1214                valuesddb[a][j].append(val.native())
1215
1216    #create a new example table reusing the domain - apply fn
1217    examples = []
1218    for v in valueso:
1219        example = orange.Example(domain, \
1220            [ floatOrUnknown(fn(valuesddb[a][v])) for a in range(len(attributes)) ] )
1221        example[joinc] = v
1222        examples.append(example)
1223
1224    return orange.ExampleTable(domain, examples)
1225
1226def floatOrUnknown(a):
1227    """
1228    Converts an element to float if possible.
1229    If now, output "?".
1230    """
1231    try:
1232        return float(a)
1233    except:
1234        return "?"
1235
1236class CallBack():
1237    """
1238    Converts "allparts" callbacks into by "callbacks"
1239    specified number of callbacks of function fn.
1240    """
1241
1242    def __init__(self, allparts, fn, callbacks=100):
1243        self.allparts = allparts
1244        self.lastreport = 0.00001
1245        self.getparts = 0
1246        self.increase = 1.0/callbacks
1247        self.callbacks = callbacks
1248        self.fn = fn
1249        self.cbs = 0
1250
1251    def __call__(self):
1252        self.getparts += 1
1253        done = float(self.getparts)/self.allparts
1254        while done > self.lastreport + self.increase:
1255            self.lastreport += self.increase
1256            self.fn()
1257            self.cbs += 1
1258
1259    def end(self):
1260        while self.cbs < self.callbacks:
1261            self.fn()
1262            self.cbs += 1
1263
1264class BufferSQLite(object):
1265
1266    def __init__(self, filename, compress=True):
1267        self.compress = compress
1268        self.filename = filename
1269        self.conn = self.connect()
1270
1271    def clear(self):
1272        """
1273        Removes all entries in the buffer
1274        """
1275        self.conn.close()
1276        os.remove(self.filename)
1277        self.conn = self.connect()
1278
1279    def connect(self):
1280        import sqlite3
1281        conn = sqlite3.connect(self.filename)
1282        c = conn.cursor()
1283        c.execute('''create table if not exists buf
1284        (address text primary key, time text, con blob)''')
1285        c.close()
1286        conn.commit()
1287        return conn
1288
1289    def contains(self, addr):
1290        """ Returns version or False, if it does not exists """
1291        c = self.conn.cursor()
1292        c.execute('select time from buf where address=?', (addr,))
1293        lc = list(c)
1294        c.close()
1295        if len(lc) == 0:
1296            return False
1297        else:
1298            return lc[0][0]
1299
1300    def list(self):
1301        c = self.conn.cursor()
1302        c.execute('select address from buf')
1303        return nth(list(c), 0)
1304
1305    def add(self, addr, con, version="0", autocommit=True):
1306        import cPickle, zlib, sqlite3
1307        if verbose:
1308            print "Adding", addr
1309        c = self.conn.cursor()
1310        if self.compress:
1311            bin = sqlite3.Binary(zlib.compress(cPickle.dumps(con)))
1312        else:
1313            bin = sqlite3.Binary(cPickle.dumps(con))
1314        c.execute('insert or replace into buf values (?,?,?)', (addr, version, bin))
1315        c.close()
1316        if autocommit:
1317            self.commit()
1318
1319    def commit(self):
1320        self.conn.commit()
1321
1322    def get(self, addr):
1323        import cPickle, zlib
1324        if verbose:
1325            print "getting from buffer", addr
1326            t = time.time()
1327        c = self.conn.cursor()
1328        c.execute('select con from buf where address=?', (addr,))
1329        ls = list(c)
1330        first = ls[0][0]
1331        if verbose:
1332            print time.time() - t
1333        if self.compress:
1334            rc = cPickle.loads(zlib.decompress(first))
1335        else:
1336            rc = cPickle.loads(str(first))
1337        c.close()
1338
1339        if verbose:
1340            print time.time() - t
1341        return rc
1342
1343def download_url(url, repeat=2):
1344    def do():
1345        return urllib2.urlopen(url)
1346
1347    if repeat <= 0:
1348        do()
1349    else:
1350        try:
1351            return do()
1352        except:
1353            return download_url(url, repeat=repeat-1)
1354
1355def empty_none(s):
1356    if s:
1357        return s
1358    else:
1359        return None
1360
1361def join_ats(atts):
1362    """ Joins attribute attributes together. If all values are the same,
1363    set the parameter to the common value, else return a list of the
1364    values in the same order as the attributes are imputed. """
1365    keys = reduce(lambda x,y: x | y, (set(at.keys()) for at in atts))
1366    od = {}
1367
1368    def man(x):
1369        if issequencens(x):
1370            return tuple(x)
1371        else:
1372            return x
1373
1374    for k in keys:
1375        s = set(man(at[k]) for at in atts)
1376        if len(s) == 1:
1377            od[k] = list(s)[0]
1378        else:
1379            od[k] = str([ at[k] for at in atts ])
1380    return od
1381
1382def join_replicates(data, ignorenames=["replicate", "id", "name", "map_stop1"], namefn=None, avg=median):
1383    """ Join replicates by median.
1384    Default parameters work for PIPA data.
1385    Sort elements in the same order as ignorenames!
1386    """
1387
1388    d = defaultdict(list)
1389
1390    if namefn == None:
1391        namefn = lambda att: ",".join(att["id"]) if issequencens(att["id"]) else att["id"]
1392
1393    #key function
1394    def key_g(att):
1395        dk = att.copy()
1396        for iname in ignorenames:
1397            dk.pop(iname, None)
1398       
1399        def man(x):
1400            if issequencens(x):
1401                return tuple(x)
1402            else:
1403                return x
1404
1405        return tuple(nth(sorted(((a, man(b)) for a,b in dk.items())), 1))
1406
1407    #prepare groups
1408    for i,a in enumerate(data.domain.attributes):
1409        att = a.attributes
1410        k = key_g(att)
1411        d[k].append(i)
1412
1413    d = dict(d) #want errors with wrong keys
1414
1415    natts = []
1416
1417    def nativeOrNone(val):
1418        if val.isSpecial(): 
1419            return None
1420        else: 
1421            return val.native()
1422
1423    def avgnone(l):
1424        """ Removes None and run avg function"""
1425        l = filter(lambda x: x != None, l)
1426        if len(l):
1427            return avg(l)
1428        else:
1429            return None
1430
1431    def data_type(vals): #data type converter
1432        try:
1433            _ = [ int(a) for a in vals ]
1434            return int
1435        except:
1436            try:
1437                _ = [ float(a) for a in vals ]
1438                return float
1439            except:
1440                return lambda x: x
1441
1442    all_values = defaultdict(set)
1443    for a in [ at.attributes for at in data.domain.attributes ]:
1444        for k,v in a.iteritems():
1445            all_values[k].add(v)
1446
1447    types = {}
1448    for k,vals in all_values.iteritems():
1449        types[k] = data_type(vals)
1450
1451    for group, elements in d.items():
1452        a = orange.FloatVariable()
1453        #here sort elements
1454   
1455        def sk(x):
1456            return [ types[n](x[n]) for n in ignorenames if n in all_values ]
1457
1458        elements = sorted(elements, key=lambda x: sk(data.domain.attributes[x].attributes))
1459
1460        a.attributes.update(join_ats([data.domain.attributes[i].attributes for i in elements]))
1461        a.name = namefn(a.attributes)
1462
1463        def avgel(ex, el):
1464            return orange.Value(avgnone([ nativeOrNone(ex[ind]) for ind in el ]))
1465
1466        a.getValueFrom = lambda ex,rw,el=elements: avgel(ex,el)
1467        natts.append(a)
1468
1469    ndom = orange.Domain(natts, data.domain.classVar)
1470    ndom.addmetas(data.domain.getmetas())
1471    return orange.ExampleTable(ndom, data)
1472
1473
1474class DictyBase(object):
1475
1476    domain = "dictybase"
1477    filename = "information_mappings.pck"
1478    tags = [ "Dictyostelium discoideum", "gene", "essential", "dictyBase" ] 
1479 
1480    @classmethod
1481    def version(cls):
1482        orngServerFiles.localpath_download(cls.domain, cls.filename)
1483        return orngServerFiles.info(cls.domain, cls.filename)["datetime"]
1484   
1485    @classmethod
1486    def download_information(cls):
1487        """
1488        Downloads gene information and parses it.
1489        Returns a dictionary {ID: (name, synonyms, products)}
1490        """
1491        s = download_url("http://www.dictybase.org/db/cgi-bin/dictyBase/download/download.pl?area=general&ID=gene_information.txt").read()
1492        out = []
1493        for l in txt2ll(s, separ='\t', lineSepar='\n')[1:]:
1494            if len(l) == 4:
1495                id = l[0]
1496                name = l[1]
1497                synonyms = filter(None, l[2].split(", "))
1498                products = l[3]
1499                out.append((id, name, synonyms, products))
1500        return dict((a,(b,c,d)) for a,b,c,d in out)
1501
1502    @classmethod
1503    def download_mappings(cls):
1504        """
1505        Downloads DDB-GeneID-UniProt mappings and parses them.
1506        Returns a list of (ddb, ddb_g, uniprot) triplets.
1507       
1508        2009/04/07: ddb's appear unique
1509        """
1510        s = download_url("http://www.dictybase.org/db/cgi-bin/dictyBase/download/download.pl?area=general&ID=DDB-GeneID-UniProt.txt").read()
1511        out = []
1512        for l in txt2ll(s, separ='\t', lineSepar='\n')[1:]:
1513            if len(l) == 4:
1514                ddb = empty_none(l[0])
1515                ddb_g = empty_none(l[1])
1516                name = empty_none(l[2])
1517                uniprot = empty_none(l[3])
1518                out.append((ddb, ddb_g, name, uniprot))
1519        return out
1520
1521    @classmethod
1522    def pickle_data(cls):
1523        info = cls.download_information()
1524        mappings = cls.download_mappings()
1525        return pickle.dumps((info,mappings), -1)
1526
1527    def __init__(self):
1528        fn = orngServerFiles.localpath_download(self.domain, self.filename)
1529        self.info, self.mappings = pickle.load(open(fn, 'rb'))
1530
1531if __name__=="__main__":
1532    verbose = 1
1533
1534    def printet(et):
1535        et.save("ett.tab")
1536        print open("ett.tab").read()
1537
1538    """
1539    a = DictyBase()
1540    print len(a.info)
1541
1542    dbc = DictyExpress(buffer=BufferSQLite("../tmpbufnew"))
1543
1544    print dbc.annotationOptions()
1545
1546    count = 0
1547    def cb():
1548        global count
1549        count += 1
1550        #print "CBBB", count
1551
1552    et = dbc.get_single_data(sample=[ "tagA-", "pkaC-"], callback=cb, exclude_constant_labels=True, allowed_labels=["sample"])
1553    print et.domain
1554    print et.domain[0].attributes
1555    printet(et)
1556    """
1557
1558    d = PIPA(buffer=BufferSQLite("../tmpbufnewpipa"))
1559
1560    print d.gene_expression_types()
1561
1562    cov = d.coverage("777", chromosome=1, genome="dd")
1563    print cov
1564
1565    allids = d.annotations().keys()
1566    allids = allids[:1]
1567    print ("list", allids)
1568    print d.annotations().items()[0]
1569    print ("annots", d.annotations().items()[:2])
1570
1571    allids = map(str, allids)
1572
1573    import math
1574
1575    data = d.get_data(ids=allids, ctype="3")
1576
1577    #data = d.get_data(ids=allids, transform=lambda x: math.log(x+1, 2), allowed_labels=["strain"], ctype="3")
1578
1579    printet(data)
Note: See TracBrowser for help on using the repository browser.