Skip to content

Commit

Permalink
Merge tag '3.31.1' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
markotoplak committed Jan 7, 2022
2 parents 9de6c69 + dd3ea29 commit e889f28
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 18 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ Change Log
[next] - TBA
------------


[3.31.1] - 2022-01-07
--------------------
##### Bugfixes
* Group by: compute mode when all values in group nan ([#5763](../../pull/5763))
* Support numpy 1.22 ([#5760](../../pull/5760))
* Unpickling pre-3.28.0 Transformation ([#5759](../../pull/5759))


[3.31.0] - 2021-12-17
--------------------
##### Enhancements
Expand Down Expand Up @@ -1630,7 +1639,8 @@ Change Log
* Initial version based on Python 1.5.2 and Qt 2.3


[next]: https://github.com/biolab/orange3/compare/3.31.0...HEAD
[next]: https://github.com/biolab/orange3/compare/3.31.1...HEAD
[3.31.1]: https://github.com/biolab/orange3/compare/3.31.0...3.31.1
[3.31.0]: https://github.com/biolab/orange3/compare/3.30.2...3.31.0
[3.30.2]: https://github.com/biolab/orange3/compare/3.30.1...3.30.2
[3.30.1]: https://github.com/biolab/orange3/compare/3.30.0...3.30.1
Expand Down
4 changes: 2 additions & 2 deletions Orange/data/io_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def isnastr(arr, out=None):
arr = np.asarray(arr)
if out is None and arr.shape != ():
out = np.empty_like(arr, dtype=bool)
return __isnastr(arr, out=out)
return __isnastr(arr, out=out, casting="unsafe")


def guess_data_type(orig_values, namask=None):
Expand Down Expand Up @@ -182,7 +182,7 @@ def get_number_of_decimals(values):

def mapvalues(arr):
arr = np.asarray(arr, dtype=object)
return mapvalues_(arr, out=np.empty_like(arr, dtype=float))
return mapvalues_(arr, out=np.empty_like(arr, dtype=float), casting="unsafe")

values = mapvalues(orig_values)

Expand Down
4 changes: 0 additions & 4 deletions Orange/data/tests/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,7 @@ def test_locking_flag(self):
def test_unpickled_empty_weights(self):
# ensure that unpickled empty arrays could be unlocked
self.assertEqual(0, self.table.W.size)
# flags.owndata asserts touch numpy internals
# feel free to remove when they do not pass
self.assertTrue(self.table.W.flags.owndata)
unpickled = pickle.loads(pickle.dumps(self.table))
self.assertFalse(unpickled.W.flags.owndata)
with unpickled.unlocked():
pass

Expand Down
22 changes: 19 additions & 3 deletions Orange/preprocess/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,28 @@ def __init__(self, variable):
:type variable: int or str or :obj:`~Orange.data.Variable`
"""
self.variable = variable
self._create_cached_target_domain()

def _create_cached_target_domain(self):
""" If the same domain is used everytime this allows better caching of
domain transformations in from_table"""
if self.variable is not None:
if self.variable.is_primitive():
self.need_domain = Domain([self.variable])
self._target_domain = Domain([self.variable])
else:
self.need_domain = Domain([], metas=[self.variable])
self._target_domain = Domain([], metas=[self.variable])

def __getstate__(self):
# Do not pickle the cached domain; rather recreate it after unpickling
state = self.__dict__.copy()
state.pop("_target_domain")
return state

def __setstate__(self, state):
# Ensure that cached target domain is created after unpickling.
# This solves the problem of unpickling old pickled models.
self.__dict__.update(state)
self._create_cached_target_domain()

def __call__(self, data):
"""
Expand All @@ -31,7 +47,7 @@ def __call__(self, data):
inst = isinstance(data, Instance)
if inst:
data = Table.from_list(data.domain, [data])
data = data.transform(self.need_domain)
data = data.transform(self._target_domain)
if self.variable.is_primitive():
col = data.X
else:
Expand Down
12 changes: 12 additions & 0 deletions Orange/tests/test_transformation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pickle
import unittest

import numpy as np
Expand Down Expand Up @@ -43,6 +44,17 @@ def test_transform_fails(self):
trans = Transformation(self.data.domain[2])
self.assertRaises(NotImplementedError, trans, self.data)

def test_pickling_target_domain(self):
data = self.data
trans = self.TransformationMock(data.domain[2])
self.assertIn("_target_domain", trans.__dict__)
# _target_domain should not be pickled
state = trans.__getstate__()
self.assertNotIn("_target_domain", state)
# _target_domain should be recreated when unpickled
unpickled = pickle.loads(pickle.dumps(trans))
self.assertIn("_target_domain", unpickled.__dict__)


class IdentityTest(unittest.TestCase):
def test_identity(self):
Expand Down
4 changes: 2 additions & 2 deletions Orange/widgets/data/oweditdomain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2645,7 +2645,7 @@ def mapper(arr, out=None, dtype=dtype, **kwargs):
arr = np.asanyarray(arr)
if out is None and dtype is not None and arr.shape != ():
out = np.empty_like(arr, dtype)
return _vmapper(arr, out, dtype=dtype, **kwargs)
return _vmapper(arr, out, dtype=dtype, casting="unsafe", **kwargs)
return mapper


Expand Down Expand Up @@ -2684,7 +2684,7 @@ def as_float_or_nan(
np.issubdtype(arr.dtype, np.integer):
np.copyto(out, arr, casting="unsafe", where=where)
return out
return _parse_float(arr, out, where=where, **kwargs)
return _parse_float(arr, out, where=where, casting="unsafe", **kwargs)


def copy_attributes(dst: V, src: Orange.data.Variable) -> V:
Expand Down
10 changes: 6 additions & 4 deletions Orange/widgets/data/owgroupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Any, Dict, List, Optional, Set

import pandas as pd
from numpy import nan
from AnyQt.QtCore import (
QAbstractTableModel,
QEvent,
Expand Down Expand Up @@ -58,7 +59,7 @@ def concatenate(x):
"Mean": Aggregation("mean", {ContinuousVariable, TimeVariable}),
"Median": Aggregation("median", {ContinuousVariable, TimeVariable}),
"Mode": Aggregation(
lambda x: pd.Series.mode(x)[0], {ContinuousVariable, TimeVariable}
lambda x: pd.Series.mode(x).get(0, nan), {ContinuousVariable, TimeVariable}
),
"Standard deviation": Aggregation("std", {ContinuousVariable, TimeVariable}),
"Variance": Aggregation("var", {ContinuousVariable, TimeVariable}),
Expand Down Expand Up @@ -404,7 +405,7 @@ def __gb_changed(self) -> None:
self.gb_attrs = [values[row.row()] for row in sorted(rows)]
# everything cached in result should be recomputed on gb change
self.result = Result()
self.commit()
self.commit.deferred()

def __aggregation_changed(self, agg: str) -> None:
"""
Expand All @@ -420,7 +421,7 @@ def __aggregation_changed(self, agg: str) -> None:
else:
self.aggregations[attr].discard(agg)
self.agg_table_model.update_aggregation(attr)
self.commit()
self.commit.deferred()

@Inputs.data
def set_data(self, data: Table) -> None:
Expand Down Expand Up @@ -448,11 +449,12 @@ def set_data(self, data: Table) -> None:
self.agg_table_model.set_domain(data.domain if data else None)
self._set_gb_selection()

self.commit()
self.commit.now()

#########################
# Task connected methods

@gui.deferred
def commit(self) -> None:
self.Error.clear()
self.Warning.clear()
Expand Down
61 changes: 61 additions & 0 deletions Orange/widgets/data/tests/test_owgroupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from Orange.data import (
Table,
table_to_frame,
Domain,
ContinuousVariable,
)
from Orange.data.tests.test_aggregate import create_sample_data
from Orange.widgets.data.owgroupby import OWGroupBy
Expand Down Expand Up @@ -689,6 +691,65 @@ def test_time_variable(self):
output = self.get_output(self.widget.Outputs.data)
self.assertEqual(2, len(output))

def test_only_nan_in_group(self):
data = Table(
Domain([ContinuousVariable("A"), ContinuousVariable("B")]),
np.array([[1, np.nan], [2, 1], [1, np.nan], [2, 1]]),
)
self.send_signal(self.widget.Inputs.data, data)

# select feature A as group-by
self._set_selection(self.widget.gb_attrs_view, [0])
# select all aggregations for feature B
self.select_table_rows(self.widget.agg_table_view, [1])
for cb in self.widget.agg_checkboxes.values():
while not cb.isChecked():
cb.click()

# unselect all aggregations for attr A
self.select_table_rows(self.widget.agg_table_view, [0])
for cb in self.widget.agg_checkboxes.values():
while cb.isChecked():
cb.click()

expected_columns = [
"B - Mean",
"B - Median",
"B - Mode",
"B - Standard deviation",
"B - Variance",
"B - Sum",
"B - Min. value",
"B - Max. value",
"B - Span",
"B - First value",
"B - Last value",
"B - Random value",
"B - Count defined",
"B - Count",
"B - Proportion defined",
"B - Concatenate",
"A",
]
n = np.nan
expected_df = pd.DataFrame(
[
[n, n, n, n, n, 0, n, n, n, n, n, n, 0, 2, 0, "", 1],
[1, 1, 1, 0, 0, 2, 1, 1, 0, 1, 1, 1, 2, 2, 1, "1.0 1.0", 2],
],
columns=expected_columns,
)
output_df = table_to_frame(
self.get_output(self.widget.Outputs.data), include_metas=True
)
pd.testing.assert_frame_equal(
output_df,
expected_df,
check_dtype=False,
check_column_type=False,
check_categorical=False,
)


if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions doc/visual-programming/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Data
widgets/data/melt
widgets/data/neighbors
widgets/data/unique
widgets/data/groupby


Visualize
Expand Down
2 changes: 1 addition & 1 deletion doc/visual-programming/source/widgets/data/groupby.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ In the **Data Table** widget, we can see that both females and males have lower
You can also observe differences between median **cholesterol** level and mean value of **major vessel colored** between groups.


![](images/Group-by-Example.png)
![](images/Group-by-example.png)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

NAME = 'Orange3'

VERSION = '3.31.0'
VERSION = '3.31.1'
ISRELEASED = True
# full version identifier including a git revision identifier for development
# build/releases (this is filled/updated in `write_version_py`)
Expand Down

0 comments on commit e889f28

Please sign in to comment.