source: orange/Orange/data/utils.py @ 9929:6df3696524a2

Revision 9929:6df3696524a2, 8.7 KB checked in by markotoplak, 2 years ago (diff)

data.variable -> feature.

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
164from Orange.feature import 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.