Skip to content

Commit

Permalink
Merge tag '3.36.0' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
PrimozGodec committed Sep 8, 2023
2 parents 41f650d + c2c0f50 commit 07eadf7
Show file tree
Hide file tree
Showing 109 changed files with 2,779 additions and 952 deletions.
26 changes: 15 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: 3.9

- name: Install Tox
run: |
Expand All @@ -36,21 +36,21 @@ jobs:
strategy:
fail-fast: False
matrix:
os: [ubuntu-20.04]
python-version: [3.8, 3.9, '3.10']
os: [ubuntu-22.04]
python-version: [3.9, '3.10', '3.11']
tox_env: [orange-released]
name: [Released]
include:
- os: ubuntu-20.04
python-version: '3.10'
- os: ubuntu-22.04
python-version: '3.11'
tox_env: orange-latest
name: Latest
- os: ubuntu-20.04
python-version: 3.8
python-version: 3.9
tox_env: orange-oldest
name: Oldest dependencies
- os: ubuntu-20.04
python-version: 3.9
- os: ubuntu-22.04
python-version: '3.10'
tox_env: pyqt6
name: PyQt6

Expand Down Expand Up @@ -85,6 +85,10 @@ jobs:
sudo apt-get update
sudo apt-get install -y libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 libegl1-mesa libxcb-shape0 libxcb-cursor0
- name: Install glibc-tools dependency on Ubuntu-22.04
if: matrix.os == 'ubuntu-22.04'
run: sudo apt-get install -y glibc-tools # required for catchsegv

- name: Install Tox
run: |
python -m pip install --upgrade pip
Expand Down Expand Up @@ -116,16 +120,16 @@ jobs:
fail-fast: false
matrix:
os: [macos-latest, windows-latest]
python-version: [3.8, 3.9, '3.10']
python-version: [3.9, '3.10', '3.11']
tox_env: [orange-released]
name: [Released]
include:
- os: windows-latest
python-version: '3.10'
python-version: '3.11'
tox_env: orange-latest
name: Latest
- os: macos-latest
python-version: '3.10'
python-version: '3.11'
tox_env: orange-latest
name: Latest
- os: windows-latest
Expand Down
42 changes: 41 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,45 @@ Change Log
[next] - TBA
------------

[3.36.0] - 2023-09-08
--------------------
##### Enhancements
* Update widget.json
* feature-constructor2-stamped.png - resize and index
* Formula documentation: Minor changes
* index.rst - fix reference to formula
* move accidentally displaced formula.md
* SOM: output columns with coordinates and errors ([#6542](../../pull/6542))
* Lazy signals for Hierarchical Clustering ([#6348](../../pull/6348))
* PCA widget runs a separate thread ([#6528](../../pull/6528))
* Edit Domain: Change types of multiple variables ([#6426](../../pull/6426))
* owsavebase: Display a warning when auto save is disabled ([#6454](../../pull/6454))
* Select Columns: Accept partially correct drops ([#6390](../../pull/6390))

##### Bugfixes
* Update widget.json
* feature-constructor2-stamped.png - resize and index
* Formula documentation: Minor changes
* index.rst - fix reference to formula
* move accidentally displaced formula.md
* Predictions: Fix column size hinting ([#6563](../../pull/6563))
* Naive Bayes: fix predictions with unknown values ([#6564](../../pull/6564))
* Scatter Plot - Handle input features that are hidden in data ([#6531](../../pull/6531))
* Adapt tests to pandas 2.1 and fix deprecations ([#6560](../../pull/6560))
* Adapt to newly released pandas 2.1 ([#6559](../../pull/6559))
* CSV Import - Change datetime format parsing ([#6539](../../pull/6539))
* owdiscretize: Fix formatting display string when user role is undefined ([#6498](../../pull/6498))
* Neural Network: Default learner name propagation ([#6526](../../pull/6526))
* PCA - Output instance of table subclass when instance of table subclass on input ([#6536](../../pull/6536))
* owpredictions: Fix a type error in report when using NoopDelegate ([#6537](../../pull/6537))
* Scatter Plot - Fix Vizrank for hidden attributes ([#6530](../../pull/6530))
* config: Replace and fix use of pkg_resources.parse_version ([#6523](../../pull/6523))
* Fix classification trees for data with repeated feature values ([#6488](../../pull/6488))
* Group By - Fix error with hidden attributes ([#6473](../../pull/6473))
* Sql - Fix comparison with values trailing spaces ([#6470](../../pull/6470))
* ListView - empty variable when all values unselected ([#6441](../../pull/6441))


[3.35.0] - 2023-05-05
---------------------
##### Enhancements
Expand Down Expand Up @@ -1784,7 +1823,8 @@ Change Log


[next]: https://github.com/biolab/orange3/compare/3.35.0..HEAD
[3.34.1]: https://github.com/biolab/orange3/compare/3.34.1...3.35.0
[3.36.0]: https://github.com/biolab/orange3/compare/3.35.0...3.36.0
[3.35.0]: https://github.com/biolab/orange3/compare/3.34.1...3.35.0
[3.34.1]: https://github.com/biolab/orange3/compare/3.34.0...3.34.1
[3.34.0]: https://github.com/biolab/orange3/compare/3.33.0...3.34.0
[3.33.0]: https://github.com/biolab/orange3/compare/3.32.0...3.33.0
Expand Down
8 changes: 3 additions & 5 deletions Orange/canvas/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
import random
import uuid
import warnings

import os
import sys
import itertools
from distutils.version import LooseVersion

from typing import Dict, Any, Optional, Iterable, List

import packaging.version
import pkg_resources
import requests

Expand Down Expand Up @@ -112,8 +110,8 @@ def splash_screen():

version = Config.ApplicationVersion
if version:
version_parsed = LooseVersion(version)
version_comp = version_parsed.version
version_parsed = packaging.version.Version(version)
version_comp = version_parsed.release
version = ".".join(map(str, version_comp[:2]))
size = 13
font = QFont("Helvetica")
Expand Down
15 changes: 13 additions & 2 deletions Orange/canvas/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
# Imported to make webwidget addons work
from Orange.widgets.utils.webview import WebviewWidget # pylint: disable=unused-import

LOG_LEVELS = [
logging.CRITICAL + 10,
logging.CRITICAL,
logging.ERROR,
logging.WARN,
logging.INFO,
logging.DEBUG
]

def main(argv=None):
app = QApplication(list(argv) if argv else [])
Expand All @@ -28,13 +36,13 @@ def main(argv=None):
)
)
parser.add_argument("--log-level", "-l", metavar="LEVEL", type=int,
default=logging.CRITICAL, dest="log_level")
default=3, dest="log_level")
parser.add_argument("--config", default="Orange.canvas.config.Config",
type=str)
parser.add_argument("file")
args = parser.parse_args(argv[1:])

log_level = args.log_level
log_level = LOG_LEVELS[args.log_level]
filename = args.file
logging.basicConfig(level=log_level)

Expand Down Expand Up @@ -71,6 +79,9 @@ def on_finished():
if msg.contents and msg.severity == msg.Error:
print(msg.contents, msg.message_id, file=sys.stderr)
severity = msg.Error
elif msg.contents and msg.severity == msg.Warning and \
log_level <= logging.WARNING:
print(msg.contents, file=sys.stderr)
if severity == UserMessage.Error:
app.exit(1)
else:
Expand Down
2 changes: 1 addition & 1 deletion Orange/classification/_tree_scorers.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def find_threshold_entropy(const double[:] x, const double[:] y,
curr_y = <int>y[idx[i]]
distr[curr_y] -= 1
distr[n_classes + curr_y] += 1
if curr_y != y[idx[i + 1]] and x[idx[i]] != x[idx[i + 1]]:
if x[idx[i]] != x[idx[i + 1]]:
entro = (i + 1) * log(i + 1) + (N - i - 1) * log(N - i - 1)
for j in range(2 * n_classes):
if distr[j]:
Expand Down
3 changes: 2 additions & 1 deletion Orange/classification/naive_bayes.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def _dense_probs(self, data, probs):
zeros = np.zeros((1, probs.shape[1]))
for col, attr_prob in zip(data.T, self.log_cont_prob):
col = col.copy()
col[np.isnan(col)] = attr_prob.shape[1] - 1
col[np.isnan(col)] = attr_prob.shape[1]
col = col.astype(int)
probs0 = np.vstack((attr_prob.T, zeros))
probs += probs0[col]
Expand All @@ -113,6 +113,7 @@ def _sparse_probs(self, data, probs):
p0 = p.T[0].copy()
probs[:] += p0
log_prob[i, :p.shape[1]] = p.T - p0
log_prob[i, n_vals-1] = -p0

dat = data.data.copy()
dat[np.isnan(dat)] = n_vals - 1
Expand Down
16 changes: 12 additions & 4 deletions Orange/data/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,18 +419,18 @@ class FilterString(ValueFilter):
The operator; should be `FilterString.Equal`, `NotEqual`, `Less`,
`LessEqual`, `Greater`, `GreaterEqual`, `Between`, `Outside`,
`Contains`, `StartsWith`, `EndsWith` or `IsDefined`.
`Contains`, `NotContain`, `StartsWith`, `NotStartsWith`, `EndsWith`, `NotEndsWith`, `IsDefined` or `NotIsDefined`.
.. attribute:: case_sensitive
Tells whether the comparisons are case sensitive
"""
Type = Enum('FilterString',
'Equal, NotEqual, Less, LessEqual, Greater,'
'GreaterEqual, Between, Outside, Contains,'
'StartsWith, EndsWith, IsDefined')
'GreaterEqual, Between, Outside, Contains, NotContain,'
'StartsWith, NotStartsWith, EndsWith, NotEndsWith, IsDefined, NotIsDefined')
(Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual,
Between, Outside, Contains, StartsWith, EndsWith, IsDefined) = Type
Between, Outside, Contains, NotContain, StartsWith, NotStartsWith, EndsWith, NotEndsWith, IsDefined, NotIsDefined) = Type

def __init__(self, position, oper, ref=None, max=None,
case_sensitive=True, **a):
Expand Down Expand Up @@ -460,6 +460,8 @@ def __call__(self, inst):
value = inst[inst.domain.index(self.column)]
if self.oper == self.IsDefined:
return not np.isnan(value)
if self.oper == self.NotIsDefined:
return np.isnan(value)
if self.case_sensitive:
value = str(value)
refval = str(self.ref)
Expand All @@ -480,10 +482,16 @@ def __call__(self, inst):
return value >= refval
if self.oper == self.Contains:
return refval in value
if self.oper == self.NotContain:
return refval not in value
if self.oper == self.StartsWith:
return value.startswith(refval)
if self.oper == self.NotStartsWith:
return not value.startswith(refval)
if self.oper == self.EndsWith:
return value.endswith(refval)
if self.oper == self.NotEndsWith:
return not value.endswith(refval)
high = self.max if self.case_sensitive else self.max.lower()
if self.oper == self.Between:
return refval <= value <= high
Expand Down
2 changes: 1 addition & 1 deletion Orange/data/io_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ def formatter(cls, var):
if var.is_time:
return var.repr_val
elif var.is_continuous:
return lambda value: "" if isnan(value) else value
return lambda value: "" if isnan(value) else var.repr_val(value)
elif var.is_discrete:
return lambda value: "" if isnan(value) else var.values[int(value)]
elif var.is_string:
Expand Down
17 changes: 10 additions & 7 deletions Orange/data/pandas_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
from scipy.sparse import csr_matrix
import pandas as pd
from pandas.core.arrays import SparseArray
from pandas.core.arrays.sparse.dtype import SparseDtype
from pandas.api.types import (
is_categorical_dtype, is_object_dtype,
is_datetime64_any_dtype, is_numeric_dtype, is_integer_dtype
is_object_dtype,
is_datetime64_any_dtype,
is_numeric_dtype,
is_integer_dtype,
)

from Orange.data import (
Expand Down Expand Up @@ -166,9 +167,11 @@ def _reset_index(df: pd.DataFrame) -> pd.DataFrame:


def _is_discrete(s, force_nominal):
return (is_categorical_dtype(s) or
is_object_dtype(s) and (force_nominal or
s.nunique() < s.size ** .666))
return (
isinstance(s.dtype, pd.CategoricalDtype)
or is_object_dtype(s)
and (force_nominal or s.nunique() < s.size**0.666)
)


def _is_datetime(s):
Expand Down Expand Up @@ -297,7 +300,7 @@ def vars_from_df(df, role=None, force_nominal=False):
elif not any(a_expr):
# if all c in columns table will share memory with dataframe
a_df = df if all(c in a_cols for c in df.columns) else df[a_cols]
if all(isinstance(a, SparseDtype) for a in a_df.dtypes):
if all(isinstance(a, pd.SparseDtype) for a in a_df.dtypes):
arr = csr_matrix(a_df.sparse.to_coo())
else:
arr = np.asarray(a_df)
Expand Down
10 changes: 6 additions & 4 deletions Orange/data/sql/backend/mssql.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,12 @@ def distinct_values_query(self, field_name: str, table_name: str) -> str:
return self.create_sql_query(
table_name,
[field],
# workaround for collations that are not case sensitive and
# UTF characters sensitive - in the domain we still want to
# have all values (collation independent)
group_by=[f"{field}, Cast({field} as binary)"],
# Cast - workaround for collations that are not case-sensitive and
# UTF characters sensitive
# DATALENGTH - workaround for string comparison that ignore trailing
# spaces, two strings that differ only in space in the end would
# group together if DATALENGTH wouldn't be used
group_by=[f"{field}, Cast({field} as binary), DATALENGTH({field})"],
order_by=[field],
limit=21,
)
24 changes: 24 additions & 0 deletions Orange/data/sql/table.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Support for example tables wrapping data stored on a PostgreSQL server.
"""
import contextlib
import functools
import logging
import threading
Expand Down Expand Up @@ -669,3 +670,26 @@ def get_nan_frequency_attribute(self):

def get_nan_frequency_class(self):
return self.__get_nan_frequency(self.domain.class_vars)

def __getstate__(self):
# avoids locking magic in Table.__getstate__
return self.__dict__

def __setstate__(self, state):
# avoid locking magic in Table.__setstate__
self.__dict__.update(state)

# if X is defined then it was already downloaded
# thus ids exist to, rewrite them
if self._X is not None:
self._init_ids(self)

# pylint: disable=unused-argument
def _update_locks(self, *args, **kwargs):
# avoid locking inherited from Table
return

# pylint: disable=unused-argument
def unlocked(self, *parts):
# avoid locking inherited from Table
return contextlib.nullcontext()

0 comments on commit 07eadf7

Please sign in to comment.