Changeset 1353:cbffc94cb604 in orange-bioinformatics


Ignore:
Timestamp:
03/24/11 15:00:42 (3 years ago)
Author:
ales_erjavec <ales.erjavec@…>
Branch:
default
Convert:
5154b0870d2d0ded9e1ac6454ed08f5ead4cb3d1
Message:

Added ArrayExpressExperiment class.
Added classes and functions for parsing MAGE-TAB standard files (this still needs lots of work).
Changed the xml parsing code to use the xml.etree parser.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • obiArrayExpress.py

    r1350 r1353  
    2020     
    2121    >>> obiArrayExpress.query_files(accession='E-MEXP-32', format="xml") 
    22     XMLNode(... 
     22    <xml.etree.ElementTree.ElementTree instance... 
    2323    
    2424.. note:: Currently querying ArrayExpress files only works with the xml format. 
     
    3333 
    3434import orngEnviron 
     35import orngServerFiles 
    3536import warnings 
    3637import posixpath 
    3738import shelve 
     39import shutil 
     40import posixpath 
    3841import json 
     42from xml.etree.ElementTree import ElementTree 
     43 
    3944from collections import defaultdict 
    4045 
     
    4247 
    4348def parse_xml(stream): 
    44     #TODO: parse stream, return the same structure as parse_json             
    45     from Orange.misc.xml import parse 
    46     tree = parse(stream) 
    47     return tree 
    48  
     49    """ Parse an xml stream into an instance of xml.etree.ElementTree.ElementTree. 
     50    """ 
     51    return ElementTree(file=stream)  
     52#    tree.parse(stream) 
     53#    return tree 
    4954 
    5055# All searchable fields of ArrayExpress (see query_experiments docstring 
     
    7580    ] 
    7681 
     82class forgetfull(dict): 
     83    """ A forgetfull dictionary. 
     84    """ 
     85    def __setitem__(self, key, value): 
     86        """ Do nothing. 
     87        """ 
     88        return 
     89     
     90 
    7791class ArrayExpressConnection(object): 
    7892    """ A connection to the ArrayExpress. Used for query construction, 
     
    8599    DEFAULT_FORMAT = "json" 
    86100     
     101    try: 
     102        DEFAULT_CACHE = shelve.open(orngServerFiles.localpath("ArrayExpress", "ArrayExpressCache.shelve")) 
     103    except Exception: 
     104        DEFAULT_CACHE = {} 
    87105    # Order of arguments in the query 
    88106    _ARGS_ORDER = ["keywords", "species", "array"] 
    89107     
    90     def __init__(self, address=None, timeout=30, 
     108    def __init__(self, address=None, timeout=30, cache=None, 
    91109                 username=None, password=None): 
    92110        """ Initialize the connection object. 
     
    100118        self.address = address if address is not None else self.DEFAULT_ADDRESS 
    101119        self.timeout = timeout 
     120        self.cache = cache if cache is not None else self.DEFAULT_CACHE 
     121        self.username = username 
     122        self.password = password 
     123         
    102124         
    103125    def format_query(self, **kwargs): 
     
    186208        url = url.format(format=kwargs.get("format", self.DEFAULT_FORMAT)) 
    187209        url = url + ("?" + query if query else "") 
    188 #        print url 
    189210        url = url.replace(" ", "%20") 
    190211        return url 
     
    208229        """ 
    209230        url = self.query_url_experiments(**kwargs) 
    210         stream = urllib2.urlopen(url, timeout=self.timeout) 
     231        stream = self._cache_urlopen(url, timeout=self.timeout) 
     232#        stream = urllib2.urlopen(url, timeout=self.timeout) 
    211233        return stream 
    212234     
     
    218240        """ 
    219241        url = self.query_url_files(**kwargs) 
    220         stream = urllib2.urlopen(url, timeout=self.timeout) 
     242        stream = self._cache_urlopen(url, timeout=self.timeout) 
     243#        stream = urllib2.urlopen(url, timeout=self.timeout) 
    221244        return stream 
    222245     
     
    237260              
    238261        """ 
    239         from Orange.misc.xml import parse  
    240         files = parse(self.query_files(accession=accession), format="xml") 
    241         files = list(files.elements("file")) 
     262        stream = self.query_files(accession=accession, format="xml") 
     263        tree = ElementTree(file=stream) 
     264        files = tree.findall("experiment/file") 
    242265        for file in files: 
    243             filekind = file.elements("kind").next() 
    244             fileext = file.elements("extension").next() 
    245             if filekind.data.strip() == kind and (fileext.data.strip() == ext or ext is None):  
    246                 url = file.elements("url").next() 
    247                 return urllib2.urlopen(url.data.strip(), timeout=self.timeout) 
    248      
     266            filekind = file.find("kind").text 
     267            fileext = file.find("extension").text 
     268            if filekind.strip() == kind and (fileext.strip() == ext or ext is None):  
     269                url = file.find("url").text 
     270                return self._cache_urlopen(url.strip(), timeout=self.timeout) 
     271#                return urllib2.urlopen(url.strip(), timeout=self.timeout) 
     272             
     273    def _cache_urlopen(self, url, timeout=30): 
     274        from StringIO import StringIO 
     275        if self.cache is not None and url in self.cache: 
     276            stream = StringIO(self.cache[url]) 
     277            return stream 
     278        elif self.cache is not None: 
     279            stream = urllib2.urlopen(url, timeout=timeout) 
     280            data = stream.read() 
     281            self.cache[url] = data 
     282            return StringIO(data) 
     283        else: 
     284            return urllib2.urlopen(url, timeout=timeout) 
     285         
    249286     
    250287def query_experiments(keywords=None, accession=None, array=None, ef=None, 
     
    310347    else: 
    311348        return parse_xml(stream) 
    312  
    313 def query_files(**kwargs): 
     349     
     350     
     351def query_files(keywords=None, accession=None, array=None, ef=None, 
     352               efv=None, expdesign=None, exptype=None, 
     353               gxa=None, pmid=None, sa=None, species=None, 
     354               expandefo=None, directsub=None, assaycount=None, 
     355               efcount=None, samplecount=None, rawcount=None, 
     356               fgemcount=None, miamescore=None, date=None,  
     357               format="json", wholewords=None, connection=None): 
    314358    """ Query Array Express files. 
    315359     
     
    319363     
    320364        >>> query_files(species="Mus musculus", ef="developmental_stage", efv="embryo", format="xml") 
    321         XMLNode(... 
     365        <xml.etree.ElementTree.ElementTree instance... 
     366         
     367    .. todo:: why does the json interface not work. 
    322368                         
    323369    """ 
    324     connection = kwargs.get("connection", None) 
    325370    if connection is None: 
    326371        connection = ArrayExpressConnection() 
    327372     
    328     stream = connection.query_files(**kwargs) 
    329     if kwargs.get("format", "json") == "json": 
     373    stream = connection.query_files(keywords=keywords, accession=accession, 
     374                array=array, ef=ef, efv=efv, expdesign=expdesign, exptype=exptype, 
     375                gxa=gxa, pmid=pmid, sa=sa, species=species, expandefo=expandefo, 
     376                directsub=directsub, assaycount=assaycount, efcount=efcount, 
     377                samplecount=samplecount, rawcount=rawcount, fgemcount=fgemcount, 
     378                miamescore=miamescore, date=date,  format=format, 
     379                wholewords=wholewords) 
     380     
     381    if format == "json": 
    330382        return parse_json(stream) 
    331383    else: 
    332384        return parse_xml(stream) 
     385     
     386     
     387def open_file(accession, kind="raw", ext=None, repo_dir=None): 
     388    """ Open a file for the experiment.  
     389      
     390    Example :: 
     391     
     392        >>> file = open_file("E-MTAB-369", kind="raw", repo_dir="~/.arrayexpress/") 
     393         
     394    """ 
     395    raise NotImplementedError 
     396 
     397"""\ 
     398MAGE-TAB convinence functions, classes 
     399====================================== 
     400""" 
     401 
     402IDF_SINGLE_TAGS = \ 
     403    ["Investigation Title", 
     404     "Date of Experiment", 
     405     "Public Release Date", 
     406     "Experiment Description", 
     407    ] 
     408 
     409def parse_idf(file): 
     410    """ Parse an idf.txt (Investigation Description  Format) formated file. 
     411    Return a list of tuples where the first element is the tag (first column) 
     412    and the second element is a list of all tag values. 
     413     
     414    """ 
     415    if isinstance(file, basestring): 
     416        file = open(file, "rb") 
     417    data = file.read() 
     418    lines = data.splitlines() 
     419    lines = [line.split("\t") for line in lines if line and not line.startswith("#")] 
     420    parsed = [(line[0], line[1:]) for line in lines] 
     421    return parsed 
     422     
     423def parse_sdrf(file): 
     424    """ Parse an sdfr formated file. Return a tuple with the first element 
     425    a list of header values and the second element is a list of all rows 
     426    (a row is a list of string values). 
     427     
     428    """ 
     429    if isinstance(file, basestring): 
     430        file = open(file, "rb") 
     431    data = file.read() 
     432    lines = data.splitlines() 
     433    lines = [line.split("\t") for line in lines if line.strip() and not line.startswith("#")] 
     434    header = [h for h in lines[0] if h] 
     435    rows = lines[1:] 
     436    assert(all([len(r) == len(header) for r in rows])) 
     437    return header, rows 
     438     
     439def parse_adf(file): 
     440    pass 
     441 
     442def parse_data_matrix(file): 
     443    """ Parse the MAGE-TAB processed data matrix. Return a tuple where the 
     444    elements are: 
     445        - a (header REF, header values) tuple (e.g. ("Hybridization REF", ["Stage1", "Stage2", ...]) ) 
     446        - a list of quantitations for header values (e.g. ["log2 ratio", "log2 ratio", ...]) 
     447        - a (row REF, row names list) tuple ("e.g. ("Reporter REF", ["Probe 1", "Probe 2", ...]) ) 
     448        - a list of list matrix with values (as strings) 
     449         
     450    """ 
     451    if isinstance(file, basestring): 
     452        file = open(file, "rb") 
     453    data = file.read() 
     454    lines = data.splitlines() 
     455    lines = [line.split("\t") for line in lines if line.strip()] 
     456    header = lines[0] 
     457    header_ref, header = header[0], header[1:] 
     458    line2 = lines[1] 
     459    row_ref, quanifications = line2[0], line2[1:] 
     460    row_names, rows = [], [] 
     461    for line in lines[2:]: 
     462        r_name, row = line[0], line[1:] 
     463        row_names.append(r_name) 
     464        rows.append(row) 
     465         
     466    return ((header_ref, header), 
     467            quanifications, 
     468            (row_ref, row_names), 
     469            rows)  
     470     
     471class InvesigationDesign(dict): 
     472    """ Investigation design (contains the contents of the .idf). 
     473     
     474    Example :: 
     475     
     476        >>> idf = InvestigationDesign("foobar.idf") 
     477        >>> print idf.investigation_title 
     478        foo investigation 
     479        >>> print idf.experimental_design 
     480        ['fubar', 'snafu'] 
     481        >>> print idf.sdrf_file 
     482        ['foobar.sdrf'] 
     483         
     484    """ 
     485    def __init__(self, idf_file=None): 
     486        idf = parse_idf(idf_file) 
     487        self._idf = idf 
     488        self.update(dict(idf)) 
     489        for tag, values in idf: 
     490            if tag in IDF_SINGLE_TAGS: 
     491                values = values[0] if values else None 
     492            ttag = self.transform_tag(tag) 
     493            setattr(self, ttag, values) 
     494         
     495    def transform_tag(self, tag): 
     496        """ Transform the tag into a proper python attribute name by 
     497        replacing all spaces and special characters (e.g '[', ']' into 
     498        underscores). 
     499         
     500        """ 
     501        toreplace = [" ", "-", "[", "]"] 
     502        for s in toreplace: 
     503            tag = tag.replace(s, "_") 
     504        return tag.lower() 
     505             
     506    def __getitem__(self, tag): 
     507        """ Return the tag values 
     508         
     509        Example :: 
     510         
     511            >>> idf["Investigation Title"] 
     512            'foo investigation' 
     513             
     514        """ 
     515        try: 
     516            return self._idf_dict[tag] 
     517        except KeyError: 
     518            pass 
     519         
     520        ttag = self.transform_tag(tag) 
     521        if hasattr(self, ttag): 
     522            return getattr(self, ttag) 
     523        else: 
     524            raise KeyError(tag) 
     525         
     526class SampleDataRelationship(object): 
     527    """ Sample-Data Relationship (contains the contents of the .sdrf file). 
     528     
     529    Example :: 
     530     
     531        >>> sdr = SampleDataRelationship("foobar.sdrf") 
     532        >>> sdr.source_name 
     533        ['foo', ... 
     534         
     535        >>> sdr.sample_name 
     536        ['sampled foo', ... 
     537         
     538        >>> sdr.extract_protocol_ref 
     539        ['bar the foo', ... 
     540         
     541        >>> sdr. 
     542        >>> sdr.derived_array_data_matrix_file 
     543        ['foobar.data.txt', ... 
     544         
     545        >>> 
     546          
     547    """ 
     548     
     549    # Nodes an edges 
     550    NODES_EDGES = ["Source Name", "Sample Name", "Extract Name", 
     551                   "Labeled Extract Name", "Hybridization Name", 
     552                   "Assay Name", "Scan Name", "Normalization Name", 
     553                   "Array Data File", "Derived Array Data File", 
     554                   "Array Data Matrix File", "Derived Array Data Matrix File", 
     555                   "Image File", "Protocol REF"] 
     556     
     557    # Attributes for nodes and edges 
     558    NODE_EDGE_ATTRIBUTES = \ 
     559    {"Source Name": ["Characteristics", "Provider", "Material Type", "Description", "Comment"], 
     560     "Sample Name": ["Characteristics", "Material Type", "Description", "Comment"], 
     561     "Extract Name":["Characteristics", "Material Type", "Description", "Comment"], 
     562     "Labeled Extract Name": ["Characteristics", "Material Type", "Description", "Label", "Comment"], 
     563     "Hybridization Name": ["Array Design File", "Array Design REF", "Comment"], 
     564     "Assay Name": ["Technology Type", "Comment"], 
     565     "Scan Name": ["Comment"], 
     566     "Normalization Name": ["Comment"], 
     567     "Array Data File": ["Comment"], 
     568     "Derived Array Data File": ["Comment"], 
     569     "Array Data Matrix File": ["Comment"], 
     570     "Derived Array Data Matrix File": ["Comment"], 
     571     "Image File": ["Comment"], 
     572     "Protocol REF": ["Term Source REF", "Parameter", "Performer", "Date", "Comment"] 
     573     } 
     574     
     575    # Attributes 
     576    ATTRIBUTE_COLUMNS = \ 
     577    {"Characteristics []": ["Unit", "Term Source REF"], 
     578     "Provider": ["Comment"], 
     579     "Material Type": ["Term Source REF"], 
     580     "Label": ["Term Source REF"], 
     581     "Array Design File": ["Comment"], 
     582     "Array Design REF": ["Term Source REF", "Comment"],     
     583     "Technology Type": ["Term Source REF"], 
     584     "Factor Value [] ()": ["Unit", "Term Source REF"], 
     585     "Performer": ["Comment"], 
     586     "Date": [], 
     587     "Parameter Value []": ["Unit", "Comment"], 
     588     "Unit []": ["Term Source REF"], 
     589     "Description": [], 
     590     "Term Source REF": ["Term Accession Number"], 
     591     "Term Accession Number": [], 
     592     "Comment []": [] 
     593     } 
     594    def __init__(self, sdrf_file=None): 
     595        header, rows = parse_sdrf(sdrf_file) 
     596        self.header = header 
     597        self.rows = rows  
     598         
     599    def transform_tag(self, tag): 
     600        """ Transform the tag into a proper python attribute name by 
     601        replacing all spaces and special characters (e.g '[', ']' into 
     602        underscores). 
     603         
     604        """ 
     605        toreplace = [" ", "-", "[", "]"] 
     606        for s in toreplace: 
     607            tag = tag.replace(s, "_") 
     608        return tag.lower() 
     609     
     610    def _subsection(self, name): 
     611        """ Return the named subsection (name must be from the 
     612        NODES_EDGES list). 
     613         
     614        """ 
     615        idx = self.NODES_EDGES.index(name) 
     616        start = self.header.index(name) 
     617        end = -1 
     618        for name in self.NODES_EDGES[idx + 1:]: 
     619            if name in self.header[start + 1:]: 
     620                end = self.header.index(name, start + 1) 
     621                break 
     622        return self.header[start:end], [r[start:end] for r in self.rows] 
     623     
     624    def _column(self, name): 
     625        """ Return the named column. 
     626        """ 
     627        index = self.header.index(name) 
     628        return [r[index] for r in self.rows] 
     629         
     630    def source(self): 
     631        """ Return the Source subsection 
     632        """ 
     633        return self._subsection("Source Name") 
     634         
     635    def source_name(self): 
     636        """ Return the Source Name subsection 
     637        """ 
     638        return self._column("Source Name") 
     639         
     640    def sample(self): 
     641        """ Return the Sample subsection 
     642        """ 
     643        return self._subsection("Sample Name") 
     644         
     645    def sample_name(self): 
     646        """ Return the Sample Name subsection 
     647        """ 
     648        return self._column("Sample Name") 
     649         
     650    def extract(self): 
     651        """ Return the Extract subsection 
     652        """ 
     653        return self._subsection("Extract Name") 
     654         
     655    def extract_name(self): 
     656        """ Return the Extract Name subsection 
     657        """ 
     658        return self._column("Extract Name") 
     659         
     660    def labeled_extract(self): 
     661        """ Return the Labeled Extract subsection 
     662        """ 
     663        return self._subsection("Labeled Extract Name") 
     664         
     665    def labeled_extract_name(self): 
     666        """ Return the Labeled Extract Name subsection 
     667        """ 
     668        return self._column("Labeled Extract Name") 
     669         
     670    def hybridization(self): 
     671        """ Return the Hibridization subsection. 
     672        """ 
     673        return self._subsection("Hibridization Name") 
     674         
     675    def hybridization_name(self): 
     676        """ Return the Hibridization Name subsection. 
     677        """ 
     678        return self._column("Hibridization Name") 
     679         
     680    def assay(self): 
     681        """ Return the Assay subsection 
     682        """ 
     683        return self._subsection("Assay Name") 
     684         
     685    def assay_name(self): 
     686        """ Return the Assay Name subsection 
     687        """ 
     688        return self._column("Assay Name") 
     689         
     690    def scan(self): 
     691        """ Return the Scan subsection 
     692        """ 
     693        return self._subsection("Scan Name") 
     694         
     695    def scan_name(self): 
     696        """ Return the Scan name subsection 
     697        """ 
     698        return self._column("Scan Name") 
     699         
     700    def normalization(self): 
     701        """ Return the Normalization subsection. 
     702        """ 
     703        return self._subsection("Normalization Name") 
     704         
     705    def normalization_name(self): 
     706        """ Return the Normalization Name subsection. 
     707        """ 
     708        return self._column("Normalization Name") 
     709          
     710    def array_data(self): 
     711        """ Return the Array Data subsection 
     712        """ 
     713        return self._subsection("Array Data File") 
     714     
     715    def array_data_file(self): 
     716        """ Return the Array Data File subsection 
     717        """ 
     718        return self._column("Array Data File") 
     719         
     720    def derived_array_data(self): 
     721        """ Return the Derived Array Data subsection 
     722        """ 
     723        return self._subsection("Derived Array Data File") 
     724     
     725    def derived_array_data_file(self): 
     726        """ Return the Derived Array Data File subsection 
     727        """ 
     728        return self._column("Derived Array Data File") 
     729         
     730    def array_data_matrix(self): 
     731        """ Return the Array Data Matrix subsection. 
     732        """ 
     733        return self._subsection("Array Data Matrix File") 
     734     
     735    def array_data_matrix_file(self): 
     736        """ Return the Array Data Matrix File subsection. 
     737        """ 
     738        return self._column("Array Data Matrix File") 
     739         
     740    def derived_array_data_matrix(self): 
     741        """ Return the Derived Array Data Matrix subsection. 
     742        """ 
     743        return self._subsection("Derived Array Data Matrix File") 
     744     
     745    def derived_array_data_matrix_file(self): 
     746        """ Return the Derived Array Data Matrix File subsection. 
     747        """ 
     748        return self._column("Derived Array Data Matrix File") 
     749         
     750    def image(self): 
     751        """ Return the Image subsection 
     752        """ 
     753        return self._subsection("Image File") 
     754     
     755    def image_file(self): 
     756        """ Return the Image File subsection. 
     757        """ 
     758        return self._column("Image File") 
     759         
     760class ArrayDesign(object): 
     761    """ Arary design (contains the contents of the .adf file). 
     762    """ 
     763    def __init__(self, adf_file=None): 
     764        adf = parse_adf(adf_file) 
     765        self._adf = adf 
     766     
     767def _is_float(str): 
     768    try: 
     769        float(str) 
     770        return True 
     771    except ValueError: 
     772        return False 
     773     
     774def _is_continuous(items, check_count=100): 
     775    """ Are the strings in items continous numbers.  
     776    """ 
     777    count = 0 
     778    i = 0 
     779    for i, item in enumerate(items): 
     780        if _is_float(item): 
     781            count += 1 
     782        if i >= check_count: 
     783            break 
     784    return count >= i * 0.5 
     785     
     786def mage_tab_to_orange(idf_filename): 
     787    """ Convert an MAGE-TAB annotated experiment into an Orange.data.Table 
     788    instance (assumes all the associated MAGE-TAB files are in the same 
     789    directory. 
     790     
     791    .. todo:: Add Characteristics, Factor Values ... to the feture.attributes dict 
     792     
     793    """ 
     794    import Orange 
     795    dirname = os.path.dirname(idf_filename) 
     796    idf = InvesigationDesign(idf_filename) 
     797     
     798    sdrf_filename = os.path.join(dirname, idf.sdrf_file[0]) 
     799    sdrf = SampleDataRelationship(sdrf_filename) 
     800     
     801    data_matrices = set(sdrf.derived_array_data_matrix_file()) 
     802    assert(len(data_matrices) == 1) # How to handle multiple array designs. 
     803    data_matrix = open(os.path.join(dirname, data_matrices.pop()), "rb") 
     804    header, quantification, rows, matrix = parse_data_matrix(data_matrix) 
     805    header_ref, header = header 
     806    row_ref, rows = rows 
     807     
     808    import numpy 
     809    matrix = numpy.array(matrix, dtype=str) 
     810     
     811     
     812    features = [] 
     813    for header_name, quant, column in zip(header, quantification, matrix.T): 
     814        if _is_continuous(column): 
     815            feature = Orange.data.variable.Continuous(header_name) 
     816        else: 
     817            values = set(column) 
     818            feature = Orange.data.variable.Discrete(header_name, values=sorted(values)) 
     819        feature.attributes["quantification"] = quant 
     820        features.append(feature) 
     821         
     822    row_ref_feature = Orange.data.variable.String(row_ref) 
     823    domain = Orange.data.Domain(features, None) 
     824    domain.addmeta(Orange.data.new_meta_id(), row_ref_feature) 
     825     
     826    matrix[numpy.where(matrix == "NA")] = "?" 
     827     
     828    table = Orange.data.Table(domain, [list(row) for row in matrix]) 
     829     
     830    for instance, row in zip(table, rows): 
     831        instance[row_ref_feature] = row 
     832     
     833    return table 
     834     
     835def _dictify(element): 
     836    """ Dictify and xml.etree.Element.Element instance. 
     837    """ 
     838    if element is None: 
     839        element = [] 
     840    dict = {} 
     841    strip = lambda s: s.strip() if s else s 
     842    for node in element: 
     843        dict[node.tag] = strip(getattr(node, "text", None)) 
     844    return dict 
     845     
     846     
     847class ArrayExpressExperiment(object): 
     848    """ An convinience class representing an Array Express Experiment. 
     849     
     850    Example :: 
     851     
     852        >>> ae = ArrayExpressExperiment("E-MEXP-2917") 
     853        >>> print ae.name 
     854        Characterization of Data Variation in Gene Expression Profiling of Human Peripheral Blood Samples 
     855         
     856        >>> for file in ae.files: 
     857        ...     print file["name"], file["url"] 
     858        E-MEXP-2917.biosamples.svg http://www.ebi.ac.uk/arrayexpress/files/E-MEXP-2917/E-MEXP-2917.biosamples.svg 
     859        ... 
     860             
     861    """ 
     862     
     863    def __init__(self, accession, connection=None): 
     864        self.accession = accession 
     865        self.connection = connection 
     866        self._etree = tree = query_experiments(accession=accession, connection=self.connection, format="xml") 
     867        experiments = tree.findall("experiment") 
     868        # find the exact match (more then one experiment can be listed in the query result) 
     869        experiments = [e for e in experiments if e.find("accession").text.strip() == accession] 
     870        self._experiment = experiment = experiments[0] #tree.find("experiment")  
     871         
     872        self.species = [e.text for e in experiment.findall("species")] 
     873        bool_values = {"true": True, "false": False} 
     874        self.rawdatafiles = bool_values[experiment.find("rawdatafiles").get("available","false")] 
     875        self.fgemdatafiles = bool_values[experiment.find("fgemdatafiles").get("available", "false")] 
     876         
     877        self.sampleattributes = [] 
     878        for sa in experiment.findall("sampleattribute"): 
     879            category = sa.find("category").text.strip() 
     880            values = [val.text for val in sa.findall("value")] 
     881            self.sampleattributes.append((category, values)) 
     882             
     883        self.experimentalfactors = [] 
     884        for ef in experiment.findall("experimentalfactor"): 
     885            name = ef.find("name").text.strip() 
     886            values = [val.text.strip() for val in ef.findall("values")] 
     887            self.experimentalfactors.append((name, values)) 
     888             
     889        self.miamescores = _dictify(experiment.find("miamescores")) 
     890             
     891        self.id = experiment.find("id").text 
     892        self.secondaryaccession = getattr(experiment.find("secondaryaccession"), "text", None) 
     893        self.name = experiment.find("name").text 
     894        self.experimenttype = experiment.find("experimenttype").text.strip() 
     895        self.releasedate = experiment.find("releasedate").text 
     896        self.lastupdatedate = getattr(experiment.find("lastupdatedate"), "text", None) 
     897        self.samples = int(experiment.find("samples").text) 
     898        self.assays = int(experiment.find("assays").text) 
     899         
     900        self.arraydesign = [_dictify(e) for e in experiment.findall("arraydesign")] 
     901             
     902        self.bioassaydatagroups = [_dictify(group) for group in experiment.findall("bioassaydatagroup")] 
     903        self.bibliography = [_dictify(e) for e in experiment.findall("bibliography")] 
     904#        assert(len(experiment.findall("bibliography")) < 2) 
     905        self.provider = [_dictify(e) for e in experiment.findall("provider")] 
     906#        assert(len(experiment.findall("provider")) < 2) 
     907         
     908        self.experimentdesign = [] 
     909        for expd in experiment.findall("experimentdesign"): 
     910            self.experimentdesign.append(expd.text) 
     911             
     912        self.description = [_dictify(e) for e in experiment.findall("description")] 
     913#        assert(len(experiment.findall("description")) < 2) 
     914         
     915        tree = query_files(accession=self.accession, format="xml", connection=self.connection) 
     916        experiments = tree.findall("experiment") 
     917        experiments = [e for e in experiments if e.find("accession").text.strip() == accession] 
     918        experiment = experiments[0] 
     919#        files = tree.findall("experiment/file") 
     920        files = experiment.findall("file") 
     921        self.files = [_dictify(file) for file in files] 
     922         
     923    def _download_processed(self): 
     924        """ Download the processed matrix file, and associated MAGE-TAB files (idf, sdrf, adf) 
     925        """ 
     926        assert(self.fgemdatafiles) 
     927        exp_files = [(f["kind"], f) for f in self.files if f.get("kind") in ["idf", "sdrf"] and f.get("extension") == "txt"] 
     928        exp_files += [(f["kind"], f) for f in self.files if f.get("kind") == "fgem"] 
     929        array_files = [(f["kind"], f) for f in self.files if f.get("kind") == "adf" and f.get("extension") == "txt"] 
     930        assert(len(files) == 3) 
     931         
     932        for type, file in files.iteritems(): 
     933            url = file["url"].strip() 
     934            rest, basename = os.path.split(url) 
     935            _, dirname = os.path.split(rest) 
     936             
     937            repo_dir = orngServerFiles.localpath("ArrayExpress", dirname) 
     938            try: 
     939                os.makedirs(repo_dir) 
     940            except OSError: 
     941                pass 
     942            local_filename = os.path.join(repo_dir, basename) 
     943            stream = urllib2.urlopen(url) 
     944            shutil.copyfileobj(stream, open(local_filename, "wb")) 
     945             
     946            if file["extension"] == "zip": 
     947                import zipfile 
     948                zfile = zlib.ZipFile(local_filename) 
     949                zfile.extractall(repo_dir) 
     950            elif file["extension"] == "gz": 
     951                import gzip 
     952                gzfile = gzip.open(local_filename) 
     953                gzfile.extractall(repo_dir) 
     954            elif file["extension"] in ["tgz", "tar.gz"]: 
     955                import tarfile 
     956                tfile = tarfile.TarFile(local_filename) 
     957                tfile.extractall(repo_dir) 
     958            elif file["extension"] == "txt": 
     959                pass 
     960            else: 
     961                raise ValueError("Unknown extension ('{0}').".format(basename)) 
     962             
     963    def _download_file(self, url, extract=True): 
     964        """ Download the `file` from the ArrayExpress saving it to a local 
     965        repository directory. 
     966          
     967        """ 
     968        rest, basename = posixpath.split(url) 
     969        dirname = posixpath.basename(rest) 
     970        repo_dir = orngServerFiles.localpath("ArrayExpress", dirname) 
     971        try: 
     972            os.makedirs(repo_dir) 
     973        except OSError: 
     974            pass 
     975        stream = urllib2.urlopen(url) 
     976        local_filename = os.path.join(repo_dir, basename) 
     977        shutil.copyfileobj(stream, open(local_filename, "wb")) 
     978         
     979        if extract: 
     980            _, extension = os.path.splitext(local_filename) 
     981            if extension == ".zip": 
     982                import zipfile 
     983                zfile = zipfile.ZipFile(local_filename) 
     984                zfile.extractall(repo_dir) 
     985            elif extension == ".gz": 
     986                import gzip 
     987                gzfile = gzip.open(local_filename) 
     988                gzfile.extractall(repo_dir) 
     989            elif extension in [".tgz"]: 
     990                import tarfile 
     991                tfile = tarfile.TarFile(local_filename) 
     992                tfile.extractall(repo_dir) 
     993            elif extension == ".txt": 
     994                pass 
     995            else: 
     996                raise ValueError("Unknown extension ('{0}').".format(basename)) 
     997             
     998    def _is_local(self, url): 
     999        """ Is the `url` stored in the local repository. 
     1000        """ 
     1001        return os.path.exists(self._local_filepath(url)) 
     1002     
     1003    def _local_filepath(self, url): 
     1004        """ Return the local file path for url. 
     1005        """ 
     1006        rest, basename = posixpath.split(url) 
     1007        dirname = posixpath.basename(rest) 
     1008        return orngServerFiles.localpath("ArrayExpress", os.path.join(dirname, basename)) 
     1009     
     1010    def _open(self, url): 
     1011        """ Return an open file like handle to url (ArrayExpress file). 
     1012        The file is cached in the local repository for future access. 
     1013         
     1014        """ 
     1015        if not self._is_local(url): 
     1016            self._download_file(url, extract=True) 
     1017        file = self._local_filepath(url) 
     1018        return open(file, "rb") 
     1019     
     1020    def _search_files(self, kind=None, extension=None): 
     1021        """ Search files by `kind` and `extension`. 
     1022        """ 
     1023        res = [] 
     1024        for file in self.files: 
     1025            kind_match = kind == file.get("kind") or kind is None 
     1026            extension_match = extension == file.get("extension") or extension is None 
     1027             
     1028            if kind_match and extension_match: 
     1029                res.append(file) 
     1030        return res 
     1031         
     1032    def array_design(self): 
     1033        """ Return a list of `ArrayDesign` instances used in this experiment. 
     1034        """ 
     1035        files = [f for f in self.files if f.get("kind") == "adf" and \ 
     1036                 f.get("extension") == "txt"] 
     1037         
     1038        array_design = [] 
     1039        for file in files: 
     1040            url = file.get("url") 
     1041            if not self._is_local(url): 
     1042                self._download_file(url) 
     1043            array_design.append(ArrayDesign(self._open(url))) 
     1044        return array_design 
     1045         
     1046    def investigation_design(self): 
     1047        """ Return an `InvestigationDesgin` instance for this experiment 
     1048        """ 
     1049        files = [f for f in self.files if f.get("kind") == "idf" and \ 
     1050                 f.get("extension") == "txt"] 
     1051        if not files: 
     1052            raise ValueError("The experiment '{0}' does not have an investigation design file".format(self.accession)) 
     1053        file = files[0] 
     1054        return InvesigationDesign(self._open(file.get("url"))) 
     1055         
     1056         
     1057    def sample_data_relationship(self): 
     1058        """ Return an `SampleDataRelationship` instance describing this experiment. 
     1059        """ 
     1060        files = [f for f in self.files if f.get("kind") == "sdrf" and \ 
     1061                 f.get("extension") == "txt"] 
     1062        if not files: 
     1063            raise ValueError("The experiment '{0}' does not have an sample and data relationship file".format(self.accession)) 
     1064        file = files[0] 
     1065        return SampleDataRelationship(self._open(file.get("url"))) 
     1066         
     1067    def fgem_to_table(self): 
     1068        assert(self.fgemdatafiles) 
     1069        repo_dir = orngServerFiles.localpath("ArrayExpress", self.accession) 
     1070        # Find the file listing the data matrix files (should be in sdrf but sometimes it is in 2column file only, why?) 
     1071        sdrf = self._search_files("sdrf", "txt") 
     1072        if sdrf: 
     1073            sdrf = SampleDataRelationship(self._open(sdrf[0].get("url"))) 
     1074            if "Derived Array Data Matrix File" not in sdrf.header: 
     1075                twocol = self._search_files("twocolumn", "txt") 
     1076                if twocol: 
     1077                    sdrf = SampleDataRelationship(self._open(twocol[0].get("url"))) 
     1078        matrix_file = self._search_files("fgem")[0] 
     1079        self._open(matrix_file.get("url"))  
     1080        matrix_files = sorted(set(sdrf.derived_array_data_matrix_file())) 
     1081         
     1082        return mage_tab_to_orange(os.path.join(repo_dir, self.accession + ".idf.txt")) 
     1083         
    3331084     
    3341085__doc__ += """\ 
Note: See TracChangeset for help on using the changeset viewer.