source: orange/Orange/data/utils.py @ 10143:596ab92746fe

Revision 10143:596ab92746fe, 8.8 KB checked in by ales_erjavec, 2 years ago (diff)

Absolute import of Orange.feature

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