From 148a589124d99779624eb4252f927bd07b4c621e Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 2 Jul 2020 13:54:19 +0300 Subject: [PATCH 01/41] BUG: fix index detection in _sanitize_and_check --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 4c5a70f4088ee..3e3f3d31f7146 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -262,7 +262,7 @@ def _sanitize_and_check(indexes): else: return indexes, "list" - if len(kinds) > 1 or Index not in kinds: + if len(kinds) > 1 or all([not isinstance(index, Index) for index in indexes]): return indexes, "special" else: return indexes, "array" From 8a51dede8ee0cbe022e18c2e57d0b0691f501ce2 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 2 Jul 2020 18:53:14 +0300 Subject: [PATCH 02/41] DOC: add comment to fix --- pandas/core/indexes/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 3e3f3d31f7146..aadc3b095da25 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -262,6 +262,7 @@ def _sanitize_and_check(indexes): else: return indexes, "list" + # GH 35092. Check for Index subclass to avoid setting special type by error if len(kinds) > 1 or all([not isinstance(index, Index) for index in indexes]): return indexes, "special" else: From 371fd6eff45cfbc007b081f939e4f0914b92ce77 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 2 Jul 2020 19:45:01 +0300 Subject: [PATCH 03/41] TST: add tests --- pandas/tests/indexes/test_common.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py index 02a173eb4958d..6b14b25815297 100644 --- a/pandas/tests/indexes/test_common.py +++ b/pandas/tests/indexes/test_common.py @@ -11,9 +11,10 @@ from pandas._libs.tslibs import iNaT from pandas.core.dtypes.common import is_period_dtype, needs_i8_conversion +from pandas.core.indexes.api import union_indexes import pandas as pd -from pandas import CategoricalIndex, MultiIndex, RangeIndex +from pandas import CategoricalIndex, Index, MultiIndex, RangeIndex import pandas._testing as tm @@ -395,3 +396,15 @@ def test_astype_preserves_name(self, index, dtype, copy): assert result.names == index.names else: assert result.name == index.name + + +@pytest.mark.parametrize("dtype", ["int8", "int16", "int32", "int64"]) +def test_union_index_no_sort(dtype): + # GH 35092. Check that we don't sort with sort=False + ind1 = Index([0, 1], dtype=dtype) + ind2 = Index([4, 3], dtype=dtype) + + expected = Index([0, 1, 4, 3], dtype=dtype) + result = union_indexes([ind1, ind2], sort=False) + + tm.assert_index_equal(result, expected) From e5f1e686dc3439f61c4e935855f8ebf666ecb019 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 2 Jul 2020 20:32:43 +0300 Subject: [PATCH 04/41] DOC: add whatsnew entry --- doc/source/whatsnew/v1.1.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 8c6add92e658b..54e0ba306cc58 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -974,6 +974,7 @@ Indexing - Bug in :meth:`DataFrame.loc` with dictionary of values changes columns with dtype of ``int`` to ``float`` (:issue:`34573`) - Bug in :meth:`Series.loc` when used with a :class:`MultiIndex` would raise an IndexingError when accessing a None value (:issue:`34318`) - Bug in :meth:`DataFrame.reset_index` and :meth:`Series.reset_index` would not preserve data types on an empty :class:`DataFrame` or :class:`Series` with a :class:`MultiIndex` (:issue:`19602`) +- Bug in :func:``pandas.core.indexes.api.union_indexes`` would lead to :meth:`DataFrame.append` sorting columns even when ``sort=False`` is specified (:issue`35092`) Missing ^^^^^^^ From 9701bb54313485fd588d68141c42f6f61cbbe3e8 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 2 Jul 2020 20:42:22 +0300 Subject: [PATCH 05/41] REFACT: refact the boolean expression --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index aadc3b095da25..4048cdfe221da 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -263,7 +263,7 @@ def _sanitize_and_check(indexes): return indexes, "list" # GH 35092. Check for Index subclass to avoid setting special type by error - if len(kinds) > 1 or all([not isinstance(index, Index) for index in indexes]): + if len(kinds) > 1 or not any([issubclass(kind, Index) for kind in kinds]): return indexes, "special" else: return indexes, "array" From 3208fbe7e742e6c843325b1a845eab10e27e9319 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Thu, 2 Jul 2020 20:47:17 +0300 Subject: [PATCH 06/41] REFACT: use generator comprehension --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 4048cdfe221da..ae12b2ffa3299 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -263,7 +263,7 @@ def _sanitize_and_check(indexes): return indexes, "list" # GH 35092. Check for Index subclass to avoid setting special type by error - if len(kinds) > 1 or not any([issubclass(kind, Index) for kind in kinds]): + if len(kinds) > 1 or not any(issubclass(kind, Index) for kind in kinds): return indexes, "special" else: return indexes, "array" From a15cef4031cb02578f06a27f0f98373dddd8991b Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 3 Jul 2020 09:27:36 +0300 Subject: [PATCH 07/41] TST: add cases to the test --- pandas/tests/indexes/test_common.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py index 6b14b25815297..8b7b9ab4c5804 100644 --- a/pandas/tests/indexes/test_common.py +++ b/pandas/tests/indexes/test_common.py @@ -398,13 +398,13 @@ def test_astype_preserves_name(self, index, dtype, copy): assert result.name == index.name +@pytest.mark.parametrize("exp_arr, sort", [([0, 1, 4, 3], False), ([0, 1, 3, 4], True)]) @pytest.mark.parametrize("dtype", ["int8", "int16", "int32", "int64"]) -def test_union_index_no_sort(dtype): +def test_union_index_no_sort(exp_arr, sort, dtype): # GH 35092. Check that we don't sort with sort=False ind1 = Index([0, 1], dtype=dtype) ind2 = Index([4, 3], dtype=dtype) - expected = Index([0, 1, 4, 3], dtype=dtype) - result = union_indexes([ind1, ind2], sort=False) - + expected = Index(exp_arr, dtype=dtype) + result = union_indexes([ind1, ind2], sort=sort) tm.assert_index_equal(result, expected) From 933956c2cc8631f93086bd4d977d49c5b1cf6af4 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 3 Jul 2020 09:36:20 +0300 Subject: [PATCH 08/41] preserve Index subclass, exclude MultiIndex --- pandas/core/indexes/api.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index ae12b2ffa3299..7222c36882560 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -204,7 +204,16 @@ def conv(i): i = i.tolist() return i - return Index(lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort)) + # GH 35092. Preserve argument type. This function gets called only when + # there is just one type + if type(inds[0]) != list: + ind_class = type(inds[0]) + else: + ind_class = Index + + return ind_class( + lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort) + ) if kind == "special": result = indexes[0] @@ -263,7 +272,10 @@ def _sanitize_and_check(indexes): return indexes, "list" # GH 35092. Check for Index subclass to avoid setting special type by error - if len(kinds) > 1 or not any(issubclass(kind, Index) for kind in kinds): + # exclude MultiIndex + if len(kinds) > 1 or not any( + issubclass(kind, Index) and kind != MultiIndex for kind in kinds + ): return indexes, "special" else: return indexes, "array" From 4ecd83e2579366237ba9aa0f7663165cde54e9cb Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 3 Jul 2020 10:25:31 +0300 Subject: [PATCH 09/41] include RangeIndex in exceptions --- pandas/core/indexes/api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 7222c36882560..02ffb49dc7b6e 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -211,6 +211,7 @@ def conv(i): else: ind_class = Index + print(inds) return ind_class( lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort) ) @@ -274,7 +275,8 @@ def _sanitize_and_check(indexes): # GH 35092. Check for Index subclass to avoid setting special type by error # exclude MultiIndex if len(kinds) > 1 or not any( - issubclass(kind, Index) and kind != MultiIndex for kind in kinds + issubclass(kind, Index) and kind != MultiIndex and kind != RangeIndex + for kind in kinds ): return indexes, "special" else: From 896d62ab2c936bfdb19498c82ced2772443c4f0c Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 3 Jul 2020 12:11:02 +0300 Subject: [PATCH 10/41] exclude DatetimeIndex DatetimeIndex has an explicit branch set up for it with kind == 'special' --- pandas/core/indexes/api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 02ffb49dc7b6e..4b94219c9e336 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -211,7 +211,6 @@ def conv(i): else: ind_class = Index - print(inds) return ind_class( lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort) ) @@ -273,9 +272,10 @@ def _sanitize_and_check(indexes): return indexes, "list" # GH 35092. Check for Index subclass to avoid setting special type by error - # exclude MultiIndex + # exclude MultiIndex, RangeIndex as sorting for them doesn't make much sense + # exclude DatetimeIndex as it's explicitly processed through union_many if len(kinds) > 1 or not any( - issubclass(kind, Index) and kind != MultiIndex and kind != RangeIndex + issubclass(kind, Index) and kind not in [MultiIndex, RangeIndex, DatetimeIndex] for kind in kinds ): return indexes, "special" From 950b27947c78294864888960b43343526221277c Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 3 Jul 2020 16:04:56 +0300 Subject: [PATCH 11/41] exclude CategoricalIndex --- pandas/core/indexes/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 4b94219c9e336..f370c699160d7 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -275,7 +275,8 @@ def _sanitize_and_check(indexes): # exclude MultiIndex, RangeIndex as sorting for them doesn't make much sense # exclude DatetimeIndex as it's explicitly processed through union_many if len(kinds) > 1 or not any( - issubclass(kind, Index) and kind not in [MultiIndex, RangeIndex, DatetimeIndex] + issubclass(kind, Index) + and kind not in [MultiIndex, RangeIndex, DatetimeIndex, CategoricalIndex] for kind in kinds ): return indexes, "special" From 4b2076d1478e3ce6a5f8a1a273c8f84cc64fe1ba Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 3 Jul 2020 16:29:08 +0300 Subject: [PATCH 12/41] CLN: sort imports in test_common --- pandas/tests/indexes/test_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py index 8b7b9ab4c5804..b1f8f1234d2d3 100644 --- a/pandas/tests/indexes/test_common.py +++ b/pandas/tests/indexes/test_common.py @@ -11,11 +11,11 @@ from pandas._libs.tslibs import iNaT from pandas.core.dtypes.common import is_period_dtype, needs_i8_conversion -from pandas.core.indexes.api import union_indexes import pandas as pd from pandas import CategoricalIndex, Index, MultiIndex, RangeIndex import pandas._testing as tm +from pandas.core.indexes.api import union_indexes class TestCommon: From 6f2f3c9e37aea9be909525f866136d4504e1ab12 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 3 Jul 2020 17:18:35 +0300 Subject: [PATCH 13/41] DOC: remove unnecessary backquoutes in whatsnew --- doc/source/whatsnew/v1.1.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 54e0ba306cc58..fc54fa3fe61ee 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -974,7 +974,7 @@ Indexing - Bug in :meth:`DataFrame.loc` with dictionary of values changes columns with dtype of ``int`` to ``float`` (:issue:`34573`) - Bug in :meth:`Series.loc` when used with a :class:`MultiIndex` would raise an IndexingError when accessing a None value (:issue:`34318`) - Bug in :meth:`DataFrame.reset_index` and :meth:`Series.reset_index` would not preserve data types on an empty :class:`DataFrame` or :class:`Series` with a :class:`MultiIndex` (:issue:`19602`) -- Bug in :func:``pandas.core.indexes.api.union_indexes`` would lead to :meth:`DataFrame.append` sorting columns even when ``sort=False`` is specified (:issue`35092`) +- Bug in :func:`pandas.core.indexes.api.union_indexes` would lead to :meth:`DataFrame.append` sorting columns even when ``sort=False`` is specified (:issue`35092`) Missing ^^^^^^^ From 0a6c62ef50170e67820e481d29d76c36b412340e Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Fri, 3 Jul 2020 17:46:24 +0300 Subject: [PATCH 14/41] DOC: add missing colon to whatsnew --- doc/source/whatsnew/v1.1.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index fc54fa3fe61ee..7815b01a37b4d 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -974,7 +974,7 @@ Indexing - Bug in :meth:`DataFrame.loc` with dictionary of values changes columns with dtype of ``int`` to ``float`` (:issue:`34573`) - Bug in :meth:`Series.loc` when used with a :class:`MultiIndex` would raise an IndexingError when accessing a None value (:issue:`34318`) - Bug in :meth:`DataFrame.reset_index` and :meth:`Series.reset_index` would not preserve data types on an empty :class:`DataFrame` or :class:`Series` with a :class:`MultiIndex` (:issue:`19602`) -- Bug in :func:`pandas.core.indexes.api.union_indexes` would lead to :meth:`DataFrame.append` sorting columns even when ``sort=False`` is specified (:issue`35092`) +- Bug in :func:`pandas.core.indexes.api.union_indexes` would lead to :meth:`DataFrame.append` sorting columns even when ``sort=False`` is specified (:issue:`35092`) Missing ^^^^^^^ From 9ca48f7dd87ec5be68454d3c11ff1634c2f43284 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 08:19:58 +0300 Subject: [PATCH 15/41] pass sort to Index.union instead of previous approach --- pandas/core/indexes/api.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index f370c699160d7..91a122759e441 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -204,16 +204,7 @@ def conv(i): i = i.tolist() return i - # GH 35092. Preserve argument type. This function gets called only when - # there is just one type - if type(inds[0]) != list: - ind_class = type(inds[0]) - else: - ind_class = Index - - return ind_class( - lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort) - ) + return Index(lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort)) if kind == "special": result = indexes[0] @@ -223,7 +214,11 @@ def conv(i): return result.union_many(indexes[1:]) else: for other in indexes[1:]: - result = result.union(other) + # GH 35092. Pass sort to Index.union + # Index.union expects sort=None instead of sort=True + if sort: + sort = None + result = result.union(other, sort=sort) return result elif kind == "array": index = indexes[0] @@ -271,14 +266,7 @@ def _sanitize_and_check(indexes): else: return indexes, "list" - # GH 35092. Check for Index subclass to avoid setting special type by error - # exclude MultiIndex, RangeIndex as sorting for them doesn't make much sense - # exclude DatetimeIndex as it's explicitly processed through union_many - if len(kinds) > 1 or not any( - issubclass(kind, Index) - and kind not in [MultiIndex, RangeIndex, DatetimeIndex, CategoricalIndex] - for kind in kinds - ): + if len(kinds) > 1 or Index not in kinds: return indexes, "special" else: return indexes, "array" From f41b7e72a3e0871cd2b9e90a190b1905a3c7d635 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 09:00:31 +0300 Subject: [PATCH 16/41] explicitly ignore sort for heavily modified Index subclasses If an Index isn't just a sliceable one-level array (Index), we should not mess with sort to avoid breaking backwards compatibility. --- pandas/core/indexes/api.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 91a122759e441..e27542f1236a5 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -206,6 +206,17 @@ def conv(i): return Index(lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort)) + # GH 35092. Detect if we have an Index type, for which the sort + # setting doesn't make sense + ind_types = list({type(index) for index in indexes}) + if any( + ind_type in [MultiIndex, RangeIndex, DatetimeIndex, CategoricalIndex] + for ind_type in ind_types + ): + ignore_sort = True + else: + ignore_sort = False + if kind == "special": result = indexes[0] From 2fe2c9dddad85c54b602ae7260da28ab8e22fa20 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 09:08:51 +0300 Subject: [PATCH 17/41] fully incorporate sort ignoring for particular Index subtypes --- pandas/core/indexes/api.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index e27542f1236a5..11bf9a0c9ef29 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -227,9 +227,12 @@ def conv(i): for other in indexes[1:]: # GH 35092. Pass sort to Index.union # Index.union expects sort=None instead of sort=True - if sort: - sort = None - result = result.union(other, sort=sort) + if not ignore_sort: + if sort: + sort = None + result = result.union(other, sort=sort) + else: + result = result.union(other) return result elif kind == "array": index = indexes[0] From cefd5c94e2e4038484101f8e8da0d3de20f122f6 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 09:11:05 +0300 Subject: [PATCH 18/41] CLN: fix typo --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 11bf9a0c9ef29..d90189f1e3b2a 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -206,7 +206,7 @@ def conv(i): return Index(lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort)) - # GH 35092. Detect if we have an Index type, for which the sort + # GH 35092. Detect if we have an Index type for which the sort # setting doesn't make sense ind_types = list({type(index) for index in indexes}) if any( From 605ac2170182ba176aa35b5dbdb8b05d4ccfb73c Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 12:04:34 +0300 Subject: [PATCH 19/41] CLN: switch ind_types to list comprehension --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index d90189f1e3b2a..ec915e4bef860 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -208,7 +208,7 @@ def conv(i): # GH 35092. Detect if we have an Index type for which the sort # setting doesn't make sense - ind_types = list({type(index) for index in indexes}) + ind_types = [type(index) for index in indexes] if any( ind_type in [MultiIndex, RangeIndex, DatetimeIndex, CategoricalIndex] for ind_type in ind_types From 18abbae3874bd53ab3f471f5503d4ea3dce152ca Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 12:12:06 +0300 Subject: [PATCH 20/41] test without DatetimeIndex and CategoricalIndex in exceptions --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index ec915e4bef860..6e3ab7330e22f 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -210,7 +210,7 @@ def conv(i): # setting doesn't make sense ind_types = [type(index) for index in indexes] if any( - ind_type in [MultiIndex, RangeIndex, DatetimeIndex, CategoricalIndex] + ind_type in [MultiIndex, RangeIndex] for ind_type in ind_types ): ignore_sort = True From 81116d47a9fd1b5ea82f0bbfc1055178b5013afb Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 12:40:34 +0300 Subject: [PATCH 21/41] test without multi, range, datetime in exceptions --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 6e3ab7330e22f..13663a2545710 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -210,7 +210,7 @@ def conv(i): # setting doesn't make sense ind_types = [type(index) for index in indexes] if any( - ind_type in [MultiIndex, RangeIndex] + ind_type in [CategoricalIndex] for ind_type in ind_types ): ignore_sort = True From a7ec4bf49457fab1723d2bb95c4eb27fc3ecde94 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 13:20:02 +0300 Subject: [PATCH 22/41] return MultiIndex, RangeIndex, CategoricalIndex to exceptions --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 13663a2545710..48a67bf245d43 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -210,7 +210,7 @@ def conv(i): # setting doesn't make sense ind_types = [type(index) for index in indexes] if any( - ind_type in [CategoricalIndex] + ind_type in [MultiIndex, RangeIndex, CategoricalIndex] for ind_type in ind_types ): ignore_sort = True From e85529df4d3282d66498d4fa03a571e1fa4d59a1 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Sun, 5 Jul 2020 14:58:55 +0300 Subject: [PATCH 23/41] CLN: black indexes/api.py --- pandas/core/indexes/api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 48a67bf245d43..a0e714db0d384 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -210,8 +210,7 @@ def conv(i): # setting doesn't make sense ind_types = [type(index) for index in indexes] if any( - ind_type in [MultiIndex, RangeIndex, CategoricalIndex] - for ind_type in ind_types + ind_type in [MultiIndex, RangeIndex, CategoricalIndex] for ind_type in ind_types ): ignore_sort = True else: From c76859e3e4177f0b11fdf84ff3fe00e77654d9bc Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Mon, 6 Jul 2020 08:57:31 +0300 Subject: [PATCH 24/41] restart tests From b9d5ab463c72377c02dbf5e3444b42e6ea2dce07 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Mon, 6 Jul 2020 19:52:08 +0300 Subject: [PATCH 25/41] add DatetimeIndex back into exceptions DatetimeIndex seems to break test_basic_series_frame_alignment in test_eval.py --- pandas/core/indexes/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index a0e714db0d384..ec915e4bef860 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -210,7 +210,8 @@ def conv(i): # setting doesn't make sense ind_types = [type(index) for index in indexes] if any( - ind_type in [MultiIndex, RangeIndex, CategoricalIndex] for ind_type in ind_types + ind_type in [MultiIndex, RangeIndex, DatetimeIndex, CategoricalIndex] + for ind_type in ind_types ): ignore_sort = True else: From ee7048e6335b2dc53557846d52864b9952b675b7 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 08:57:28 +0300 Subject: [PATCH 26/41] DOC: move to Reshaping in whatsnew and edit --- doc/source/whatsnew/v1.1.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 3083eda842852..0b2493cfaf7ff 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -976,7 +976,6 @@ Indexing - Bug in :meth:`DataFrame.loc` with dictionary of values changes columns with dtype of ``int`` to ``float`` (:issue:`34573`) - Bug in :meth:`Series.loc` when used with a :class:`MultiIndex` would raise an IndexingError when accessing a None value (:issue:`34318`) - Bug in :meth:`DataFrame.reset_index` and :meth:`Series.reset_index` would not preserve data types on an empty :class:`DataFrame` or :class:`Series` with a :class:`MultiIndex` (:issue:`19602`) -- Bug in :func:`pandas.core.indexes.api.union_indexes` would lead to :meth:`DataFrame.append` sorting columns even when ``sort=False`` is specified (:issue:`35092`) Missing ^^^^^^^ @@ -1113,6 +1112,7 @@ Reshaping - Fixed bug in :func:`melt` where melting MultiIndex columns with ``col_level`` > 0 would raise a ``KeyError`` on ``id_vars`` (:issue:`34129`) - Bug in :meth:`Series.where` with an empty Series and empty ``cond`` having non-bool dtype (:issue:`34592`) - Fixed regression where :meth:`DataFrame.apply` would raise ``ValueError`` for elements whth ``S`` dtype (:issue:`34529`) +- Bug in :meth:`DataFrame.append` leading to sorting columns even when ``sort=False`` is specified (:issue:`35092`) Sparse ^^^^^^ From 1f67a3c0dc1e1373bdf309560492c15cdfb36837 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 09:00:55 +0300 Subject: [PATCH 27/41] pass sort through to all Index subclasses --- pandas/core/indexes/api.py | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index ec915e4bef860..91a122759e441 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -206,17 +206,6 @@ def conv(i): return Index(lib.fast_unique_multiple_list([conv(i) for i in inds], sort=sort)) - # GH 35092. Detect if we have an Index type for which the sort - # setting doesn't make sense - ind_types = [type(index) for index in indexes] - if any( - ind_type in [MultiIndex, RangeIndex, DatetimeIndex, CategoricalIndex] - for ind_type in ind_types - ): - ignore_sort = True - else: - ignore_sort = False - if kind == "special": result = indexes[0] @@ -227,12 +216,9 @@ def conv(i): for other in indexes[1:]: # GH 35092. Pass sort to Index.union # Index.union expects sort=None instead of sort=True - if not ignore_sort: - if sort: - sort = None - result = result.union(other, sort=sort) - else: - result = result.union(other) + if sort: + sort = None + result = result.union(other, sort=sort) return result elif kind == "array": index = indexes[0] From c722154fe47adc505c266fb50830f730287aa8ea Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 09:37:49 +0300 Subject: [PATCH 28/41] TST: alter test_construct_with_two_categoricalindex_series --- pandas/tests/frame/test_constructors.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index dba243f1a339a..ab4f7781467e7 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -2542,11 +2542,13 @@ def test_construct_with_two_categoricalindex_series(self): index=pd.CategoricalIndex(["f", "female", "m", "male", "unknown"]), ) result = DataFrame([s1, s2]) + # GH 35092. Extra s2 columns are now appended to s1 columns + # in original order expected = DataFrame( np.array( - [[np.nan, 39.0, np.nan, 6.0, 4.0], [2.0, 152.0, 2.0, 242.0, 150.0]] + [[39.0, 6.0, 4.0, np.nan, np.nan], [152.0, 242.0, 150.0, 2.0, 2.0]] ), - columns=["f", "female", "m", "male", "unknown"], + columns=["female", "male", "unknown", "f", "m"], ) tm.assert_frame_equal(result, expected) From ab291a7e80e78e8f7bd420db04ade7ecaed30892 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 10:09:35 +0300 Subject: [PATCH 29/41] TST: alter test_str_cat_align_mixed_inputs --- pandas/tests/test_strings.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pandas/tests/test_strings.py b/pandas/tests/test_strings.py index d9396d70f9112..3a4e54052305e 100644 --- a/pandas/tests/test_strings.py +++ b/pandas/tests/test_strings.py @@ -636,8 +636,15 @@ def test_str_cat_align_mixed_inputs(self, join): # mixed list of indexed/unindexed u = np.array(["A", "B", "C", "D"]) expected_outer = Series(["aaA", "bbB", "c-C", "ddD", "-e-"]) + # joint index of rhs [t, u]; u will be forced have index of s - rhs_idx = t.index & s.index if join == "inner" else t.index | s.index + # GH 35092. If right join, maintain order of t.index + if join == "inner": + rhs_idx = t.index & s.index + elif join == "right": + rhs_idx = t.index.union(s.index, sort=False) + else: + rhs_idx = t.index | s.index expected = expected_outer.loc[s.index.join(rhs_idx, how=join)] result = s.str.cat([t, u], join=join, na_rep="-") From 93a4ccc7df44e55a88aea15e7b58690408b45a47 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 10:21:47 +0300 Subject: [PATCH 30/41] TST: alter test_unbalanced in test_melt --- pandas/tests/reshape/test_melt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/tests/reshape/test_melt.py b/pandas/tests/reshape/test_melt.py index a0fa10802f860..0760407cedaee 100644 --- a/pandas/tests/reshape/test_melt.py +++ b/pandas/tests/reshape/test_melt.py @@ -691,11 +691,11 @@ def test_unbalanced(self): ) df["id"] = df.index exp_data = { - "X": ["X1", "X1", "X2", "X2"], - "A": [1.0, 3.0, 2.0, 4.0], - "B": [5.0, np.nan, 6.0, np.nan], - "id": [0, 0, 1, 1], - "year": [2010, 2011, 2010, 2011], + "X": ["X1", "X2", "X1", "X2"], + "A": [1.0, 2.0, 3.0, 4.0], + "B": [5.0, 6.0, np.nan, np.nan], + "id": [0, 1, 0, 1], + "year": [2010, 2010, 2011, 2011], } expected = pd.DataFrame(exp_data) expected = expected.set_index(["id", "year"])[["X", "A", "B"]] From 93452c101d0ba5f1abbcbc1c60b9336b48d1db26 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 10:34:33 +0300 Subject: [PATCH 31/41] TST: alter nonnumeric and float suffix tests in test_melt --- pandas/tests/reshape/test_melt.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pandas/tests/reshape/test_melt.py b/pandas/tests/reshape/test_melt.py index 0760407cedaee..84e89cf8b9dcf 100644 --- a/pandas/tests/reshape/test_melt.py +++ b/pandas/tests/reshape/test_melt.py @@ -938,10 +938,10 @@ def test_nonnumeric_suffix(self): ) expected = pd.DataFrame( { - "A": ["X1", "X1", "X2", "X2"], - "colname": ["placebo", "test", "placebo", "test"], - "result": [5.0, np.nan, 6.0, np.nan], - "treatment": [1.0, 3.0, 2.0, 4.0], + "A": ["X1", "X2", "X1", "X2"], + "colname": ["placebo", "placebo", "test", "test"], + "result": [5.0, 6.0, np.nan, np.nan], + "treatment": [1.0, 2.0, 3.0, 4.0], } ) expected = expected.set_index(["A", "colname"]) @@ -985,10 +985,10 @@ def test_float_suffix(self): ) expected = pd.DataFrame( { - "A": ["X1", "X1", "X1", "X1", "X2", "X2", "X2", "X2"], - "colname": [1, 1.1, 1.2, 2.1, 1, 1.1, 1.2, 2.1], - "result": [0.0, np.nan, 5.0, np.nan, 9.0, np.nan, 6.0, np.nan], - "treatment": [np.nan, 1.0, np.nan, 3.0, np.nan, 2.0, np.nan, 4.0], + "A": ['X1', 'X2', 'X1', 'X2', 'X1', 'X2', 'X1', 'X2'], + "colname": [1.2, 1.2, 1.0, 1.0, 1.1, 1.1, 2.1, 2.1], + "result": [5.0, 6.0, 0.0, 9.0, np.nan, np.nan, np.nan, np.nan], + "treatment": [np.nan, np.nan, np.nan, np.nan, 1.0, 2.0, 3.0, 4.0], } ) expected = expected.set_index(["A", "colname"]) From fb3a906fbc006a06b312877d0ed705eb95f36e13 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 10:37:50 +0300 Subject: [PATCH 32/41] CLN: run black pandas --- pandas/tests/reshape/test_melt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/reshape/test_melt.py b/pandas/tests/reshape/test_melt.py index 84e89cf8b9dcf..03fda038539e2 100644 --- a/pandas/tests/reshape/test_melt.py +++ b/pandas/tests/reshape/test_melt.py @@ -985,7 +985,7 @@ def test_float_suffix(self): ) expected = pd.DataFrame( { - "A": ['X1', 'X2', 'X1', 'X2', 'X1', 'X2', 'X1', 'X2'], + "A": ["X1", "X2", "X1", "X2", "X1", "X2", "X1", "X2"], "colname": [1.2, 1.2, 1.0, 1.0, 1.1, 1.1, 2.1, 2.1], "result": [5.0, 6.0, 0.0, 9.0, np.nan, np.nan, np.nan, np.nan], "treatment": [np.nan, np.nan, np.nan, np.nan, 1.0, 2.0, 3.0, 4.0], From 0479618ad82cbbe71e806a383f1757c5dbb2c6c3 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 11:43:12 +0300 Subject: [PATCH 33/41] TST: add OP test, use fixture in the index test --- pandas/tests/indexes/test_common.py | 13 ++++++++----- pandas/tests/reshape/test_concat.py | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py index b1f8f1234d2d3..c85696e02ad39 100644 --- a/pandas/tests/indexes/test_common.py +++ b/pandas/tests/indexes/test_common.py @@ -398,13 +398,16 @@ def test_astype_preserves_name(self, index, dtype, copy): assert result.name == index.name -@pytest.mark.parametrize("exp_arr, sort", [([0, 1, 4, 3], False), ([0, 1, 3, 4], True)]) +@pytest.mark.parametrize("arr", [[0, 1, 4, 3]]) @pytest.mark.parametrize("dtype", ["int8", "int16", "int32", "int64"]) -def test_union_index_no_sort(exp_arr, sort, dtype): +def test_union_index_no_sort(arr, sort, dtype): # GH 35092. Check that we don't sort with sort=False - ind1 = Index([0, 1], dtype=dtype) - ind2 = Index([4, 3], dtype=dtype) + ind1 = Index(arr[:2], dtype=dtype) + ind2 = Index(arr[2:], dtype=dtype) - expected = Index(exp_arr, dtype=dtype) + # sort is None indicates that we sort the combined index + if sort is None: + arr.sort() + expected = Index(arr, dtype=dtype) result = union_indexes([ind1, ind2], sort=sort) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/reshape/test_concat.py b/pandas/tests/reshape/test_concat.py index ffeb5ff0f8aaa..f9a38d0873407 100644 --- a/pandas/tests/reshape/test_concat.py +++ b/pandas/tests/reshape/test_concat.py @@ -2857,3 +2857,17 @@ def test_concat_frame_axis0_extension_dtypes(): result = pd.concat([df2, df1], ignore_index=True) expected = pd.DataFrame({"a": [4, 5, 6, 1, 2, 3]}, dtype="Int64") tm.assert_frame_equal(result, expected) + + +@pytest.mark.parametrize("sort", [True, False]) +def test_append_sort(sort): + # GH 35092. Check that DataFrame.append respects the sort argument. + df1 = pd.DataFrame(data={0: [1,2], 1: [3,4]}) + df2 = pd.DataFrame(data={3: [1,2], 2: [3,4]}) + cols = list(df1.columns) + list(df2.columns) + if sort: + cols.sort() + + result = df1.append(df2, sort=sort).columns + expected = type(result)(cols) + tm.assert_index_equal(result, expected) From 0f96dbedd533dc168c445e40405f04084a671d7a Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 11:45:36 +0300 Subject: [PATCH 34/41] CLN: run black on test files --- pandas/tests/reshape/test_concat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/reshape/test_concat.py b/pandas/tests/reshape/test_concat.py index f9a38d0873407..ff95d8ad997a4 100644 --- a/pandas/tests/reshape/test_concat.py +++ b/pandas/tests/reshape/test_concat.py @@ -2862,8 +2862,8 @@ def test_concat_frame_axis0_extension_dtypes(): @pytest.mark.parametrize("sort", [True, False]) def test_append_sort(sort): # GH 35092. Check that DataFrame.append respects the sort argument. - df1 = pd.DataFrame(data={0: [1,2], 1: [3,4]}) - df2 = pd.DataFrame(data={3: [1,2], 2: [3,4]}) + df1 = pd.DataFrame(data={0: [1, 2], 1: [3, 4]}) + df2 = pd.DataFrame(data={3: [1, 2], 2: [3, 4]}) cols = list(df1.columns) + list(df2.columns) if sort: cols.sort() From ada734694d477ae5000367f2250929f0b9297afb Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 16:41:01 +0300 Subject: [PATCH 35/41] REFACT: remove unnecessary change of sort from True to None --- pandas/core/indexes/api.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 91a122759e441..93b25b3e0aec4 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -214,10 +214,6 @@ def conv(i): return result.union_many(indexes[1:]) else: for other in indexes[1:]: - # GH 35092. Pass sort to Index.union - # Index.union expects sort=None instead of sort=True - if sort: - sort = None result = result.union(other, sort=sort) return result elif kind == "array": From c3cbecc3d3a73806bb4cd6c9212cee456ea822c4 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 16:43:10 +0300 Subject: [PATCH 36/41] Revert "REFACT: remove unnecessary change of sort from True to None" This reverts commit ada734694d477ae5000367f2250929f0b9297afb. It threw an error. --- pandas/core/indexes/api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 93b25b3e0aec4..91a122759e441 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -214,6 +214,10 @@ def conv(i): return result.union_many(indexes[1:]) else: for other in indexes[1:]: + # GH 35092. Pass sort to Index.union + # Index.union expects sort=None instead of sort=True + if sort: + sort = None result = result.union(other, sort=sort) return result elif kind == "array": From 7cc4d9dfe7c764be2aad2dcbce4b969ee7a3f2ae Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Tue, 7 Jul 2020 20:43:08 +0300 Subject: [PATCH 37/41] restart tests From 4c1cc426ecb8932cc345976adbc30bf750297602 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Wed, 8 Jul 2020 16:30:59 +0300 Subject: [PATCH 38/41] DOC: add comment with ref to sort=None issue sort=None signifies that Index.union doesn't gurarantee sorting or error by default. It might mirror legacy behavior and leave the Index unsorted in edge cases. --- pandas/core/indexes/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 91a122759e441..ac0a40f846829 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -216,6 +216,8 @@ def conv(i): for other in indexes[1:]: # GH 35092. Pass sort to Index.union # Index.union expects sort=None instead of sort=True + # to signify that sometimes it might not sort (see GH 24959) + # to mirror legacy behavior. In this case, it does. if sort: sort = None result = result.union(other, sort=sort) From cc0d16702842975d579f5ef11d5eaf2497f61cd2 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Wed, 8 Jul 2020 17:46:20 +0300 Subject: [PATCH 39/41] DOC: clarify comment language --- pandas/core/indexes/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index ac0a40f846829..810e31c9ec3c7 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -217,7 +217,7 @@ def conv(i): # GH 35092. Pass sort to Index.union # Index.union expects sort=None instead of sort=True # to signify that sometimes it might not sort (see GH 24959) - # to mirror legacy behavior. In this case, it does. + # to mirror legacy behavior. if sort: sort = None result = result.union(other, sort=sort) From 54140af50e6b5a71325a9243ccf3ae322814b151 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Wed, 8 Jul 2020 18:10:23 +0300 Subject: [PATCH 40/41] DOC: clarify comment more --- pandas/core/indexes/api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 810e31c9ec3c7..9849742abcfca 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -214,10 +214,10 @@ def conv(i): return result.union_many(indexes[1:]) else: for other in indexes[1:]: - # GH 35092. Pass sort to Index.union - # Index.union expects sort=None instead of sort=True - # to signify that sometimes it might not sort (see GH 24959) - # to mirror legacy behavior. + # GH 35092. Index.union expects sort=None instead of sort=True + # to signify that sort=True isn't fully implemented and + # legacy implementation sometimes might not sort (see GH 24959) + # In this case we currently sort in _get_combined_index if sort: sort = None result = result.union(other, sort=sort) From 1c371bd884f290969be408166829f446d7c2c2f7 Mon Sep 17 00:00:00 2001 From: Alexander Kirko Date: Wed, 8 Jul 2020 19:01:53 +0300 Subject: [PATCH 41/41] restart tests