source: orange/Orange/data/utils.py @ 9936:08300d990d26

Revision 9936:08300d990d26, 8.8 KB checked in by markotoplak, 2 years ago (diff)

moved new_meta_ide from feature to feature.Descriptor

Line 
1"""\
2**************************
3Data Utilities (``utils``)
4**************************
5
6Common operations on :class:`Orange.data.Table`.
7
8"""
9#from __future__ import absolute_import
10
11import random
12import math
13from operator import itemgetter
14
15from collections import defaultdict
16
17from Orange.data import Table, Domain, Instance
18
19import feature as variable
20
21def table_map(table, attrs, exclude_special=True):
22    map = defaultdict(list)
23    for i, ex in enumerate(table):
24        key = [ex[a] for a in attrs]
25        if exclude_special and any(k.isSpecial() for k in key):
26            continue
27        key = tuple([str(k) for k in key])
28        map[key].append(i)
29    return map
30   
31def join_domains(domain1, domain2):
32    variables = domain1.variables + domain1.variables
33    used_set = set()
34    def used(vars):
35        mask = []
36        for var in vars:
37            mask.append(var not in used_set)
38            used_set.add(var)
39           
40    used_mask1 = used(domain1.variables)
41    used_mask2 = used(domain2.variables)
42    if domain2.classVar:
43        used_mask2[-1] = True
44       
45    variables = [v for v, used in zip(variables, used_mask1 + used_mask2)]
46   
47    joined_domain = Domain(variables, domain2.classVar)
48    joined_domain.add_metas(domain1.get_metas())
49    joined_domain.add_metas(domain2.get_metas())
50    return joined_domain, used_mask1, used_mask2
51   
52def left_join(table1, table2, on_attrs1, on_attrs2):
53    """ Left join table1 and table2 on attributes attr1 and attr2
54    """
55    if not isinstance(on_attrs1, (list, tuple)):
56        on_attrs1 = [on_attrs1]
57    if not isinstance(on_attrs2, (list, tuple)):
58        on_attrs2 = [on_attrs2]
59    key_map1 = table_map(table1, on_attrs1)
60    key_map2 = table_map(table2, on_attrs2)
61    domain1, domain2 = table1.domain, table2.domain
62   
63    left_examples = []
64    right_examples = []
65    for ex in table1:
66        key = tuple([str(ex[a]) for a in on_attrs1])
67        if key in key_map1 and key in key_map2:
68            for ind in key_map2[key]:
69                ex2 = table2[ind]
70                left_examples.append(ex)
71                right_examples.append(ex2)
72               
73    left_table = Table(left_examples)
74    right_table = Table(right_examples)
75    new_table = Table([left_table, right_table])
76    return new_table
77   
78def right_join(table1, table2, on_attrs1, on_attrs2):
79    """ Right join table1 and table2 on attributes attr1 and attr2
80    """
81    if not isinstance(on_attrs1, (list, tuple)):
82        on_attrs1 = [on_attrs1]
83    if not isinstance(on_attrs2, (list, tuple)):
84        on_attrs2 = [on_attrs2]
85    key_map1 = table_map(table1, on_attrs1)
86    key_map2 = table_map(table2, on_attrs2)
87    domain1, domain2 = table1.domain, table2.domain
88
89    left_examples = []
90    right_examples = []
91    for ex in table2:
92        key = tuple([str(ex[a]) for a in on_attrs2])
93        if key in key_map1 and key in key_map2:
94            for ind in key_map1[key]:
95                ex1 = table1[ind]
96                left_examples.append(ex1)
97                right_examples.append(ex)
98               
99    left_table = Table(left_examples)
100    right_table = Table(right_examples)
101    new_table = Table([left_table, right_table])
102    return new_table
103   
104def hstack(table1, table2):
105    """ Horizontally stack ``table1`` and ``table2``
106    """
107    return Table([table1, table2])
108
109def vstack(table1, table2):
110    """ Stack ``table1`` and ``table2`` vertically.
111    """
112    return Table(table1[:] + table2[:])
113
114def take(table, indices, axis=0):
115    """ Take values form the ``table`` along the ``axis``.
116    """
117    indices = mask_to_indices(indices, (len(table), len(table.domain)), axis)
118    if axis == 0:
119        # Take the rows (instances)
120        instances = [table[i] for i in indices]
121        table = Table(instances) if instances else Table(table.domain)
122    elif axis == 1:
123        # Take the columns (attributes)
124        variables = table.domain.variables
125        vars = [variables[i] for i in indices]
126        domain = Domain(vars, table.domain.class_var in vars)
127        domain.add_metas(table.domain.get_metas())
128        table = Table(domain, table)
129    return table
130
131def mask_to_indices(mask, shape, axis=0):
132    """ Convert a mask into indices.
133    """
134    import numpy
135    mask = numpy.asarray(mask)
136    dtype = mask.dtype
137    size = shape[axis]
138    if dtype.kind == "b":
139        if len(mask) != size:
140            raise ValueError("Mask size does not match the shape.")
141        indices = [i for i, m in zip(range(size), mask)]
142    elif dtype.kind == "i":
143        indices = mask
144    return indices
145
146
147from threading import Lock as _Lock
148_global_id = 0
149_global_id_lock = _Lock()
150 
151def range_generator():
152    global _global_id
153    while True:
154        with _global_id_lock:
155            id = int(_global_id)
156            _global_id += 1
157        yield id
158       
159def uuid_generator():
160    import uuid
161    while True:
162        yield str(uuid.uuid4())
163
164import Orange.feature
165new_meta_id = Orange.feature.Descriptor.new_meta_id
166
167_row_meta_id = new_meta_id()
168_id_variable = variable.String("Row Id")
169
170def add_row_id(table, start=0):
171    """ Add an Row Id meta variable to the table.
172   
173    Parameters
174    ==========
175    :param table: The ids will be added to this table.
176    :type table: Orange.data.Table
177       
178    :param start: Start id for the ids. It can also be an iterator
179        yielding unique values.
180    :type start: int
181       
182       
183    """
184    if _row_meta_id not in table.domain.get_metas():
185        table.domain.add_meta(_row_meta_id, _id_variable)
186       
187    if isinstance(start, int):
188        ids = iter(range(start, start + len(table)))
189    else:
190        ids = start
191               
192    for ex in table:
193        ex[_id_variable] = str(ids.next())
194
195   
196from Orange.statistics import distribution
197
198def modus(values):
199    dist = distribution.Distribution(values[0].variable)
200    for v in values:
201        dist.add(v)
202    return dist.modus()
203
204def mean(values):
205    dist = distribution.Distribution(values[0].variable)
206    for v in values:
207        dist.add(v)
208    return dist.average()
209
210def geometric_mean(values):
211    values = [float(v) for v in values if not v.is_special()]
212    if values:
213        prod = reduce(float.__mul__, values, 1.0)
214        if prod >= 0:
215            return math.pow(prod, 1.0/len(values))
216        else:
217            return "?"
218    else:
219        return "?"
220   
221def harmonic_mean(values):
222    values = [float(v) for v in values if not v.is_special()]
223    if values:
224        hsum = sum(map(lambda v: 1.0 / (v or 1e-6), values))
225        return len(values) / (hsum or 1e-6) 
226    else:
227        return "?"
228
229_aggregate_mapping = {"random": random.choice,
230                      "first": itemgetter(0),
231                      "last": itemgetter(-1),
232                      "modus": modus,
233                      "mean": mean,
234                      "geometric mean": geometric_mean,
235                      "harmonic mean": harmonic_mean,
236                      "join": lambda values: ", ".join(map(str, values))
237                      }
238
239def _aggregate_func(func):
240    if isinstance(func, basestring):
241        if func in _aggregate_mapping:
242            return _aggregate_mapping[func]
243        else:
244            raise ValueError("Unknown aggregate function %r." % func)
245    return func
246       
247def group_by(table, group_attrs, aggregate_disc="first", aggregate_cont="mean",
248             aggregate_string="join", attr_aggregate=None):
249    if attr_aggregate is None:
250        attr_aggregate = {}
251    else:
252        attr_aggregate = dict(attr_aggregate) # It is modified later
253       
254    all_vars = table.domain.variables + table.domain.getmetas().values()
255    aggregate_vars = []
256    for v in all_vars:
257        if v not in group_attrs:
258            if v in attr_aggregate:
259                pass
260            elif isinstance(v, variable.Continuous):
261                attr_aggregate[v] = aggregate_cont
262            elif isinstance(v, variable.Discrete):
263                attr_aggregate[v] = aggregate_disc
264            elif isinstance(v, variable.String):
265                attr_aggregate[v] = aggregate_string
266            else:
267                raise TypeError(v)
268            aggregate_vars.append(v)
269            attr_aggregate[v] = _aggregate_func(attr_aggregate[v])
270           
271    indices_map = table_map(table, group_attrs, exclude_special=False)
272    new_instances = []
273    key_set = set()
274   
275    for inst in table: # Iterate over the table instead of the inidces_map to preserve order
276        key = tuple([str(inst[v]) for v in group_attrs])
277        if key in key_set:
278            continue # Already seen this group
279        indices = indices_map[key]
280        new_instance = Instance(inst) # Copy
281        for v in aggregate_vars:
282            values = [table[i][v] for i in indices] # Values to aggregate
283            new_instance[v] = attr_aggregate[v](values)
284        new_instances.append(new_instance)
285        key_set.add(key)
286    return Table(new_instances)
Note: See TracBrowser for help on using the repository browser.