From 18edbd642f85774df591c9a843e43af6bdd68323 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 19 Jan 2019 20:50:11 -0800 Subject: [PATCH 001/215] Added support for iso time --- pandas/_libs/tslibs/strptime.pyx | 70 ++++++++++++++++++-- pandas/tests/indexes/datetimes/test_tools.py | 47 +++++++++++++ 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 87658ae92175e..a63a0080006b6 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -54,7 +54,10 @@ cdef dict _parse_code_table = {'y': 0, 'W': 16, 'Z': 17, 'p': 18, # an additional key, only with I - 'z': 19} + 'z': 19, + 'G': 20, + 'V': 21, + 'u': 22} def array_strptime(object[:] values, object fmt, @@ -76,7 +79,7 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal + int week_of_year, week_of_year_start, parse_code, ordinal, iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -169,13 +172,14 @@ def array_strptime(object[:] values, object fmt, raise ValueError("time data %r does not match format " "%r (search)" % (values[i], fmt)) + iso_year = -1 year = 1900 month = day = 1 hour = minute = second = ns = us = 0 timezone = None # Default to -1 to signify that values not known; not critical to have, # though - week_of_year = -1 + iso_week = week_of_year = -1 week_of_year_start = -1 # weekday and julian defaulted to -1 so as to signal need to calculate # values @@ -265,13 +269,43 @@ def array_strptime(object[:] values, object fmt, timezone = pytz.timezone(found_dict['Z']) elif parse_code == 19: timezone = parse_timezone_directive(found_dict['z']) + elif parse_code == 20: + iso_year = int(found_dict['G']) + elif parse_code == 21: + iso_week = int(found_dict['V']) + elif parse_code == 22: + weekday = int(found_dict['u']) + weekday -= 1 + + # don't assume default values for ISO week/year + if iso_year != -1: + if iso_week == -1 or weekday == -1: + raise ValueError("ISO year directive '%G' must be used with " + "the ISO week directive '%V' and a weekday " + "directive '%A', '%a', '%w', or '%u'.") + if julian != -1: + raise ValueError("Day of the year directive '%j' is not " + "compatible with ISO year directive '%G'. " + "Use '%Y' instead.") + elif year != -1 and week_of_year == -1 and iso_week != -1: + if weekday == -1: + raise ValueError("ISO week directive '%V' must be used with " + "the ISO year directive '%G' and a weekday " + "directive '%A', '%a', '%w', or '%u'.") + else: + raise ValueError("ISO week directive '%V' is incompatible with" + " the year directive '%Y'. Use the ISO year " + "'%G' instead.") # If we know the wk of the year and what day of that wk, we can figure # out the Julian day of the year. - if julian == -1 and week_of_year != -1 and weekday != -1: - week_starts_Mon = True if week_of_year_start == 0 else False - julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + if julian == -1 and weekday != -1: + if week_of_year != -1: + week_starts_Mon = True if week_of_year_start == 0 else False + julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, + week_starts_Mon) + elif iso_year != -1 and iso_week != -1: + year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. @@ -511,6 +545,7 @@ class TimeRE(dict): # The " \d" part of the regex is to make %c from ANSI C work 'd': r"(?P3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])", 'f': r"(?P[0-9]{1,9})", + 'G': r"(?P\d\d\d\d)", 'H': r"(?P2[0-3]|[0-1]\d|\d)", 'I': r"(?P1[0-2]|0[1-9]|[1-9])", 'j': (r"(?P36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|" @@ -518,7 +553,9 @@ class TimeRE(dict): 'm': r"(?P1[0-2]|0[1-9]|[1-9])", 'M': r"(?P[0-5]\d|\d)", 'S': r"(?P6[0-1]|[0-5]\d|\d)", + 'u': r"(?P[1-7])", 'U': r"(?P5[0-3]|[0-4]\d|\d)", + 'V': r"(?P5[0-3]|0[1-9]|[1-4]\d|\d)", 'w': r"(?P[0-6])", # W is set below by using 'U' 'y': r"(?P\d\d)", @@ -620,6 +657,25 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year, return 1 + days_to_week + day_of_week +cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): + """Calculate the Julian day based on the ISO 8601 year, week, and weekday. + ISO weeks start on Mondays, with week 01 being the week containing 4 Jan. + ISO week days range from 1 (Monday) to 7 (Sunday).""" + + cdef: + int correction, ordinal + + correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 + ordinal = (iso_week * 7) + iso_weekday - correction + # ordinal may be negative or 0 now, which means the date is in the previous + # calendar year + if ordinal < 1: + ordinal += datetime_date(iso_year, 1, 1).toordinal() + iso_year -= 1 + ordinal -= datetime_date(iso_year, 1, 1).toordinal() + return iso_year, ordinal + + cdef parse_timezone_directive(object z): """ Parse the '%z' directive and return a pytz.FixedOffset diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index 50c8f8d4c1f4c..6f6f0ffe2c595 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -247,6 +247,53 @@ def test_to_datetime_parse_timezone_keeps_name(self): class TestToDatetime(object): + @pytest.mark.parametrize("s, _format, dt", [ + ['2015-1-1', '%G-%V-%u', datetime(2014, 12, 29, 0, 0)], + ['2015-1-4', '%G-%V-%u', datetime(2015, 1, 1, 0, 0)], + ['2015-1-7', '%G-%V-%u', datetime(2015, 1, 4, 0, 0)] + ]) + def test_to_datetime_iso_week_year_format(self, s, _format, dt): + assert to_datetime(s, format=_format) == dt + + @pytest.mark.parametrize("msg, s, _format", [ + ["ISO week directive '%V' must be used with the ISO year directive " + "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 50", + "%Y %V"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 51", + "%G %V"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 " + "Monday", "%G %A"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 Mon", + "%G %a"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 6", + "%G %w"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 6", + "%G %u"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "2051", + "%G"], + ["Day of the year directive '%j' is not compatible with ISO year " + "directive '%G'. Use '%Y' instead.", "1999 51 6 256", "%G %V %u %j"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 Sunday", "%Y %V %A"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 Sun", "%Y %V %a"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 1", "%Y %V %w"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 1", "%Y %V %u"], + ["ISO week directive '%V' must be used with the ISO year directive " + "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] + ]) + def test_ValueError_iso_week_year(self, msg, s, _format): + with tm.assert_raises_regex(ValueError, msg): + to_datetime(s, format=_format) + @pytest.mark.parametrize('tz', [None, 'US/Central']) def test_to_datetime_dtarr(self, tz): # DatetimeArray From f3183f0d3c0835ada311719daf5159aa27b9928d Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 19 Jan 2019 21:22:52 -0800 Subject: [PATCH 002/215] Added whatsnew entry? --- doc/source/whatsnew/v0.24.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 3268575c7064d..e2ed7e4829aeb 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -362,6 +362,7 @@ See the :ref:`Advanced documentation on renaming` for more Other Enhancements ^^^^^^^^^^^^^^^^^^ +- Added support for %G strptime directive in parsing datetimes - :func:`merge` now directly allows merge between objects of type ``DataFrame`` and named ``Series``, without the need to convert the ``Series`` object into a ``DataFrame`` beforehand (:issue:`21220`) - ``ExcelWriter`` now accepts ``mode`` as a keyword argument, enabling append to existing workbooks when using the ``openpyxl`` engine (:issue:`3441`) - ``FrozenList`` has gained the ``.union()`` and ``.difference()`` methods. This functionality greatly simplifies groupby's that rely on explicitly excluding certain columns. See :ref:`Splitting an object into groups ` for more information (:issue:`15475`, :issue:`15506`). From 89921a31550d36a27fc48178d87364d62eb7e55f Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 10:38:00 -0800 Subject: [PATCH 003/215] n --- pandas/_libs/tslibs/strptime.pyx | 7 ++++--- pandas/tests/indexes/datetimes/test_tools.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index a63a0080006b6..dc3ec51a90fea 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -79,7 +79,8 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal, iso_week, iso_year + int week_of_year, week_of_year_start, parse_code, ordinal, + iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -303,7 +304,7 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian @@ -664,7 +665,7 @@ cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): cdef: int correction, ordinal - + correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 ordinal = (iso_week * 7) + iso_weekday - correction # ordinal may be negative or 0 now, which means the date is in the previous diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index 6f6f0ffe2c595..5575a7c447535 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -291,7 +291,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] ]) def test_ValueError_iso_week_year(self, msg, s, _format): - with tm.assert_raises_regex(ValueError, msg): + with pytest.raises(ValueError, message=msg): to_datetime(s, format=_format) @pytest.mark.parametrize('tz', [None, 'US/Central']) From 9ee53bd02b1d99fc03c59f6735d8ee0a2631fa94 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 13:11:01 -0800 Subject: [PATCH 004/215] Fix linting error --- pandas/_libs/tslibs/strptime.pyx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index dc3ec51a90fea..cfada70db525b 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -79,8 +79,8 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal, - iso_week, iso_year + int week_of_year, week_of_year_start, parse_code, ordinal + int iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -304,9 +304,10 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: - year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) + year, julian = _calc_julian_from_V(iso_year, iso_week, + weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. From 7c3a3056fedb26e581da7861fd4ffd6149e881be Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 13:32:06 -0800 Subject: [PATCH 005/215] Add changes from reviewer --- doc/source/whatsnew/v0.24.0.rst | 2 +- pandas/tests/indexes/datetimes/test_tools.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index e41e9cf7d2896..fc5b6667703ff 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -362,7 +362,7 @@ See the :ref:`Advanced documentation on renaming` for more Other Enhancements ^^^^^^^^^^^^^^^^^^ -- Added support for %G strptime directive in parsing datetimes +- Added support for ISO week year format when parsing datetimes using `to_datetime` (:issue:`16607`) - :func:`merge` now directly allows merge between objects of type ``DataFrame`` and named ``Series``, without the need to convert the ``Series`` object into a ``DataFrame`` beforehand (:issue:`21220`) - ``ExcelWriter`` now accepts ``mode`` as a keyword argument, enabling append to existing workbooks when using the ``openpyxl`` engine (:issue:`3441`) - ``FrozenList`` has gained the ``.union()`` and ``.difference()`` methods. This functionality greatly simplifies groupby's that rely on explicitly excluding certain columns. See :ref:`Splitting an object into groups ` for more information (:issue:`15475`, :issue:`15506`). diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index 5575a7c447535..79a9416ca93de 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -253,6 +253,7 @@ class TestToDatetime(object): ['2015-1-7', '%G-%V-%u', datetime(2015, 1, 4, 0, 0)] ]) def test_to_datetime_iso_week_year_format(self, s, _format, dt): + # See GH#16607 assert to_datetime(s, format=_format) == dt @pytest.mark.parametrize("msg, s, _format", [ @@ -291,6 +292,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] ]) def test_ValueError_iso_week_year(self, msg, s, _format): + # See GH#16607 with pytest.raises(ValueError, message=msg): to_datetime(s, format=_format) From e99049a820bf6a914513f715ce7ed0b01224d826 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 21:20:52 -0800 Subject: [PATCH 006/215] Fix errors found in azure --- pandas/_libs/tslibs/strptime.pyx | 4 ++-- pandas/tests/indexes/datetimes/test_tools.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index cfada70db525b..cfe402b11092c 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -304,10 +304,10 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: year, julian = _calc_julian_from_V(iso_year, iso_week, - weekday + 1) + weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index 79a9416ca93de..666fafa8ec628 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -293,7 +293,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): ]) def test_ValueError_iso_week_year(self, msg, s, _format): # See GH#16607 - with pytest.raises(ValueError, message=msg): + with pytest.raises(ValueError, match=msg): to_datetime(s, format=_format) @pytest.mark.parametrize('tz', [None, 'US/Central']) From fcc11feb1caf04061195d1b21c4952a4c31fa23a Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 27 Jan 2019 16:44:25 -0800 Subject: [PATCH 007/215] Making change to one thing --- pandas/_libs/tslibs/strptime.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index cfe402b11092c..8f45879feaa3a 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -302,7 +302,7 @@ def array_strptime(object[:] values, object fmt, # out the Julian day of the year. if julian == -1 and weekday != -1: if week_of_year != -1: - week_starts_Mon = True if week_of_year_start == 0 else False + week_starts_Mon = week_of_year_start == 0 julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, week_starts_Mon) elif iso_year != -1 and iso_week != -1: From 9f2c421a3a0fa74285f086824cb0ea80970ca5e5 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Mon, 28 Jan 2019 12:38:29 +0000 Subject: [PATCH 008/215] STY: use pytest.raises context syntax (indexing) (#24960) --- pandas/tests/indexing/multiindex/test_loc.py | 6 +- .../tests/indexing/multiindex/test_partial.py | 4 +- .../tests/indexing/multiindex/test_slice.py | 3 +- pandas/tests/indexing/test_categorical.py | 52 ++-- .../indexing/test_chaining_and_caching.py | 6 +- pandas/tests/indexing/test_floats.py | 227 ++++++++++++------ pandas/tests/indexing/test_iloc.py | 50 ++-- pandas/tests/indexing/test_ix.py | 18 +- pandas/tests/indexing/test_loc.py | 53 ++-- pandas/tests/indexing/test_partial.py | 10 +- pandas/tests/indexing/test_scalar.py | 32 ++- 11 files changed, 307 insertions(+), 154 deletions(-) diff --git a/pandas/tests/indexing/multiindex/test_loc.py b/pandas/tests/indexing/multiindex/test_loc.py index ea451d40eb5d3..073d40001a16b 100644 --- a/pandas/tests/indexing/multiindex/test_loc.py +++ b/pandas/tests/indexing/multiindex/test_loc.py @@ -123,10 +123,12 @@ def test_loc_multiindex(self): tm.assert_frame_equal(rs, xp) # missing label - pytest.raises(KeyError, lambda: mi_int.loc[2]) + with pytest.raises(KeyError, match=r"^2L?$"): + mi_int.loc[2] with catch_warnings(record=True): # GH 21593 - pytest.raises(KeyError, lambda: mi_int.ix[2]) + with pytest.raises(KeyError, match=r"^2L?$"): + mi_int.ix[2] def test_loc_multiindex_indexer_none(self): diff --git a/pandas/tests/indexing/multiindex/test_partial.py b/pandas/tests/indexing/multiindex/test_partial.py index 2e37ebe4a0629..473463def2b87 100644 --- a/pandas/tests/indexing/multiindex/test_partial.py +++ b/pandas/tests/indexing/multiindex/test_partial.py @@ -104,8 +104,8 @@ def test_getitem_partial_column_select(self): result = df.ix[('a', 'y'), [1, 0]] tm.assert_frame_equal(result, expected) - pytest.raises(KeyError, df.loc.__getitem__, - (('a', 'foo'), slice(None, None))) + with pytest.raises(KeyError, match=r"\('a', 'foo'\)"): + df.loc[('a', 'foo'), :] def test_partial_set( self, multiindex_year_month_day_dataframe_random_data): diff --git a/pandas/tests/indexing/multiindex/test_slice.py b/pandas/tests/indexing/multiindex/test_slice.py index fcecb2b454eb6..db7d079186708 100644 --- a/pandas/tests/indexing/multiindex/test_slice.py +++ b/pandas/tests/indexing/multiindex/test_slice.py @@ -107,7 +107,8 @@ def test_per_axis_per_level_getitem(self): # ambiguous cases # these can be multiply interpreted (e.g. in this case # as df.loc[slice(None),[1]] as well - pytest.raises(KeyError, lambda: df.loc[slice(None), [1]]) + with pytest.raises(KeyError, match=r"'\[1\] not in index'"): + df.loc[slice(None), [1]] result = df.loc[(slice(None), [1]), :] expected = df.iloc[[0, 3]] diff --git a/pandas/tests/indexing/test_categorical.py b/pandas/tests/indexing/test_categorical.py index b7443e242137b..317aac1766cf8 100644 --- a/pandas/tests/indexing/test_categorical.py +++ b/pandas/tests/indexing/test_categorical.py @@ -53,23 +53,20 @@ def test_loc_scalar(self): assert_frame_equal(df, expected) # value not in the categories - pytest.raises(KeyError, lambda: df.loc['d']) + with pytest.raises(KeyError, match=r"^'d'$"): + df.loc['d'] - def f(): + msg = "cannot append a non-category item to a CategoricalIndex" + with pytest.raises(TypeError, match=msg): df.loc['d'] = 10 - pytest.raises(TypeError, f) - - def f(): + msg = ("cannot insert an item into a CategoricalIndex that is not" + " already an existing category") + with pytest.raises(TypeError, match=msg): df.loc['d', 'A'] = 10 - - pytest.raises(TypeError, f) - - def f(): + with pytest.raises(TypeError, match=msg): df.loc['d', 'C'] = 10 - pytest.raises(TypeError, f) - def test_getitem_scalar(self): cats = Categorical([Timestamp('12-31-1999'), @@ -318,7 +315,8 @@ def test_loc_listlike(self): assert_frame_equal(result, expected, check_index_type=True) # element in the categories but not in the values - pytest.raises(KeyError, lambda: self.df2.loc['e']) + with pytest.raises(KeyError, match=r"^'e'$"): + self.df2.loc['e'] # assign is ok df = self.df2.copy() @@ -616,22 +614,29 @@ def test_reindexing(self): assert_frame_equal(result, expected, check_index_type=True) # passed duplicate indexers are not allowed - pytest.raises(ValueError, lambda: self.df2.reindex(['a', 'a'])) + msg = "cannot reindex with a non-unique indexer" + with pytest.raises(ValueError, match=msg): + self.df2.reindex(['a', 'a']) # args NotImplemented ATM - pytest.raises(NotImplementedError, - lambda: self.df2.reindex(['a'], method='ffill')) - pytest.raises(NotImplementedError, - lambda: self.df2.reindex(['a'], level=1)) - pytest.raises(NotImplementedError, - lambda: self.df2.reindex(['a'], limit=2)) + msg = r"argument {} is not implemented for CategoricalIndex\.reindex" + with pytest.raises(NotImplementedError, match=msg.format('method')): + self.df2.reindex(['a'], method='ffill') + with pytest.raises(NotImplementedError, match=msg.format('level')): + self.df2.reindex(['a'], level=1) + with pytest.raises(NotImplementedError, match=msg.format('limit')): + self.df2.reindex(['a'], limit=2) def test_loc_slice(self): # slicing # not implemented ATM # GH9748 - pytest.raises(TypeError, lambda: self.df.loc[1:5]) + msg = ("cannot do slice indexing on {klass} with these " + r"indexers \[1\] of {kind}".format( + klass=str(CategoricalIndex), kind=str(int))) + with pytest.raises(TypeError, match=msg): + self.df.loc[1:5] # result = df.loc[1:5] # expected = df.iloc[[1,2,3,4]] @@ -679,8 +684,11 @@ def test_boolean_selection(self): # categories=[3, 2, 1], # ordered=False, # name=u'B') - pytest.raises(TypeError, lambda: df4[df4.index < 2]) - pytest.raises(TypeError, lambda: df4[df4.index > 1]) + msg = "Unordered Categoricals can only compare equality or not" + with pytest.raises(TypeError, match=msg): + df4[df4.index < 2] + with pytest.raises(TypeError, match=msg): + df4[df4.index > 1] def test_indexing_with_category(self): diff --git a/pandas/tests/indexing/test_chaining_and_caching.py b/pandas/tests/indexing/test_chaining_and_caching.py index e38c1b16b3b60..be0d9c5cf24ca 100644 --- a/pandas/tests/indexing/test_chaining_and_caching.py +++ b/pandas/tests/indexing/test_chaining_and_caching.py @@ -302,11 +302,11 @@ def test_setting_with_copy_bug(self): 'c': ['a', 'b', np.nan, 'd']}) mask = pd.isna(df.c) - def f(): + msg = ("A value is trying to be set on a copy of a slice from a" + " DataFrame") + with pytest.raises(com.SettingWithCopyError, match=msg): df[['c']][mask] = df[['b']][mask] - pytest.raises(com.SettingWithCopyError, f) - # invalid warning as we are returning a new object # GH 8730 df1 = DataFrame({'x': Series(['a', 'b', 'c']), diff --git a/pandas/tests/indexing/test_floats.py b/pandas/tests/indexing/test_floats.py index de91b8f4a796c..b9b47338c9de2 100644 --- a/pandas/tests/indexing/test_floats.py +++ b/pandas/tests/indexing/test_floats.py @@ -6,7 +6,7 @@ import pytest from pandas import ( - DataFrame, Float64Index, Index, Int64Index, RangeIndex, Series) + DataFrame, Float64Index, Index, Int64Index, RangeIndex, Series, compat) import pandas.util.testing as tm from pandas.util.testing import assert_almost_equal, assert_series_equal @@ -54,9 +54,11 @@ def test_scalar_error(self): with pytest.raises(TypeError, match=msg): s.iloc[3.0] - def f(): + msg = ("cannot do positional indexing on {klass} with these " + r"indexers \[3\.0\] of {kind}".format( + klass=type(i), kind=str(float))) + with pytest.raises(TypeError, match=msg): s.iloc[3.0] = 0 - pytest.raises(TypeError, f) @ignore_ix def test_scalar_non_numeric(self): @@ -82,35 +84,46 @@ def test_scalar_non_numeric(self): (lambda x: x.iloc, False), (lambda x: x, True)]: - def f(): - with catch_warnings(record=True): - idxr(s)[3.0] - # gettitem on a DataFrame is a KeyError as it is indexing # via labels on the columns if getitem and isinstance(s, DataFrame): error = KeyError + msg = r"^3(\.0)?$" else: error = TypeError - pytest.raises(error, f) + msg = (r"cannot do (label|index|positional) indexing" + r" on {klass} with these indexers \[3\.0\] of" + r" {kind}|" + "Cannot index by location index with a" + " non-integer key" + .format(klass=type(i), kind=str(float))) + with catch_warnings(record=True): + with pytest.raises(error, match=msg): + idxr(s)[3.0] # label based can be a TypeError or KeyError - def f(): - s.loc[3.0] - if s.index.inferred_type in ['string', 'unicode', 'mixed']: error = KeyError + msg = r"^3$" else: error = TypeError - pytest.raises(error, f) + msg = (r"cannot do (label|index) indexing" + r" on {klass} with these indexers \[3\.0\] of" + r" {kind}" + .format(klass=type(i), kind=str(float))) + with pytest.raises(error, match=msg): + s.loc[3.0] # contains assert 3.0 not in s # setting with a float fails with iloc - def f(): + msg = (r"cannot do (label|index|positional) indexing" + r" on {klass} with these indexers \[3\.0\] of" + r" {kind}" + .format(klass=type(i), kind=str(float))) + with pytest.raises(TypeError, match=msg): s.iloc[3.0] = 0 - pytest.raises(TypeError, f) # setting with an indexer if s.index.inferred_type in ['categorical']: @@ -145,7 +158,12 @@ def f(): # fallsback to position selection, series only s = Series(np.arange(len(i)), index=i) s[3] - pytest.raises(TypeError, lambda: s[3.0]) + msg = (r"cannot do (label|index) indexing" + r" on {klass} with these indexers \[3\.0\] of" + r" {kind}" + .format(klass=type(i), kind=str(float))) + with pytest.raises(TypeError, match=msg): + s[3.0] @ignore_ix def test_scalar_with_mixed(self): @@ -153,19 +171,23 @@ def test_scalar_with_mixed(self): s2 = Series([1, 2, 3], index=['a', 'b', 'c']) s3 = Series([1, 2, 3], index=['a', 'b', 1.5]) - # lookup in a pure string index + # lookup in a pure stringstr # with an invalid indexer for idxr in [lambda x: x.ix, lambda x: x, lambda x: x.iloc]: - def f(): - with catch_warnings(record=True): + msg = (r"cannot do label indexing" + r" on {klass} with these indexers \[1\.0\] of" + r" {kind}|" + "Cannot index by location index with a non-integer key" + .format(klass=str(Index), kind=str(float))) + with catch_warnings(record=True): + with pytest.raises(TypeError, match=msg): idxr(s2)[1.0] - pytest.raises(TypeError, f) - - pytest.raises(KeyError, lambda: s2.loc[1.0]) + with pytest.raises(KeyError, match=r"^1$"): + s2.loc[1.0] result = s2.loc['b'] expected = 2 @@ -175,11 +197,13 @@ def f(): # indexing for idxr in [lambda x: x]: - def f(): + msg = (r"cannot do label indexing" + r" on {klass} with these indexers \[1\.0\] of" + r" {kind}" + .format(klass=str(Index), kind=str(float))) + with pytest.raises(TypeError, match=msg): idxr(s3)[1.0] - pytest.raises(TypeError, f) - result = idxr(s3)[1] expected = 2 assert result == expected @@ -189,17 +213,22 @@ def f(): for idxr in [lambda x: x.ix]: with catch_warnings(record=True): - def f(): + msg = (r"cannot do label indexing" + r" on {klass} with these indexers \[1\.0\] of" + r" {kind}" + .format(klass=str(Index), kind=str(float))) + with pytest.raises(TypeError, match=msg): idxr(s3)[1.0] - pytest.raises(TypeError, f) - result = idxr(s3)[1] expected = 2 assert result == expected - pytest.raises(TypeError, lambda: s3.iloc[1.0]) - pytest.raises(KeyError, lambda: s3.loc[1.0]) + msg = "Cannot index by location index with a non-integer key" + with pytest.raises(TypeError, match=msg): + s3.iloc[1.0] + with pytest.raises(KeyError, match=r"^1$"): + s3.loc[1.0] result = s3.loc[1.5] expected = 3 @@ -280,16 +309,14 @@ def test_scalar_float(self): # setting s2 = s.copy() - def f(): - with catch_warnings(record=True): - idxr(s2)[indexer] = expected with catch_warnings(record=True): result = idxr(s2)[indexer] self.check(result, s, 3, getitem) # random integer is a KeyError with catch_warnings(record=True): - pytest.raises(KeyError, lambda: idxr(s)[3.5]) + with pytest.raises(KeyError, match=r"^3\.5$"): + idxr(s)[3.5] # contains assert 3.0 in s @@ -303,11 +330,16 @@ def f(): self.check(result, s, 3, False) # iloc raises with a float - pytest.raises(TypeError, lambda: s.iloc[3.0]) + msg = "Cannot index by location index with a non-integer key" + with pytest.raises(TypeError, match=msg): + s.iloc[3.0] - def g(): + msg = (r"cannot do positional indexing" + r" on {klass} with these indexers \[3\.0\] of" + r" {kind}" + .format(klass=str(Float64Index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s2.iloc[3.0] = 0 - pytest.raises(TypeError, g) @ignore_ix def test_slice_non_numeric(self): @@ -329,37 +361,55 @@ def test_slice_non_numeric(self): slice(3, 4.0), slice(3.0, 4.0)]: - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[(3|4)\.0\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s.iloc[l] - pytest.raises(TypeError, f) for idxr in [lambda x: x.ix, lambda x: x.loc, lambda x: x.iloc, lambda x: x]: - def f(): - with catch_warnings(record=True): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers" + r" \[(3|4)(\.0)?\]" + r" of ({kind_float}|{kind_int})" + .format(klass=type(index), + kind_float=str(float), + kind_int=str(int))) + with catch_warnings(record=True): + with pytest.raises(TypeError, match=msg): idxr(s)[l] - pytest.raises(TypeError, f) # setitem for l in [slice(3.0, 4), slice(3, 4.0), slice(3.0, 4.0)]: - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[(3|4)\.0\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s.iloc[l] = 0 - pytest.raises(TypeError, f) for idxr in [lambda x: x.ix, lambda x: x.loc, lambda x: x.iloc, lambda x: x]: - def f(): - with catch_warnings(record=True): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers" + r" \[(3|4)(\.0)?\]" + r" of ({kind_float}|{kind_int})" + .format(klass=type(index), + kind_float=str(float), + kind_int=str(int))) + with catch_warnings(record=True): + with pytest.raises(TypeError, match=msg): idxr(s)[l] = 0 - pytest.raises(TypeError, f) @ignore_ix def test_slice_integer(self): @@ -396,11 +446,13 @@ def test_slice_integer(self): self.check(result, s, indexer, False) # positional indexing - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[(3|4)\.0\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s[l] - pytest.raises(TypeError, f) - # getitem out-of-bounds for l in [slice(-6, 6), slice(-6.0, 6.0)]: @@ -420,11 +472,13 @@ def f(): self.check(result, s, indexer, False) # positional indexing - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[-6\.0\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s[slice(-6.0, 6.0)] - pytest.raises(TypeError, f) - # getitem odd floats for l, res1 in [(slice(2.5, 4), slice(3, 5)), (slice(2, 3.5), slice(2, 4)), @@ -443,11 +497,13 @@ def f(): self.check(result, s, res, False) # positional indexing - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[(2|3)\.5\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s[l] - pytest.raises(TypeError, f) - # setitem for l in [slice(3.0, 4), slice(3, 4.0), @@ -462,11 +518,13 @@ def f(): assert (result == 0).all() # positional indexing - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[(3|4)\.0\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s[l] = 0 - pytest.raises(TypeError, f) - def test_integer_positional_indexing(self): """ make sure that we are raising on positional indexing w.r.t. an integer index """ @@ -484,11 +542,17 @@ def test_integer_positional_indexing(self): slice(2.0, 4), slice(2.0, 4.0)]: - def f(): + if compat.PY2: + klass = Int64Index + else: + klass = RangeIndex + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[(2|4)\.0\] of" + " {kind}" + .format(klass=str(klass), kind=str(float))) + with pytest.raises(TypeError, match=msg): idxr(s)[l] - pytest.raises(TypeError, f) - @ignore_ix def test_slice_integer_frame_getitem(self): @@ -509,11 +573,13 @@ def f(idxr): self.check(result, s, indexer, False) # positional indexing - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[(0|1)\.0\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s[l] - pytest.raises(TypeError, f) - # getitem out-of-bounds for l in [slice(-10, 10), slice(-10.0, 10.0)]: @@ -522,11 +588,13 @@ def f(): self.check(result, s, slice(-10, 10), True) # positional indexing - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[-10\.0\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s[slice(-10.0, 10.0)] - pytest.raises(TypeError, f) - # getitem odd floats for l, res in [(slice(0.5, 1), slice(1, 2)), (slice(0, 0.5), slice(0, 1)), @@ -536,11 +604,13 @@ def f(): self.check(result, s, res, False) # positional indexing - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[0\.5\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s[l] - pytest.raises(TypeError, f) - # setitem for l in [slice(3.0, 4), slice(3, 4.0), @@ -552,11 +622,13 @@ def f(): assert (result == 0).all() # positional indexing - def f(): + msg = ("cannot do slice indexing" + r" on {klass} with these indexers \[(3|4)\.0\] of" + " {kind}" + .format(klass=type(index), kind=str(float))) + with pytest.raises(TypeError, match=msg): s[l] = 0 - pytest.raises(TypeError, f) - f(lambda x: x.loc) with catch_warnings(record=True): f(lambda x: x.ix) @@ -632,9 +704,12 @@ def test_floating_misc(self): # value not found (and no fallbacking at all) # scalar integers - pytest.raises(KeyError, lambda: s.loc[4]) - pytest.raises(KeyError, lambda: s.loc[4]) - pytest.raises(KeyError, lambda: s[4]) + with pytest.raises(KeyError, match=r"^4\.0$"): + s.loc[4] + with pytest.raises(KeyError, match=r"^4\.0$"): + s.loc[4] + with pytest.raises(KeyError, match=r"^4\.0$"): + s[4] # fancy floats/integers create the correct entry (as nan) # fancy tests diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index a867387db4b46..5c87d553daba3 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -26,26 +26,33 @@ def test_iloc_exceeds_bounds(self): msg = 'positional indexers are out-of-bounds' with pytest.raises(IndexError, match=msg): df.iloc[:, [0, 1, 2, 3, 4, 5]] - pytest.raises(IndexError, lambda: df.iloc[[1, 30]]) - pytest.raises(IndexError, lambda: df.iloc[[1, -30]]) - pytest.raises(IndexError, lambda: df.iloc[[100]]) + with pytest.raises(IndexError, match=msg): + df.iloc[[1, 30]] + with pytest.raises(IndexError, match=msg): + df.iloc[[1, -30]] + with pytest.raises(IndexError, match=msg): + df.iloc[[100]] s = df['A'] - pytest.raises(IndexError, lambda: s.iloc[[100]]) - pytest.raises(IndexError, lambda: s.iloc[[-100]]) + with pytest.raises(IndexError, match=msg): + s.iloc[[100]] + with pytest.raises(IndexError, match=msg): + s.iloc[[-100]] # still raise on a single indexer msg = 'single positional indexer is out-of-bounds' with pytest.raises(IndexError, match=msg): df.iloc[30] - pytest.raises(IndexError, lambda: df.iloc[-30]) + with pytest.raises(IndexError, match=msg): + df.iloc[-30] # GH10779 # single positive/negative indexer exceeding Series bounds should raise # an IndexError with pytest.raises(IndexError, match=msg): s.iloc[30] - pytest.raises(IndexError, lambda: s.iloc[-30]) + with pytest.raises(IndexError, match=msg): + s.iloc[-30] # slices are ok result = df.iloc[:, 4:10] # 0 < start < len < stop @@ -104,8 +111,12 @@ def check(result, expected): check(dfl.iloc[:, 1:3], dfl.iloc[:, [1]]) check(dfl.iloc[4:6], dfl.iloc[[4]]) - pytest.raises(IndexError, lambda: dfl.iloc[[4, 5, 6]]) - pytest.raises(IndexError, lambda: dfl.iloc[:, 4]) + msg = "positional indexers are out-of-bounds" + with pytest.raises(IndexError, match=msg): + dfl.iloc[[4, 5, 6]] + msg = "single positional indexer is out-of-bounds" + with pytest.raises(IndexError, match=msg): + dfl.iloc[:, 4] def test_iloc_getitem_int(self): @@ -437,10 +448,16 @@ def test_iloc_getitem_labelled_frame(self): assert result == exp # out-of-bounds exception - pytest.raises(IndexError, df.iloc.__getitem__, tuple([10, 5])) + msg = "single positional indexer is out-of-bounds" + with pytest.raises(IndexError, match=msg): + df.iloc[10, 5] # trying to use a label - pytest.raises(ValueError, df.iloc.__getitem__, tuple(['j', 'D'])) + msg = (r"Location based indexing can only have \[integer, integer" + r" slice \(START point is INCLUDED, END point is EXCLUDED\)," + r" listlike of integers, boolean array\] types") + with pytest.raises(ValueError, match=msg): + df.iloc['j', 'D'] def test_iloc_getitem_doc_issue(self): @@ -555,10 +572,15 @@ def test_iloc_mask(self): # GH 3631, iloc with a mask (of a series) should raise df = DataFrame(lrange(5), list('ABCDE'), columns=['a']) mask = (df.a % 2 == 0) - pytest.raises(ValueError, df.iloc.__getitem__, tuple([mask])) + msg = ("iLocation based boolean indexing cannot use an indexable as" + " a mask") + with pytest.raises(ValueError, match=msg): + df.iloc[mask] mask.index = lrange(len(mask)) - pytest.raises(NotImplementedError, df.iloc.__getitem__, - tuple([mask])) + msg = ("iLocation based boolean indexing on an integer type is not" + " available") + with pytest.raises(NotImplementedError, match=msg): + df.iloc[mask] # ndarray ok result = df.iloc[np.array([True] * len(mask), dtype=bool)] diff --git a/pandas/tests/indexing/test_ix.py b/pandas/tests/indexing/test_ix.py index 35805bce07705..fb4dfbb39ce94 100644 --- a/pandas/tests/indexing/test_ix.py +++ b/pandas/tests/indexing/test_ix.py @@ -102,7 +102,12 @@ def compare(result, expected): with catch_warnings(record=True): df.ix[key] - pytest.raises(TypeError, lambda: df.loc[key]) + msg = (r"cannot do slice indexing" + r" on {klass} with these indexers \[(0|1)\] of" + r" {kind}" + .format(klass=type(df.index), kind=str(int))) + with pytest.raises(TypeError, match=msg): + df.loc[key] df = DataFrame(np.random.randn(5, 4), columns=list('ABCD'), index=pd.date_range('2012-01-01', periods=5)) @@ -122,7 +127,8 @@ def compare(result, expected): with catch_warnings(record=True): expected = df.ix[key] except KeyError: - pytest.raises(KeyError, lambda: df.loc[key]) + with pytest.raises(KeyError, match=r"^'2012-01-31'$"): + df.loc[key] continue result = df.loc[key] @@ -279,14 +285,18 @@ def test_ix_setitem_out_of_bounds_axis_0(self): np.random.randn(2, 5), index=["row%s" % i for i in range(2)], columns=["col%s" % i for i in range(5)]) with catch_warnings(record=True): - pytest.raises(ValueError, df.ix.__setitem__, (2, 0), 100) + msg = "cannot set by positional indexing with enlargement" + with pytest.raises(ValueError, match=msg): + df.ix[2, 0] = 100 def test_ix_setitem_out_of_bounds_axis_1(self): df = DataFrame( np.random.randn(5, 2), index=["row%s" % i for i in range(5)], columns=["col%s" % i for i in range(2)]) with catch_warnings(record=True): - pytest.raises(ValueError, df.ix.__setitem__, (0, 2), 100) + msg = "cannot set by positional indexing with enlargement" + with pytest.raises(ValueError, match=msg): + df.ix[0, 2] = 100 def test_ix_empty_list_indexer_is_ok(self): with catch_warnings(record=True): diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 17e107c7a1130..3bf4a6bee4af9 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -233,8 +233,10 @@ def test_loc_to_fail(self): columns=['e', 'f', 'g']) # raise a KeyError? - pytest.raises(KeyError, df.loc.__getitem__, - tuple([[1, 2], [1, 2]])) + msg = (r"\"None of \[Int64Index\(\[1, 2\], dtype='int64'\)\] are" + r" in the \[index\]\"") + with pytest.raises(KeyError, match=msg): + df.loc[[1, 2], [1, 2]] # GH 7496 # loc should not fallback @@ -243,10 +245,18 @@ def test_loc_to_fail(self): s.loc[1] = 1 s.loc['a'] = 2 - pytest.raises(KeyError, lambda: s.loc[-1]) - pytest.raises(KeyError, lambda: s.loc[[-1, -2]]) + with pytest.raises(KeyError, match=r"^-1$"): + s.loc[-1] + + msg = (r"\"None of \[Int64Index\(\[-1, -2\], dtype='int64'\)\] are" + r" in the \[index\]\"") + with pytest.raises(KeyError, match=msg): + s.loc[[-1, -2]] - pytest.raises(KeyError, lambda: s.loc[['4']]) + msg = (r"\"None of \[Index\(\[u?'4'\], dtype='object'\)\] are" + r" in the \[index\]\"") + with pytest.raises(KeyError, match=msg): + s.loc[['4']] s.loc[-1] = 3 with tm.assert_produces_warning(FutureWarning, @@ -256,29 +266,28 @@ def test_loc_to_fail(self): tm.assert_series_equal(result, expected) s['a'] = 2 - pytest.raises(KeyError, lambda: s.loc[[-2]]) + msg = (r"\"None of \[Int64Index\(\[-2\], dtype='int64'\)\] are" + r" in the \[index\]\"") + with pytest.raises(KeyError, match=msg): + s.loc[[-2]] del s['a'] - def f(): + with pytest.raises(KeyError, match=msg): s.loc[[-2]] = 0 - pytest.raises(KeyError, f) - # inconsistency between .loc[values] and .loc[values,:] # GH 7999 df = DataFrame([['a'], ['b']], index=[1, 2], columns=['value']) - def f(): + msg = (r"\"None of \[Int64Index\(\[3\], dtype='int64'\)\] are" + r" in the \[index\]\"") + with pytest.raises(KeyError, match=msg): df.loc[[3], :] - pytest.raises(KeyError, f) - - def f(): + with pytest.raises(KeyError, match=msg): df.loc[[3]] - pytest.raises(KeyError, f) - def test_loc_getitem_list_with_fail(self): # 15747 # should KeyError if *any* missing labels @@ -600,11 +609,15 @@ def test_loc_non_unique(self): # these are going to raise because the we are non monotonic df = DataFrame({'A': [1, 2, 3, 4, 5, 6], 'B': [3, 4, 5, 6, 7, 8]}, index=[0, 1, 0, 1, 2, 3]) - pytest.raises(KeyError, df.loc.__getitem__, - tuple([slice(1, None)])) - pytest.raises(KeyError, df.loc.__getitem__, - tuple([slice(0, None)])) - pytest.raises(KeyError, df.loc.__getitem__, tuple([slice(1, 2)])) + msg = "'Cannot get left slice bound for non-unique label: 1'" + with pytest.raises(KeyError, match=msg): + df.loc[1:] + msg = "'Cannot get left slice bound for non-unique label: 0'" + with pytest.raises(KeyError, match=msg): + df.loc[0:] + msg = "'Cannot get left slice bound for non-unique label: 1'" + with pytest.raises(KeyError, match=msg): + df.loc[1:2] # monotonic are ok df = DataFrame({'A': [1, 2, 3, 4, 5, 6], diff --git a/pandas/tests/indexing/test_partial.py b/pandas/tests/indexing/test_partial.py index b863afe02c2e8..5b6a5ab9ecf7b 100644 --- a/pandas/tests/indexing/test_partial.py +++ b/pandas/tests/indexing/test_partial.py @@ -246,7 +246,10 @@ def test_series_partial_set(self): tm.assert_series_equal(result, expected, check_index_type=True) # raises as nothing in in the index - pytest.raises(KeyError, lambda: ser.loc[[3, 3, 3]]) + msg = (r"\"None of \[Int64Index\(\[3, 3, 3\], dtype='int64'\)\] are" + r" in the \[index\]\"") + with pytest.raises(KeyError, match=msg): + ser.loc[[3, 3, 3]] expected = Series([0.2, 0.2, np.nan], index=[2, 2, 3]) with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): @@ -342,7 +345,10 @@ def test_series_partial_set_with_name(self): tm.assert_series_equal(result, expected, check_index_type=True) # raises as nothing in in the index - pytest.raises(KeyError, lambda: ser.loc[[3, 3, 3]]) + msg = (r"\"None of \[Int64Index\(\[3, 3, 3\], dtype='int64'," + r" name=u?'idx'\)\] are in the \[index\]\"") + with pytest.raises(KeyError, match=msg): + ser.loc[[3, 3, 3]] exp_idx = Index([2, 2, 3], dtype='int64', name='idx') expected = Series([0.2, 0.2, np.nan], index=exp_idx, name='s') diff --git a/pandas/tests/indexing/test_scalar.py b/pandas/tests/indexing/test_scalar.py index e4b8181a67514..6d607ce86c08e 100644 --- a/pandas/tests/indexing/test_scalar.py +++ b/pandas/tests/indexing/test_scalar.py @@ -30,7 +30,9 @@ def _check(f, func, values=False): for f in [d['labels'], d['ts'], d['floats']]: if f is not None: - pytest.raises(ValueError, self.check_values, f, 'iat') + msg = "iAt based indexing can only have integer indexers" + with pytest.raises(ValueError, match=msg): + self.check_values(f, 'iat') # at for f in [d['ints'], d['uints'], d['labels'], @@ -57,7 +59,9 @@ def _check(f, func, values=False): for f in [d['labels'], d['ts'], d['floats']]: if f is not None: - pytest.raises(ValueError, _check, f, 'iat') + msg = "iAt based indexing can only have integer indexers" + with pytest.raises(ValueError, match=msg): + _check(f, 'iat') # at for f in [d['ints'], d['uints'], d['labels'], @@ -107,8 +111,12 @@ def test_imethods_with_dups(self): result = s.iat[2] assert result == 2 - pytest.raises(IndexError, lambda: s.iat[10]) - pytest.raises(IndexError, lambda: s.iat[-10]) + msg = "index 10 is out of bounds for axis 0 with size 5" + with pytest.raises(IndexError, match=msg): + s.iat[10] + msg = "index -10 is out of bounds for axis 0 with size 5" + with pytest.raises(IndexError, match=msg): + s.iat[-10] result = s.iloc[[2, 3]] expected = Series([2, 3], [2, 2], dtype='int64') @@ -128,22 +136,30 @@ def test_at_to_fail(self): s = Series([1, 2, 3], index=list('abc')) result = s.at['a'] assert result == 1 - pytest.raises(ValueError, lambda: s.at[0]) + msg = ("At based indexing on an non-integer index can only have" + " non-integer indexers") + with pytest.raises(ValueError, match=msg): + s.at[0] df = DataFrame({'A': [1, 2, 3]}, index=list('abc')) result = df.at['a', 'A'] assert result == 1 - pytest.raises(ValueError, lambda: df.at['a', 0]) + with pytest.raises(ValueError, match=msg): + df.at['a', 0] s = Series([1, 2, 3], index=[3, 2, 1]) result = s.at[1] assert result == 3 - pytest.raises(ValueError, lambda: s.at['a']) + msg = ("At based indexing on an integer index can only have integer" + " indexers") + with pytest.raises(ValueError, match=msg): + s.at['a'] df = DataFrame({0: [1, 2, 3]}, index=[3, 2, 1]) result = df.at[1, 0] assert result == 3 - pytest.raises(ValueError, lambda: df.at['a', 0]) + with pytest.raises(ValueError, match=msg): + df.at['a', 0] # GH 13822, incorrect error string with non-unique columns when missing # column is accessed From bf693ffb520da3b315737a8d52595646afa74d63 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 28 Jan 2019 09:31:55 -0600 Subject: [PATCH 009/215] Fixed itertuples usage in to_dict (#24965) * Fixed itertuples usage in to_dict Closes https://github.com/pandas-dev/pandas/issues/24940 Closes https://github.com/pandas-dev/pandas/issues/24939 --- doc/source/whatsnew/v0.24.1.rst | 7 +++++++ pandas/core/frame.py | 15 +++++++++------ pandas/tests/frame/test_convert_to.py | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 3ac2ed73ea53f..de33ce64c1597 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -15,6 +15,13 @@ Whats New in 0.24.1 (February XX, 2019) These are the changes in pandas 0.24.1. See :ref:`release` for a full changelog including other versions of pandas. +.. _whatsnew_0241.regressions: + +Fixed Regressions +^^^^^^^^^^^^^^^^^ + +- Bug in :meth:`DataFrame.itertuples` with ``records`` orient raising an ``AttributeError`` when the ``DataFrame`` contained more than 255 columns (:issue:`24939`) +- Bug in :meth:`DataFrame.itertuples` orient converting integer column names to strings prepended with an underscore (:issue:`24940`) .. _whatsnew_0241.enhancements: diff --git a/pandas/core/frame.py b/pandas/core/frame.py index b4f79bda25517..28c6f3c23a3ce 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -847,7 +847,7 @@ def itertuples(self, index=True, name="Pandas"): ---------- index : bool, default True If True, return the index as the first element of the tuple. - name : str, default "Pandas" + name : str or None, default "Pandas" The name of the returned namedtuples or None to return regular tuples. @@ -1290,23 +1290,26 @@ def to_dict(self, orient='dict', into=dict): ('columns', self.columns.tolist()), ('data', [ list(map(com.maybe_box_datetimelike, t)) - for t in self.itertuples(index=False)] - ))) + for t in self.itertuples(index=False, name=None) + ]))) elif orient.lower().startswith('s'): return into_c((k, com.maybe_box_datetimelike(v)) for k, v in compat.iteritems(self)) elif orient.lower().startswith('r'): + columns = self.columns.tolist() + rows = (dict(zip(columns, row)) + for row in self.itertuples(index=False, name=None)) return [ into_c((k, com.maybe_box_datetimelike(v)) - for k, v in compat.iteritems(row._asdict())) - for row in self.itertuples(index=False)] + for k, v in compat.iteritems(row)) + for row in rows] elif orient.lower().startswith('i'): if not self.index.is_unique: raise ValueError( "DataFrame index must be unique for orient='index'." ) return into_c((t[0], dict(zip(self.columns, t[1:]))) - for t in self.itertuples()) + for t in self.itertuples(name=None)) else: raise ValueError("orient '{o}' not understood".format(o=orient)) diff --git a/pandas/tests/frame/test_convert_to.py b/pandas/tests/frame/test_convert_to.py index ddf85136126a1..7b98395dd6dec 100644 --- a/pandas/tests/frame/test_convert_to.py +++ b/pandas/tests/frame/test_convert_to.py @@ -488,3 +488,17 @@ def test_to_dict_index_dtypes(self, into, expected): result = DataFrame.from_dict(result, orient='index')[cols] expected = DataFrame.from_dict(expected, orient='index')[cols] tm.assert_frame_equal(result, expected) + + def test_to_dict_numeric_names(self): + # https://github.com/pandas-dev/pandas/issues/24940 + df = DataFrame({str(i): [i] for i in range(5)}) + result = set(df.to_dict('records')[0].keys()) + expected = set(df.columns) + assert result == expected + + def test_to_dict_wide(self): + # https://github.com/pandas-dev/pandas/issues/24939 + df = DataFrame({('A_{:d}'.format(i)): [i] for i in range(256)}) + result = df.to_dict('records')[0] + expected = {'A_{:d}'.format(i): i for i in range(256)} + assert result == expected From 3fd47fef4442a9a116e9bf37b607332551924179 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Mon, 28 Jan 2019 22:15:37 +0000 Subject: [PATCH 010/215] STY: use pytest.raises context manager (resample) (#24977) --- pandas/tests/resample/test_base.py | 9 +++- pandas/tests/resample/test_datetime_index.py | 18 ++++--- pandas/tests/resample/test_period_index.py | 39 +++++++++----- pandas/tests/resample/test_resample_api.py | 56 +++++++++++--------- pandas/tests/resample/test_time_grouper.py | 2 +- 5 files changed, 75 insertions(+), 49 deletions(-) diff --git a/pandas/tests/resample/test_base.py b/pandas/tests/resample/test_base.py index 911cd990ab881..48debfa2848e7 100644 --- a/pandas/tests/resample/test_base.py +++ b/pandas/tests/resample/test_base.py @@ -95,7 +95,10 @@ def test_resample_interpolate_all_ts(frame): def test_raises_on_non_datetimelike_index(): # this is a non datetimelike index xp = DataFrame() - pytest.raises(TypeError, lambda: xp.resample('A').mean()) + msg = ("Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex," + " but got an instance of 'Index'") + with pytest.raises(TypeError, match=msg): + xp.resample('A').mean() @pytest.mark.parametrize('freq', ['M', 'D', 'H']) @@ -189,8 +192,10 @@ def test_resample_loffset_arg_type_all_ts(frame, create_index): # GH 13022, 7687 - TODO: fix resample w/ TimedeltaIndex if isinstance(expected.index, TimedeltaIndex): - with pytest.raises(AssertionError): + msg = "DataFrame are different" + with pytest.raises(AssertionError, match=msg): assert_frame_equal(result_agg, expected) + with pytest.raises(AssertionError, match=msg): assert_frame_equal(result_how, expected) else: assert_frame_equal(result_agg, expected) diff --git a/pandas/tests/resample/test_datetime_index.py b/pandas/tests/resample/test_datetime_index.py index 73995cbe79ecd..74487052f8982 100644 --- a/pandas/tests/resample/test_datetime_index.py +++ b/pandas/tests/resample/test_datetime_index.py @@ -113,16 +113,18 @@ def test_resample_basic_grouper(series): @pytest.mark.parametrize( '_index_start,_index_end,_index_name', [('1/1/2000 00:00:00', '1/1/2000 00:13:00', 'index')]) -@pytest.mark.parametrize('kwargs', [ - dict(label='righttt'), - dict(closed='righttt'), - dict(convention='starttt') +@pytest.mark.parametrize('keyword,value', [ + ('label', 'righttt'), + ('closed', 'righttt'), + ('convention', 'starttt') ]) -def test_resample_string_kwargs(series, kwargs): +def test_resample_string_kwargs(series, keyword, value): # see gh-19303 # Check that wrong keyword argument strings raise an error - with pytest.raises(ValueError, match='Unsupported value'): - series.resample('5min', **kwargs) + msg = "Unsupported value {value} for `{keyword}`".format( + value=value, keyword=keyword) + with pytest.raises(ValueError, match=msg): + series.resample('5min', **({keyword: value})) @pytest.mark.parametrize( @@ -676,7 +678,7 @@ def test_asfreq_non_unique(): ts = Series(np.random.randn(len(rng2)), index=rng2) msg = 'cannot reindex from a duplicate axis' - with pytest.raises(Exception, match=msg): + with pytest.raises(ValueError, match=msg): ts.asfreq('B') diff --git a/pandas/tests/resample/test_period_index.py b/pandas/tests/resample/test_period_index.py index c2fbb5bbb088c..8abdf9034527b 100644 --- a/pandas/tests/resample/test_period_index.py +++ b/pandas/tests/resample/test_period_index.py @@ -11,6 +11,7 @@ import pandas as pd from pandas import DataFrame, Series, Timestamp +from pandas.core.indexes.base import InvalidIndexError from pandas.core.indexes.datetimes import date_range from pandas.core.indexes.period import Period, PeriodIndex, period_range from pandas.core.resample import _get_period_range_edges @@ -72,17 +73,19 @@ def test_asfreq_fill_value(self, series): @pytest.mark.parametrize('freq', ['H', '12H', '2D', 'W']) @pytest.mark.parametrize('kind', [None, 'period', 'timestamp']) - def test_selection(self, index, freq, kind): + @pytest.mark.parametrize('kwargs', [dict(on='date'), dict(level='d')]) + def test_selection(self, index, freq, kind, kwargs): # This is a bug, these should be implemented # GH 14008 rng = np.arange(len(index), dtype=np.int64) df = DataFrame({'date': index, 'a': rng}, index=pd.MultiIndex.from_arrays([rng, index], names=['v', 'd'])) - with pytest.raises(NotImplementedError): - df.resample(freq, on='date', kind=kind) - with pytest.raises(NotImplementedError): - df.resample(freq, level='d', kind=kind) + msg = ("Resampling from level= or on= selection with a PeriodIndex is" + r" not currently supported, use \.set_index\(\.\.\.\) to" + " explicitly set index") + with pytest.raises(NotImplementedError, match=msg): + df.resample(freq, kind=kind, **kwargs) @pytest.mark.parametrize('month', MONTHS) @pytest.mark.parametrize('meth', ['ffill', 'bfill']) @@ -110,13 +113,20 @@ def test_basic_downsample(self, simple_period_range_series): assert_series_equal(ts.resample('a-dec').mean(), result) assert_series_equal(ts.resample('a').mean(), result) - def test_not_subperiod(self, simple_period_range_series): + @pytest.mark.parametrize('rule,expected_error_msg', [ + ('a-dec', ''), + ('q-mar', ''), + ('M', ''), + ('w-thu', '') + ]) + def test_not_subperiod( + self, simple_period_range_series, rule, expected_error_msg): # These are incompatible period rules for resampling ts = simple_period_range_series('1/1/1990', '6/30/1995', freq='w-wed') - pytest.raises(ValueError, lambda: ts.resample('a-dec').mean()) - pytest.raises(ValueError, lambda: ts.resample('q-mar').mean()) - pytest.raises(ValueError, lambda: ts.resample('M').mean()) - pytest.raises(ValueError, lambda: ts.resample('w-thu').mean()) + msg = ("Frequency cannot be resampled to {}, as they" + " are not sub or super periods").format(expected_error_msg) + with pytest.raises(IncompatibleFrequency, match=msg): + ts.resample(rule).mean() @pytest.mark.parametrize('freq', ['D', '2D']) def test_basic_upsample(self, freq, simple_period_range_series): @@ -212,8 +222,9 @@ def test_resample_same_freq(self, resample_method): assert_series_equal(result, expected) def test_resample_incompat_freq(self): - - with pytest.raises(IncompatibleFrequency): + msg = ("Frequency cannot be resampled to ," + " as they are not sub or super periods") + with pytest.raises(IncompatibleFrequency, match=msg): Series(range(3), index=pd.period_range( start='2000', periods=3, freq='M')).resample('W').mean() @@ -373,7 +384,9 @@ def test_resample_fill_missing(self): def test_cant_fill_missing_dups(self): rng = PeriodIndex([2000, 2005, 2005, 2007, 2007], freq='A') s = Series(np.random.randn(5), index=rng) - pytest.raises(Exception, lambda: s.resample('A').ffill()) + msg = "Reindexing only valid with uniquely valued Index objects" + with pytest.raises(InvalidIndexError, match=msg): + s.resample('A').ffill() @pytest.mark.parametrize('freq', ['5min']) @pytest.mark.parametrize('kind', ['period', None, 'timestamp']) diff --git a/pandas/tests/resample/test_resample_api.py b/pandas/tests/resample/test_resample_api.py index 69684daf05f3d..a694942cc4c40 100644 --- a/pandas/tests/resample/test_resample_api.py +++ b/pandas/tests/resample/test_resample_api.py @@ -113,16 +113,14 @@ def test_getitem(): test_frame.columns[[0, 1]]) -def test_select_bad_cols(): - +@pytest.mark.parametrize('key', [['D'], ['A', 'D']]) +def test_select_bad_cols(key): g = test_frame.resample('H') - pytest.raises(KeyError, g.__getitem__, ['D']) - - pytest.raises(KeyError, g.__getitem__, ['A', 'D']) - with pytest.raises(KeyError, match='^[^A]+$'): - # A should not be referenced as a bad column... - # will have to rethink regex if you change message! - g[['A', 'D']] + # 'A' should not be referenced as a bad column... + # will have to rethink regex if you change message! + msg = r"^\"Columns not found: 'D'\"$" + with pytest.raises(KeyError, match=msg): + g[key] def test_attribute_access(): @@ -216,7 +214,9 @@ def test_fillna(): result = r.fillna(method='bfill') assert_series_equal(result, expected) - with pytest.raises(ValueError): + msg = (r"Invalid fill method\. Expecting pad \(ffill\), backfill" + r" \(bfill\) or nearest\. Got 0") + with pytest.raises(ValueError, match=msg): r.fillna(0) @@ -437,12 +437,11 @@ def test_agg_misc(): # errors # invalid names in the agg specification + msg = "\"Column 'B' does not exist!\"" for t in cases: - with pytest.raises(KeyError): - with tm.assert_produces_warning(FutureWarning, - check_stacklevel=False): - t[['A']].agg({'A': ['sum', 'std'], - 'B': ['mean', 'std']}) + with pytest.raises(KeyError, match=msg): + t[['A']].agg({'A': ['sum', 'std'], + 'B': ['mean', 'std']}) def test_agg_nested_dicts(): @@ -464,11 +463,11 @@ def test_agg_nested_dicts(): df.groupby(pd.Grouper(freq='2D')) ] + msg = r"cannot perform renaming for r(1|2) with a nested dictionary" for t in cases: - def f(): + with pytest.raises(pd.core.base.SpecificationError, match=msg): t.aggregate({'r1': {'A': ['mean', 'sum']}, 'r2': {'B': ['mean', 'sum']}}) - pytest.raises(ValueError, f) for t in cases: expected = pd.concat([t['A'].mean(), t['A'].std(), t['B'].mean(), @@ -499,7 +498,8 @@ def test_try_aggregate_non_existing_column(): df = DataFrame(data).set_index('dt') # Error as we don't have 'z' column - with pytest.raises(KeyError): + msg = "\"Column 'z' does not exist!\"" + with pytest.raises(KeyError, match=msg): df.resample('30T').agg({'x': ['mean'], 'y': ['median'], 'z': ['sum']}) @@ -517,23 +517,29 @@ def test_selection_api_validation(): df_exp = DataFrame({'a': rng}, index=index) # non DatetimeIndex - with pytest.raises(TypeError): + msg = ("Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex," + " but got an instance of 'Int64Index'") + with pytest.raises(TypeError, match=msg): df.resample('2D', level='v') - with pytest.raises(ValueError): + msg = "The Grouper cannot specify both a key and a level!" + with pytest.raises(ValueError, match=msg): df.resample('2D', on='date', level='d') - with pytest.raises(TypeError): + msg = "unhashable type: 'list'" + with pytest.raises(TypeError, match=msg): df.resample('2D', on=['a', 'date']) - with pytest.raises(KeyError): + msg = r"\"Level \['a', 'date'\] not found\"" + with pytest.raises(KeyError, match=msg): df.resample('2D', level=['a', 'date']) # upsampling not allowed - with pytest.raises(ValueError): + msg = ("Upsampling from level= or on= selection is not supported, use" + r" \.set_index\(\.\.\.\) to explicitly set index to datetime-like") + with pytest.raises(ValueError, match=msg): df.resample('2D', level='d').asfreq() - - with pytest.raises(ValueError): + with pytest.raises(ValueError, match=msg): df.resample('2D', on='date').asfreq() exp = df_exp.resample('2D').sum() diff --git a/pandas/tests/resample/test_time_grouper.py b/pandas/tests/resample/test_time_grouper.py index ec29b55ac9d67..a4eb7933738c0 100644 --- a/pandas/tests/resample/test_time_grouper.py +++ b/pandas/tests/resample/test_time_grouper.py @@ -112,7 +112,7 @@ def test_fails_on_no_datetime_index(name, func): df = DataFrame({'a': np.random.randn(n)}, index=index) msg = ("Only valid with DatetimeIndex, TimedeltaIndex " - "or PeriodIndex, but got an instance of %r" % name) + "or PeriodIndex, but got an instance of '{}'".format(name)) with pytest.raises(TypeError, match=msg): df.groupby(TimeGrouper('D')) From ba7b895cd7505add274cdd70f62b645e0eb0fa8e Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 29 Jan 2019 06:22:40 -0600 Subject: [PATCH 011/215] DOC: Document breaking change to read_csv (#24989) --- doc/source/user_guide/io.rst | 30 +++++++++++++++++++++ doc/source/whatsnew/v0.24.0.rst | 46 +++++++++++++++++++++++++++++++++ pandas/io/parsers.py | 11 +++++--- 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/doc/source/user_guide/io.rst b/doc/source/user_guide/io.rst index 58e1b2370c7c8..b23a0f10e9e2b 100644 --- a/doc/source/user_guide/io.rst +++ b/doc/source/user_guide/io.rst @@ -989,6 +989,36 @@ a single date rather than the entire array. os.remove('tmp.csv') + +.. _io.csv.mixed_timezones: + +Parsing a CSV with mixed Timezones +++++++++++++++++++++++++++++++++++ + +Pandas cannot natively represent a column or index with mixed timezones. If your CSV +file contains columns with a mixture of timezones, the default result will be +an object-dtype column with strings, even with ``parse_dates``. + + +.. ipython:: python + + content = """\ + a + 2000-01-01T00:00:00+05:00 + 2000-01-01T00:00:00+06:00""" + df = pd.read_csv(StringIO(content), parse_dates=['a']) + df['a'] + +To parse the mixed-timezone values as a datetime column, pass a partially-applied +:func:`to_datetime` with ``utc=True`` as the ``date_parser``. + +.. ipython:: python + + df = pd.read_csv(StringIO(content), parse_dates=['a'], + date_parser=lambda col: pd.to_datetime(col, utc=True)) + df['a'] + + .. _io.dayfirst: diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 16319a3b83ca4..a49ea2cf493a6 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -648,6 +648,52 @@ that the dates have been converted to UTC pd.to_datetime(["2015-11-18 15:30:00+05:30", "2015-11-18 16:30:00+06:30"], utc=True) + +.. _whatsnew_0240.api_breaking.read_csv_mixed_tz: + +Parsing mixed-timezones with :func:`read_csv` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:func:`read_csv` no longer silently converts mixed-timezone columns to UTC (:issue:`24987`). + +*Previous Behavior* + +.. code-block:: python + + >>> import io + >>> content = """\ + ... a + ... 2000-01-01T00:00:00+05:00 + ... 2000-01-01T00:00:00+06:00""" + >>> df = pd.read_csv(io.StringIO(content), parse_dates=['a']) + >>> df.a + 0 1999-12-31 19:00:00 + 1 1999-12-31 18:00:00 + Name: a, dtype: datetime64[ns] + +*New Behavior* + +.. ipython:: python + + import io + content = """\ + a + 2000-01-01T00:00:00+05:00 + 2000-01-01T00:00:00+06:00""" + df = pd.read_csv(io.StringIO(content), parse_dates=['a']) + df.a + +As can be seen, the ``dtype`` is object; each value in the column is a string. +To convert the strings to an array of datetimes, the ``date_parser`` argument + +.. ipython:: python + + df = pd.read_csv(io.StringIO(content), parse_dates=['a'], + date_parser=lambda col: pd.to_datetime(col, utc=True)) + df.a + +See :ref:`whatsnew_0240.api.timezone_offset_parsing` for more. + .. _whatsnew_0240.api_breaking.period_end_time: Time values in ``dt.end_time`` and ``to_timestamp(how='end')`` diff --git a/pandas/io/parsers.py b/pandas/io/parsers.py index b31d3f665f47f..4163a571df800 100755 --- a/pandas/io/parsers.py +++ b/pandas/io/parsers.py @@ -203,9 +203,14 @@ * dict, e.g. {{'foo' : [1, 3]}} -> parse columns 1, 3 as date and call result 'foo' - If a column or index contains an unparseable date, the entire column or - index will be returned unaltered as an object data type. For non-standard - datetime parsing, use ``pd.to_datetime`` after ``pd.read_csv`` + If a column or index cannot be represented as an array of datetimes, + say because of an unparseable value or a mixture of timezones, the column + or index will be returned unaltered as an object data type. For + non-standard datetime parsing, use ``pd.to_datetime`` after + ``pd.read_csv``. To parse an index or column with a mixture of timezones, + specify ``date_parser`` to be a partially-applied + :func:`pandas.to_datetime` with ``utc=True``. See + :ref:`io.csv.mixed_timezones` for more. Note: A fast-path exists for iso8601-formatted dates. infer_datetime_format : bool, default False From dd16aa7df49ba094ee77cf34605fc46836414ae9 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 29 Jan 2019 06:23:32 -0600 Subject: [PATCH 012/215] DEPR: Fixed warning for implicit registration (#24964) --- doc/source/whatsnew/v0.24.1.rst | 5 +++++ pandas/plotting/_core.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index de33ce64c1597..78eba1fe5d025 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -74,6 +74,11 @@ Bug Fixes - Bug in :func:`merge` when merging by index name would sometimes result in an incorrectly numbered index (:issue:`24212`) +**Visualization** + +- Fixed the warning for implicitly registered matplotlib converters not showing. See :ref:`whatsnew_0211.converters` for more (:issue:`24963`). + + **Other** - diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index e543ab88f53b2..85549bafa8dc0 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -39,7 +39,7 @@ else: _HAS_MPL = True if get_option('plotting.matplotlib.register_converters'): - _converter.register(explicit=True) + _converter.register(explicit=False) def _raise_if_no_mpl(): From 145ade2a2f011dbfbe531347fd0dc563ee0b5642 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Tue, 29 Jan 2019 12:37:42 +0000 Subject: [PATCH 013/215] STY: use pytest.raises context manager (indexes/datetimes) (#24995) --- pandas/core/arrays/datetimes.py | 2 +- pandas/tests/indexes/common.py | 14 ++- .../indexes/datetimes/test_construction.py | 51 +++++++---- .../indexes/datetimes/test_date_range.py | 6 +- pandas/tests/indexes/datetimes/test_misc.py | 4 +- pandas/tests/indexes/datetimes/test_ops.py | 8 +- .../indexes/datetimes/test_partial_slicing.py | 32 ++++--- .../indexes/datetimes/test_scalar_compat.py | 24 +++-- pandas/tests/indexes/datetimes/test_tools.py | 91 ++++++++++++------- 9 files changed, 151 insertions(+), 81 deletions(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index d7a8417a71be2..f9aef2401a4d3 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -2058,7 +2058,7 @@ def validate_tz_from_dtype(dtype, tz): # tz-naive dtype (i.e. datetime64[ns]) if tz is not None and not timezones.tz_compare(tz, dtz): raise ValueError("cannot supply both a tz and a " - "timezone-naive dtype (i.e. datetime64[ns]") + "timezone-naive dtype (i.e. datetime64[ns])") return tz diff --git a/pandas/tests/indexes/common.py b/pandas/tests/indexes/common.py index 499f01f0e7f7b..d72dccadf0ac0 100644 --- a/pandas/tests/indexes/common.py +++ b/pandas/tests/indexes/common.py @@ -30,7 +30,12 @@ def setup_indices(self): def test_pickle_compat_construction(self): # need an object to create with - pytest.raises(TypeError, self._holder) + msg = (r"Index\(\.\.\.\) must be called with a collection of some" + r" kind, None was passed|" + r"__new__\(\) missing 1 required positional argument: 'data'|" + r"__new__\(\) takes at least 2 arguments \(1 given\)") + with pytest.raises(TypeError, match=msg): + self._holder() def test_to_series(self): # assert that we are creating a copy of the index @@ -84,8 +89,11 @@ def test_shift(self): # GH8083 test the base class for shift idx = self.create_index() - pytest.raises(NotImplementedError, idx.shift, 1) - pytest.raises(NotImplementedError, idx.shift, 1, 2) + msg = "Not supported for type {}".format(type(idx).__name__) + with pytest.raises(NotImplementedError, match=msg): + idx.shift(1) + with pytest.raises(NotImplementedError, match=msg): + idx.shift(1, 2) def test_create_index_existing_name(self): diff --git a/pandas/tests/indexes/datetimes/test_construction.py b/pandas/tests/indexes/datetimes/test_construction.py index 7ebebbf6dee28..6893f635c82ac 100644 --- a/pandas/tests/indexes/datetimes/test_construction.py +++ b/pandas/tests/indexes/datetimes/test_construction.py @@ -135,8 +135,10 @@ def test_construction_with_alt_tz_localize(self, kwargs, tz_aware_fixture): tm.assert_index_equal(i2, expected) # incompat tz/dtype - pytest.raises(ValueError, lambda: DatetimeIndex( - i.tz_localize(None).asi8, dtype=i.dtype, tz='US/Pacific')) + msg = "cannot supply both a tz and a dtype with a tz" + with pytest.raises(ValueError, match=msg): + DatetimeIndex(i.tz_localize(None).asi8, + dtype=i.dtype, tz='US/Pacific') def test_construction_index_with_mixed_timezones(self): # gh-11488: no tz results in DatetimeIndex @@ -439,14 +441,19 @@ def test_constructor_coverage(self): tm.assert_index_equal(from_ints, expected) # non-conforming - pytest.raises(ValueError, DatetimeIndex, - ['2000-01-01', '2000-01-02', '2000-01-04'], freq='D') + msg = ("Inferred frequency None from passed values does not conform" + " to passed frequency D") + with pytest.raises(ValueError, match=msg): + DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-04'], freq='D') - pytest.raises(ValueError, date_range, start='2011-01-01', - freq='b') - pytest.raises(ValueError, date_range, end='2011-01-01', - freq='B') - pytest.raises(ValueError, date_range, periods=10, freq='D') + msg = ("Of the four parameters: start, end, periods, and freq, exactly" + " three must be specified") + with pytest.raises(ValueError, match=msg): + date_range(start='2011-01-01', freq='b') + with pytest.raises(ValueError, match=msg): + date_range(end='2011-01-01', freq='B') + with pytest.raises(ValueError, match=msg): + date_range(periods=10, freq='D') @pytest.mark.parametrize('freq', ['AS', 'W-SUN']) def test_constructor_datetime64_tzformat(self, freq): @@ -511,18 +518,20 @@ def test_constructor_dtype(self): idx = DatetimeIndex(['2013-01-01', '2013-01-02'], dtype='datetime64[ns, US/Eastern]') - pytest.raises(ValueError, - lambda: DatetimeIndex(idx, - dtype='datetime64[ns]')) + msg = ("cannot supply both a tz and a timezone-naive dtype" + r" \(i\.e\. datetime64\[ns\]\)") + with pytest.raises(ValueError, match=msg): + DatetimeIndex(idx, dtype='datetime64[ns]') # this is effectively trying to convert tz's - pytest.raises(TypeError, - lambda: DatetimeIndex(idx, - dtype='datetime64[ns, CET]')) - pytest.raises(ValueError, - lambda: DatetimeIndex( - idx, tz='CET', - dtype='datetime64[ns, US/Eastern]')) + msg = ("data is already tz-aware US/Eastern, unable to set specified" + " tz: CET") + with pytest.raises(TypeError, match=msg): + DatetimeIndex(idx, dtype='datetime64[ns, CET]') + msg = "cannot supply both a tz and a dtype with a tz" + with pytest.raises(ValueError, match=msg): + DatetimeIndex(idx, tz='CET', dtype='datetime64[ns, US/Eastern]') + result = DatetimeIndex(idx, dtype='datetime64[ns, US/Eastern]') tm.assert_index_equal(idx, result) @@ -732,7 +741,9 @@ def test_from_freq_recreate_from_data(self, freq): def test_datetimeindex_constructor_misc(self): arr = ['1/1/2005', '1/2/2005', 'Jn 3, 2005', '2005-01-04'] - pytest.raises(Exception, DatetimeIndex, arr) + msg = r"(\(u?')?Unknown string format(:', 'Jn 3, 2005'\))?" + with pytest.raises(ValueError, match=msg): + DatetimeIndex(arr) arr = ['1/1/2005', '1/2/2005', '1/3/2005', '2005-01-04'] idx1 = DatetimeIndex(arr) diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index a9bece248e9d0..a38ee264d362c 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -346,8 +346,10 @@ def test_compat_replace(self, f): def test_catch_infinite_loop(self): offset = offsets.DateOffset(minute=5) # blow up, don't loop forever - pytest.raises(Exception, date_range, datetime(2011, 11, 11), - datetime(2011, 11, 12), freq=offset) + msg = "Offset did not increment date" + with pytest.raises(ValueError, match=msg): + date_range(datetime(2011, 11, 11), datetime(2011, 11, 12), + freq=offset) @pytest.mark.parametrize('periods', (1, 2)) def test_wom_len(self, periods): diff --git a/pandas/tests/indexes/datetimes/test_misc.py b/pandas/tests/indexes/datetimes/test_misc.py index cec181161fc11..fc6080e68a803 100644 --- a/pandas/tests/indexes/datetimes/test_misc.py +++ b/pandas/tests/indexes/datetimes/test_misc.py @@ -190,7 +190,9 @@ def test_datetimeindex_accessors(self): # Ensure is_start/end accessors throw ValueError for CustomBusinessDay, bday_egypt = offsets.CustomBusinessDay(weekmask='Sun Mon Tue Wed Thu') dti = date_range(datetime(2013, 4, 30), periods=5, freq=bday_egypt) - pytest.raises(ValueError, lambda: dti.is_month_start) + msg = "Custom business days is not supported by is_month_start" + with pytest.raises(ValueError, match=msg): + dti.is_month_start dti = DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-03']) diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py index 2a546af79931e..84085141fcf92 100644 --- a/pandas/tests/indexes/datetimes/test_ops.py +++ b/pandas/tests/indexes/datetimes/test_ops.py @@ -37,15 +37,19 @@ def test_ops_properties_basic(self): # sanity check that the behavior didn't change # GH#7206 + msg = "'Series' object has no attribute '{}'" for op in ['year', 'day', 'second', 'weekday']: - pytest.raises(TypeError, lambda x: getattr(self.dt_series, op)) + with pytest.raises(AttributeError, match=msg.format(op)): + getattr(self.dt_series, op) # attribute access should still work! s = Series(dict(year=2000, month=1, day=10)) assert s.year == 2000 assert s.month == 1 assert s.day == 10 - pytest.raises(AttributeError, lambda: s.weekday) + msg = "'Series' object has no attribute 'weekday'" + with pytest.raises(AttributeError, match=msg): + s.weekday def test_repeat_range(self, tz_naive_fixture): tz = tz_naive_fixture diff --git a/pandas/tests/indexes/datetimes/test_partial_slicing.py b/pandas/tests/indexes/datetimes/test_partial_slicing.py index 1b2aab9d370a3..a0c9d9f02385c 100644 --- a/pandas/tests/indexes/datetimes/test_partial_slicing.py +++ b/pandas/tests/indexes/datetimes/test_partial_slicing.py @@ -170,7 +170,8 @@ def test_partial_slice(self): result = s['2005-1-1'] assert result == s.iloc[0] - pytest.raises(Exception, s.__getitem__, '2004-12-31') + with pytest.raises(KeyError, match=r"^'2004-12-31'$"): + s['2004-12-31'] def test_partial_slice_daily(self): rng = date_range(freq='H', start=datetime(2005, 1, 31), periods=500) @@ -179,7 +180,8 @@ def test_partial_slice_daily(self): result = s['2005-1-31'] tm.assert_series_equal(result, s.iloc[:24]) - pytest.raises(Exception, s.__getitem__, '2004-12-31 00') + with pytest.raises(KeyError, match=r"^'2004-12-31 00'$"): + s['2004-12-31 00'] def test_partial_slice_hourly(self): rng = date_range(freq='T', start=datetime(2005, 1, 1, 20, 0, 0), @@ -193,7 +195,8 @@ def test_partial_slice_hourly(self): tm.assert_series_equal(result, s.iloc[:60]) assert s['2005-1-1 20:00'] == s.iloc[0] - pytest.raises(Exception, s.__getitem__, '2004-12-31 00:15') + with pytest.raises(KeyError, match=r"^'2004-12-31 00:15'$"): + s['2004-12-31 00:15'] def test_partial_slice_minutely(self): rng = date_range(freq='S', start=datetime(2005, 1, 1, 23, 59, 0), @@ -207,7 +210,8 @@ def test_partial_slice_minutely(self): tm.assert_series_equal(result, s.iloc[:60]) assert s[Timestamp('2005-1-1 23:59:00')] == s.iloc[0] - pytest.raises(Exception, s.__getitem__, '2004-12-31 00:00:00') + with pytest.raises(KeyError, match=r"^'2004-12-31 00:00:00'$"): + s['2004-12-31 00:00:00'] def test_partial_slice_second_precision(self): rng = date_range(start=datetime(2005, 1, 1, 0, 0, 59, @@ -255,7 +259,9 @@ def test_partial_slicing_dataframe(self): result = df['a'][ts_string] assert isinstance(result, np.int64) assert result == expected - pytest.raises(KeyError, df.__getitem__, ts_string) + msg = r"^'{}'$".format(ts_string) + with pytest.raises(KeyError, match=msg): + df[ts_string] # Timestamp with resolution less precise than index for fmt in formats[:rnum]: @@ -282,15 +288,20 @@ def test_partial_slicing_dataframe(self): result = df['a'][ts_string] assert isinstance(result, np.int64) assert result == 2 - pytest.raises(KeyError, df.__getitem__, ts_string) + msg = r"^'{}'$".format(ts_string) + with pytest.raises(KeyError, match=msg): + df[ts_string] # Not compatible with existing key # Should raise KeyError for fmt, res in list(zip(formats, resolutions))[rnum + 1:]: ts = index[1] + Timedelta("1 " + res) ts_string = ts.strftime(fmt) - pytest.raises(KeyError, df['a'].__getitem__, ts_string) - pytest.raises(KeyError, df.__getitem__, ts_string) + msg = r"^'{}'$".format(ts_string) + with pytest.raises(KeyError, match=msg): + df['a'][ts_string] + with pytest.raises(KeyError, match=msg): + df[ts_string] def test_partial_slicing_with_multiindex(self): @@ -316,11 +327,10 @@ def test_partial_slicing_with_multiindex(self): # this is an IndexingError as we don't do partial string selection on # multi-levels. - def f(): + msg = "Too many indexers" + with pytest.raises(IndexingError, match=msg): df_multi.loc[('2013-06-19', 'ACCT1', 'ABC')] - pytest.raises(IndexingError, f) - # GH 4294 # partial slice on a series mi s = pd.DataFrame(np.random.rand(1000, 1000), index=pd.date_range( diff --git a/pandas/tests/indexes/datetimes/test_scalar_compat.py b/pandas/tests/indexes/datetimes/test_scalar_compat.py index 680eddd27cf9f..42338a751e0fc 100644 --- a/pandas/tests/indexes/datetimes/test_scalar_compat.py +++ b/pandas/tests/indexes/datetimes/test_scalar_compat.py @@ -7,6 +7,8 @@ import numpy as np import pytest +from pandas._libs.tslibs.np_datetime import OutOfBoundsDatetime + import pandas as pd from pandas import DatetimeIndex, Timestamp, date_range import pandas.util.testing as tm @@ -27,10 +29,14 @@ def test_dti_date(self): expected = [t.date() for t in rng] assert (result == expected).all() - def test_dti_date_out_of_range(self): + @pytest.mark.parametrize('data', [ + ['1400-01-01'], + [datetime(1400, 1, 1)]]) + def test_dti_date_out_of_range(self, data): # GH#1475 - pytest.raises(ValueError, DatetimeIndex, ['1400-01-01']) - pytest.raises(ValueError, DatetimeIndex, [datetime(1400, 1, 1)]) + msg = "Out of bounds nanosecond timestamp: 1400-01-01 00:00:00" + with pytest.raises(OutOfBoundsDatetime, match=msg): + DatetimeIndex(data) @pytest.mark.parametrize('field', [ 'dayofweek', 'dayofyear', 'week', 'weekofyear', 'quarter', @@ -74,9 +80,15 @@ def test_round_daily(self): result = dti.round('s') tm.assert_index_equal(result, dti) - # invalid - for freq in ['Y', 'M', 'foobar']: - pytest.raises(ValueError, lambda: dti.round(freq)) + @pytest.mark.parametrize('freq, error_msg', [ + ('Y', ' is a non-fixed frequency'), + ('M', ' is a non-fixed frequency'), + ('foobar', 'Invalid frequency: foobar')]) + def test_round_invalid(self, freq, error_msg): + dti = date_range('20130101 09:10:11', periods=5) + dti = dti.tz_localize('UTC').tz_convert('US/Eastern') + with pytest.raises(ValueError, match=error_msg): + dti.round(freq) def test_round(self, tz_naive_fixture): tz = tz_naive_fixture diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index bec2fa66c43cd..38f5eab15041f 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -346,12 +346,16 @@ def test_to_datetime_dt64s(self, cache): for dt in in_bound_dts: assert pd.to_datetime(dt, cache=cache) == Timestamp(dt) - oob_dts = [np.datetime64('1000-01-01'), np.datetime64('5000-01-02'), ] - - for dt in oob_dts: - pytest.raises(ValueError, pd.to_datetime, dt, errors='raise') - pytest.raises(ValueError, Timestamp, dt) - assert pd.to_datetime(dt, errors='coerce', cache=cache) is NaT + @pytest.mark.parametrize('dt', [np.datetime64('1000-01-01'), + np.datetime64('5000-01-02')]) + @pytest.mark.parametrize('cache', [True, False]) + def test_to_datetime_dt64s_out_of_bounds(self, cache, dt): + msg = "Out of bounds nanosecond timestamp: {}".format(dt) + with pytest.raises(OutOfBoundsDatetime, match=msg): + pd.to_datetime(dt, errors='raise') + with pytest.raises(OutOfBoundsDatetime, match=msg): + Timestamp(dt) + assert pd.to_datetime(dt, errors='coerce', cache=cache) is NaT @pytest.mark.parametrize('cache', [True, False]) def test_to_datetime_array_of_dt64s(self, cache): @@ -367,8 +371,9 @@ def test_to_datetime_array_of_dt64s(self, cache): # A list of datetimes where the last one is out of bounds dts_with_oob = dts + [np.datetime64('9999-01-01')] - pytest.raises(ValueError, pd.to_datetime, dts_with_oob, - errors='raise') + msg = "Out of bounds nanosecond timestamp: 9999-01-01 00:00:00" + with pytest.raises(OutOfBoundsDatetime, match=msg): + pd.to_datetime(dts_with_oob, errors='raise') tm.assert_numpy_array_equal( pd.to_datetime(dts_with_oob, box=False, errors='coerce', @@ -410,7 +415,10 @@ def test_to_datetime_tz(self, cache): # mixed tzs will raise arr = [pd.Timestamp('2013-01-01 13:00:00', tz='US/Pacific'), pd.Timestamp('2013-01-02 14:00:00', tz='US/Eastern')] - pytest.raises(ValueError, lambda: pd.to_datetime(arr, cache=cache)) + msg = ("Tz-aware datetime.datetime cannot be converted to datetime64" + " unless utc=True") + with pytest.raises(ValueError, match=msg): + pd.to_datetime(arr, cache=cache) @pytest.mark.parametrize('cache', [True, False]) def test_to_datetime_tz_pytz(self, cache): @@ -1088,9 +1096,9 @@ def test_to_datetime_on_datetime64_series(self, cache): def test_to_datetime_with_space_in_series(self, cache): # GH 6428 s = Series(['10/18/2006', '10/18/2008', ' ']) - pytest.raises(ValueError, lambda: to_datetime(s, - errors='raise', - cache=cache)) + msg = r"(\(u?')?String does not contain a date(:', ' '\))?" + with pytest.raises(ValueError, match=msg): + to_datetime(s, errors='raise', cache=cache) result_coerce = to_datetime(s, errors='coerce', cache=cache) expected_coerce = Series([datetime(2006, 10, 18), datetime(2008, 10, 18), @@ -1111,13 +1119,12 @@ def test_to_datetime_with_apply(self, cache): assert_series_equal(result, expected) td = pd.Series(['May 04', 'Jun 02', ''], index=[1, 2, 3]) - pytest.raises(ValueError, - lambda: pd.to_datetime(td, format='%b %y', - errors='raise', - cache=cache)) - pytest.raises(ValueError, - lambda: td.apply(pd.to_datetime, format='%b %y', - errors='raise', cache=cache)) + msg = r"time data '' does not match format '%b %y' \(match\)" + with pytest.raises(ValueError, match=msg): + pd.to_datetime(td, format='%b %y', errors='raise', cache=cache) + with pytest.raises(ValueError, match=msg): + td.apply(pd.to_datetime, format='%b %y', + errors='raise', cache=cache) expected = pd.to_datetime(td, format='%b %y', errors='coerce', cache=cache) @@ -1168,8 +1175,9 @@ def test_to_datetime_unprocessable_input(self, cache, box, klass): result = to_datetime([1, '1'], errors='ignore', cache=cache, box=box) expected = klass(np.array([1, '1'], dtype='O')) tm.assert_equal(result, expected) - pytest.raises(TypeError, to_datetime, [1, '1'], errors='raise', - cache=cache, box=box) + msg = "invalid string coercion to datetime" + with pytest.raises(TypeError, match=msg): + to_datetime([1, '1'], errors='raise', cache=cache, box=box) def test_to_datetime_other_datetime64_units(self): # 5/25/2012 @@ -1225,17 +1233,18 @@ def test_string_na_nat_conversion(self, cache): malformed = np.array(['1/100/2000', np.nan], dtype=object) # GH 10636, default is now 'raise' - pytest.raises(ValueError, - lambda: to_datetime(malformed, errors='raise', - cache=cache)) + msg = (r"\(u?'Unknown string format:', '1/100/2000'\)|" + "day is out of range for month") + with pytest.raises(ValueError, match=msg): + to_datetime(malformed, errors='raise', cache=cache) result = to_datetime(malformed, errors='ignore', cache=cache) # GH 21864 expected = Index(malformed) tm.assert_index_equal(result, expected) - pytest.raises(ValueError, to_datetime, malformed, errors='raise', - cache=cache) + with pytest.raises(ValueError, match=msg): + to_datetime(malformed, errors='raise', cache=cache) idx = ['a', 'b', 'c', 'd', 'e'] series = Series(['1/1/2000', np.nan, '1/3/2000', np.nan, @@ -1414,14 +1423,24 @@ def test_day_not_in_month_coerce(self, cache): @pytest.mark.parametrize('cache', [True, False]) def test_day_not_in_month_raise(self, cache): - pytest.raises(ValueError, to_datetime, '2015-02-29', - errors='raise', cache=cache) - pytest.raises(ValueError, to_datetime, '2015-02-29', - errors='raise', format="%Y-%m-%d", cache=cache) - pytest.raises(ValueError, to_datetime, '2015-02-32', - errors='raise', format="%Y-%m-%d", cache=cache) - pytest.raises(ValueError, to_datetime, '2015-04-31', - errors='raise', format="%Y-%m-%d", cache=cache) + msg = "day is out of range for month" + with pytest.raises(ValueError, match=msg): + to_datetime('2015-02-29', errors='raise', cache=cache) + + msg = "time data 2015-02-29 doesn't match format specified" + with pytest.raises(ValueError, match=msg): + to_datetime('2015-02-29', errors='raise', format="%Y-%m-%d", + cache=cache) + + msg = "time data 2015-02-32 doesn't match format specified" + with pytest.raises(ValueError, match=msg): + to_datetime('2015-02-32', errors='raise', format="%Y-%m-%d", + cache=cache) + + msg = "time data 2015-04-31 doesn't match format specified" + with pytest.raises(ValueError, match=msg): + to_datetime('2015-04-31', errors='raise', format="%Y-%m-%d", + cache=cache) @pytest.mark.parametrize('cache', [True, False]) def test_day_not_in_month_ignore(self, cache): @@ -1656,7 +1675,9 @@ def test_parsers_time(self): assert tools.to_time(time_string) == expected new_string = "14.15" - pytest.raises(ValueError, tools.to_time, new_string) + msg = r"Cannot convert arg \['14\.15'\] to a time" + with pytest.raises(ValueError, match=msg): + tools.to_time(new_string) assert tools.to_time(new_string, format="%H.%M") == expected arg = ["14:15", "20:20"] From 6449bc15758325bd753bbb13160ee825197023c3 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 29 Jan 2019 14:24:53 +0100 Subject: [PATCH 014/215] DOC: move whatsnew note of #24916 (#24999) --- doc/source/whatsnew/v0.24.1.rst | 3 ++- doc/source/whatsnew/v0.25.0.rst | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 78eba1fe5d025..85a2ba5bb03b6 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -72,7 +72,8 @@ Bug Fixes **Reshaping** -- Bug in :func:`merge` when merging by index name would sometimes result in an incorrectly numbered index (:issue:`24212`) +- +- **Visualization** diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 5129449e4fdf3..d0ddb6e09d555 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -179,7 +179,7 @@ Groupby/Resample/Rolling Reshaping ^^^^^^^^^ -- +- Bug in :func:`merge` when merging by index name would sometimes result in an incorrectly numbered index (:issue:`24212`) - - From 1fc88c7c6a5b1fa3310c2d80321aa895954d220d Mon Sep 17 00:00:00 2001 From: Yoann Goular <22172257+yoanngoular@users.noreply.github.com> Date: Tue, 29 Jan 2019 16:09:37 +0100 Subject: [PATCH 015/215] BUG: Fix broken links (#25002) The previous location of contributing.rst file was /doc/source/contributing.rst but has been moved to /doc/source/development/contributing.rst --- .github/CONTRIBUTING.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 21df1a3aacd59..faff68b636109 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -8,16 +8,16 @@ Our main contributing guide can be found [in this repo](https://github.com/panda If you are looking to contribute to the *pandas* codebase, the best place to start is the [GitHub "issues" tab](https://github.com/pandas-dev/pandas/issues). This is also a great place for filing bug reports and making suggestions for ways in which we can improve the code and documentation. -If you have additional questions, feel free to ask them on the [mailing list](https://groups.google.com/forum/?fromgroups#!forum/pydata) or on [Gitter](https://gitter.im/pydata/pandas). Further information can also be found in the "[Where to start?](https://github.com/pandas-dev/pandas/blob/master/doc/source/contributing.rst#where-to-start)" section. +If you have additional questions, feel free to ask them on the [mailing list](https://groups.google.com/forum/?fromgroups#!forum/pydata) or on [Gitter](https://gitter.im/pydata/pandas). Further information can also be found in the "[Where to start?](https://github.com/pandas-dev/pandas/blob/master/doc/source/development/contributing.rst#where-to-start)" section. ## Filing Issues -If you notice a bug in the code or documentation, or have suggestions for how we can improve either, feel free to create an issue on the [GitHub "issues" tab](https://github.com/pandas-dev/pandas/issues) using [GitHub's "issue" form](https://github.com/pandas-dev/pandas/issues/new). The form contains some questions that will help us best address your issue. For more information regarding how to file issues against *pandas*, please refer to the "[Bug reports and enhancement requests](https://github.com/pandas-dev/pandas/blob/master/doc/source/contributing.rst#bug-reports-and-enhancement-requests)" section. +If you notice a bug in the code or documentation, or have suggestions for how we can improve either, feel free to create an issue on the [GitHub "issues" tab](https://github.com/pandas-dev/pandas/issues) using [GitHub's "issue" form](https://github.com/pandas-dev/pandas/issues/new). The form contains some questions that will help us best address your issue. For more information regarding how to file issues against *pandas*, please refer to the "[Bug reports and enhancement requests](https://github.com/pandas-dev/pandas/blob/master/doc/source/development/contributing.rst#bug-reports-and-enhancement-requests)" section. ## Contributing to the Codebase -The code is hosted on [GitHub](https://www.github.com/pandas-dev/pandas), so you will need to use [Git](http://git-scm.com/) to clone the project and make changes to the codebase. Once you have obtained a copy of the code, you should create a development environment that is separate from your existing Python environment so that you can make and test changes without compromising your own work environment. For more information, please refer to the "[Working with the code](https://github.com/pandas-dev/pandas/blob/master/doc/source/contributing.rst#working-with-the-code)" section. +The code is hosted on [GitHub](https://www.github.com/pandas-dev/pandas), so you will need to use [Git](http://git-scm.com/) to clone the project and make changes to the codebase. Once you have obtained a copy of the code, you should create a development environment that is separate from your existing Python environment so that you can make and test changes without compromising your own work environment. For more information, please refer to the "[Working with the code](https://github.com/pandas-dev/pandas/blob/master/doc/source/development/contributing.rst#working-with-the-code)" section. -Before submitting your changes for review, make sure to check that your changes do not break any tests. You can find more information about our test suites in the "[Test-driven development/code writing](https://github.com/pandas-dev/pandas/blob/master/doc/source/contributing.rst#test-driven-development-code-writing)" section. We also have guidelines regarding coding style that will be enforced during testing, which can be found in the "[Code standards](https://github.com/pandas-dev/pandas/blob/master/doc/source/contributing.rst#code-standards)" section. +Before submitting your changes for review, make sure to check that your changes do not break any tests. You can find more information about our test suites in the "[Test-driven development/code writing](https://github.com/pandas-dev/pandas/blob/master/doc/source/contributing.rst#test-driven-development-code-writing)" section. We also have guidelines regarding coding style that will be enforced during testing, which can be found in the "[Code standards](https://github.com/pandas-dev/pandas/blob/master/doc/source/development/contributing.rst#code-standards)" section. -Once your changes are ready to be submitted, make sure to push your changes to GitHub before creating a pull request. Details about how to do that can be found in the "[Contributing your changes to pandas](https://github.com/pandas-dev/pandas/blob/master/doc/source/contributing.rst#contributing-your-changes-to-pandas)" section. We will review your changes, and you will most likely be asked to make additional changes before it is finally ready to merge. However, once it's ready, we will merge it, and you will have successfully contributed to the codebase! +Once your changes are ready to be submitted, make sure to push your changes to GitHub before creating a pull request. Details about how to do that can be found in the "[Contributing your changes to pandas](https://github.com/pandas-dev/pandas/blob/master/doc/source/development/contributing.rst#contributing-your-changes-to-pandas)" section. We will review your changes, and you will most likely be asked to make additional changes before it is finally ready to merge. However, once it's ready, we will merge it, and you will have successfully contributed to the codebase! From abf0824200a34c00f6a20b283bbb968f2c2d288f Mon Sep 17 00:00:00 2001 From: Alexander Buchkovsky Date: Tue, 29 Jan 2019 17:54:37 +0200 Subject: [PATCH 016/215] fix for BUG: grouping with tz-aware: Values falls after last bin (#24973) --- doc/source/whatsnew/v0.24.1.rst | 3 +- pandas/core/resample.py | 31 ++++++++++---------- pandas/tests/resample/test_datetime_index.py | 15 ++++++++++ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 85a2ba5bb03b6..8f4c3982c745f 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -72,8 +72,7 @@ Bug Fixes **Reshaping** -- -- +- Bug in :meth:`DataFrame.groupby` with :class:`Grouper` when there is a time change (DST) and grouping frequency is ``'1d'`` (:issue:`24972`) **Visualization** diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 6822225273906..7723827ff478a 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -30,8 +30,7 @@ from pandas.core.indexes.timedeltas import TimedeltaIndex, timedelta_range from pandas.tseries.frequencies import to_offset -from pandas.tseries.offsets import ( - DateOffset, Day, Nano, Tick, delta_to_nanoseconds) +from pandas.tseries.offsets import DateOffset, Day, Nano, Tick _shared_docs_kwargs = dict() @@ -1613,20 +1612,20 @@ def _get_timestamp_range_edges(first, last, offset, closed='left', base=0): A tuple of length 2, containing the adjusted pd.Timestamp objects. """ if isinstance(offset, Tick): - is_day = isinstance(offset, Day) - day_nanos = delta_to_nanoseconds(timedelta(1)) - - # #1165 and #24127 - if (is_day and not offset.nanos % day_nanos) or not is_day: - first, last = _adjust_dates_anchored(first, last, offset, - closed=closed, base=base) - if is_day and first.tz is not None: - # _adjust_dates_anchored assumes 'D' means 24H, but first/last - # might contain a DST transition (23H, 24H, or 25H). - # Ensure first/last snap to midnight. - first = first.normalize() - last = last.normalize() - return first, last + if isinstance(offset, Day): + # _adjust_dates_anchored assumes 'D' means 24H, but first/last + # might contain a DST transition (23H, 24H, or 25H). + # So "pretend" the dates are naive when adjusting the endpoints + tz = first.tz + first = first.tz_localize(None) + last = last.tz_localize(None) + + first, last = _adjust_dates_anchored(first, last, offset, + closed=closed, base=base) + if isinstance(offset, Day): + first = first.tz_localize(tz) + last = last.tz_localize(tz) + return first, last else: first = first.normalize() diff --git a/pandas/tests/resample/test_datetime_index.py b/pandas/tests/resample/test_datetime_index.py index 74487052f8982..856c4df5380e5 100644 --- a/pandas/tests/resample/test_datetime_index.py +++ b/pandas/tests/resample/test_datetime_index.py @@ -1278,6 +1278,21 @@ def test_resample_across_dst(): assert_frame_equal(result, expected) +def test_groupby_with_dst_time_change(): + # GH 24972 + index = pd.DatetimeIndex([1478064900001000000, 1480037118776792000], + tz='UTC').tz_convert('America/Chicago') + + df = pd.DataFrame([1, 2], index=index) + result = df.groupby(pd.Grouper(freq='1d')).last() + expected_index_values = pd.date_range('2016-11-02', '2016-11-24', + freq='d', tz='America/Chicago') + + index = pd.DatetimeIndex(expected_index_values) + expected = pd.DataFrame([1.0] + ([np.nan] * 21) + [2.0], index=index) + assert_frame_equal(result, expected) + + def test_resample_dst_anchor(): # 5172 dti = DatetimeIndex([datetime(2012, 11, 4, 23)], tz='US/Eastern') From ece58cb39f2afa6dbc0c64f701847f7efd718a58 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 29 Jan 2019 15:43:07 -0600 Subject: [PATCH 017/215] REGR: Preserve order by default in Index.difference (#24967) Closes https://github.com/pandas-dev/pandas/issues/24959 --- doc/source/whatsnew/v0.24.1.rst | 1 + pandas/core/indexes/base.py | 8 ++++++-- pandas/core/indexes/datetimes.py | 10 +++++++++- pandas/core/indexes/interval.py | 6 +++--- pandas/core/indexes/multi.py | 6 +++++- pandas/core/indexes/range.py | 6 +++++- pandas/tests/indexes/test_base.py | 22 +++++++++++++++++----- 7 files changed, 46 insertions(+), 13 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 8f4c3982c745f..828c35c10e958 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -22,6 +22,7 @@ Fixed Regressions - Bug in :meth:`DataFrame.itertuples` with ``records`` orient raising an ``AttributeError`` when the ``DataFrame`` contained more than 255 columns (:issue:`24939`) - Bug in :meth:`DataFrame.itertuples` orient converting integer column names to strings prepended with an underscore (:issue:`24940`) +- Fixed regression in :class:`Index.intersection` incorrectly sorting the values by default (:issue:`24959`). .. _whatsnew_0241.enhancements: diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 767da81c5c43a..3d176012df22b 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -2333,7 +2333,7 @@ def union(self, other, sort=True): def _wrap_setop_result(self, other, result): return self._constructor(result, name=get_op_result_name(self, other)) - def intersection(self, other, sort=True): + def intersection(self, other, sort=False): """ Form the intersection of two Index objects. @@ -2342,11 +2342,15 @@ def intersection(self, other, sort=True): Parameters ---------- other : Index or array-like - sort : bool, default True + sort : bool, default False Sort the resulting index if possible .. versionadded:: 0.24.0 + .. versionchanged:: 0.24.1 + + Changed the default from ``True`` to ``False``. + Returns ------- intersection : Index diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index cc373c06efcc9..ef941ab87ba12 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -594,7 +594,7 @@ def _wrap_setop_result(self, other, result): name = get_op_result_name(self, other) return self._shallow_copy(result, name=name, freq=None, tz=self.tz) - def intersection(self, other, sort=True): + def intersection(self, other, sort=False): """ Specialized intersection for DatetimeIndex objects. May be much faster than Index.intersection @@ -602,6 +602,14 @@ def intersection(self, other, sort=True): Parameters ---------- other : DatetimeIndex or array-like + sort : bool, default True + Sort the resulting index if possible. + + .. versionadded:: 0.24.0 + + .. versionchanged:: 0.24.1 + + Changed the default from ``True`` to ``False``. Returns ------- diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 0210560aaa21f..736de94991181 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -1093,8 +1093,8 @@ def equals(self, other): def overlaps(self, other): return self._data.overlaps(other) - def _setop(op_name): - def func(self, other, sort=True): + def _setop(op_name, sort=True): + def func(self, other, sort=sort): other = self._as_like_interval_index(other) # GH 19016: ensure set op will not return a prohibited dtype @@ -1128,7 +1128,7 @@ def is_all_dates(self): return False union = _setop('union') - intersection = _setop('intersection') + intersection = _setop('intersection', sort=False) difference = _setop('difference') symmetric_difference = _setop('symmetric_difference') diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index e4d01a40bd181..16af3fe8eef26 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2910,7 +2910,7 @@ def union(self, other, sort=True): return MultiIndex.from_arrays(lzip(*uniq_tuples), sortorder=0, names=result_names) - def intersection(self, other, sort=True): + def intersection(self, other, sort=False): """ Form the intersection of two MultiIndex objects. @@ -2922,6 +2922,10 @@ def intersection(self, other, sort=True): .. versionadded:: 0.24.0 + .. versionchanged:: 0.24.1 + + Changed the default from ``True`` to ``False``. + Returns ------- Index diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index ebf5b279563cf..e17a6a682af40 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -343,7 +343,7 @@ def equals(self, other): return super(RangeIndex, self).equals(other) - def intersection(self, other, sort=True): + def intersection(self, other, sort=False): """ Form the intersection of two Index objects. @@ -355,6 +355,10 @@ def intersection(self, other, sort=True): .. versionadded:: 0.24.0 + .. versionchanged:: 0.24.1 + + Changed the default from ``True`` to ``False``. + Returns ------- intersection : Index diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index f3e9d835c7391..20e439de46bde 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -765,6 +765,11 @@ def test_intersect_str_dates(self, sort): assert len(result) == 0 + def test_intersect_nosort(self): + result = pd.Index(['c', 'b', 'a']).intersection(['b', 'a']) + expected = pd.Index(['b', 'a']) + tm.assert_index_equal(result, expected) + @pytest.mark.parametrize("sort", [True, False]) def test_chained_union(self, sort): # Chained unions handles names correctly @@ -1595,20 +1600,27 @@ def test_drop_tuple(self, values, to_drop): for drop_me in to_drop[1], [to_drop[1]]: pytest.raises(KeyError, removed.drop, drop_me) - @pytest.mark.parametrize("method,expected", [ + @pytest.mark.parametrize("method,expected,sort", [ + ('intersection', np.array([(1, 'A'), (2, 'A'), (1, 'B'), (2, 'B')], + dtype=[('num', int), ('let', 'a1')]), + False), + ('intersection', np.array([(1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')], - dtype=[('num', int), ('let', 'a1')])), + dtype=[('num', int), ('let', 'a1')]), + True), + ('union', np.array([(1, 'A'), (1, 'B'), (1, 'C'), (2, 'A'), (2, 'B'), - (2, 'C')], dtype=[('num', int), ('let', 'a1')])) + (2, 'C')], dtype=[('num', int), ('let', 'a1')]), + True) ]) - def test_tuple_union_bug(self, method, expected): + def test_tuple_union_bug(self, method, expected, sort): index1 = Index(np.array([(1, 'A'), (2, 'A'), (1, 'B'), (2, 'B')], dtype=[('num', int), ('let', 'a1')])) index2 = Index(np.array([(1, 'A'), (2, 'A'), (1, 'B'), (2, 'B'), (1, 'C'), (2, 'C')], dtype=[('num', int), ('let', 'a1')])) - result = getattr(index1, method)(index2) + result = getattr(index1, method)(index2, sort=sort) assert result.ndim == 1 expected = Index(expected) From 3f90255eb0acbb2d7c428ecbc16765bfa43ec385 Mon Sep 17 00:00:00 2001 From: Christopher Whelan Date: Tue, 29 Jan 2019 21:48:52 -0800 Subject: [PATCH 018/215] CLN: do not use .repeat asv setting for storing benchmark data (#25015) --- asv_bench/benchmarks/strings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asv_bench/benchmarks/strings.py b/asv_bench/benchmarks/strings.py index e9f2727f64e15..b5b2c955f0133 100644 --- a/asv_bench/benchmarks/strings.py +++ b/asv_bench/benchmarks/strings.py @@ -102,10 +102,10 @@ def setup(self, repeats): N = 10**5 self.s = Series(tm.makeStringIndex(N)) repeat = {'int': 1, 'array': np.random.randint(1, 3, N)} - self.repeat = repeat[repeats] + self.values = repeat[repeats] def time_repeat(self, repeats): - self.s.str.repeat(self.repeat) + self.s.str.repeat(self.values) class Cat(object): From 5d1e6337aedd2e6fc15a3efddc896bbba894b892 Mon Sep 17 00:00:00 2001 From: Andrew Gaspari Date: Tue, 29 Jan 2019 23:53:17 -0600 Subject: [PATCH 019/215] CLN: isort asv_bench/benchmark/algorithms.py (#24958) --- asv_bench/benchmarks/algorithms.py | 3 +-- setup.cfg | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/asv_bench/benchmarks/algorithms.py b/asv_bench/benchmarks/algorithms.py index 34fb161e5afcb..74849d330f2bc 100644 --- a/asv_bench/benchmarks/algorithms.py +++ b/asv_bench/benchmarks/algorithms.py @@ -5,7 +5,6 @@ import pandas as pd from pandas.util import testing as tm - for imp in ['pandas.util', 'pandas.tools.hashing']: try: hashing = import_module(imp) @@ -142,4 +141,4 @@ def time_quantile(self, quantile, interpolation, dtype): self.idx.quantile(quantile, interpolation=interpolation) -from .pandas_vb_common import setup # noqa: F401 +from .pandas_vb_common import setup # noqa: F401 isort:skip diff --git a/setup.cfg b/setup.cfg index 7155cc1013544..b15c3ce8a110a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -114,7 +114,6 @@ force_sort_within_sections=True skip= pandas/core/api.py, pandas/core/frame.py, - asv_bench/benchmarks/algorithms.py, asv_bench/benchmarks/attrs_caching.py, asv_bench/benchmarks/binary_ops.py, asv_bench/benchmarks/categoricals.py, From 7486808514a84f64d1fd7dab9e4d5074e87c11c6 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 30 Jan 2019 04:42:49 -0800 Subject: [PATCH 020/215] fix+test to_timedelta('NaT', box=False) (#24961) --- doc/source/whatsnew/v0.24.1.rst | 2 +- pandas/core/tools/timedeltas.py | 3 ++- pandas/tests/scalar/timedelta/test_timedelta.py | 9 +++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 828c35c10e958..57fdff041db28 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -66,7 +66,7 @@ Bug Fixes - **Timedelta** - +- Bug in :func:`to_timedelta` with `box=False` incorrectly returning a ``datetime64`` object instead of a ``timedelta64`` object (:issue:`24961`) - - - diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index e3428146b91d8..ddd21d0f62d08 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -120,7 +120,8 @@ def _coerce_scalar_to_timedelta_type(r, unit='ns', box=True, errors='raise'): try: result = Timedelta(r, unit) if not box: - result = result.asm8 + # explicitly view as timedelta64 for case when result is pd.NaT + result = result.asm8.view('timedelta64[ns]') except ValueError: if errors == 'raise': raise diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index 9b5fdfb06a9fa..e1838e0160fec 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -309,8 +309,13 @@ def test_iso_conversion(self): assert to_timedelta('P0DT0H0M1S') == expected def test_nat_converters(self): - assert to_timedelta('nat', box=False).astype('int64') == iNaT - assert to_timedelta('nan', box=False).astype('int64') == iNaT + result = to_timedelta('nat', box=False) + assert result.dtype.kind == 'm' + assert result.astype('int64') == iNaT + + result = to_timedelta('nan', box=False) + assert result.dtype.kind == 'm' + assert result.astype('int64') == iNaT @pytest.mark.parametrize('units, np_unit', [(['Y', 'y'], 'Y'), From b9f2e2bd526ad69fe3d737d395742193e29fd0e5 Mon Sep 17 00:00:00 2001 From: Christopher Whelan Date: Wed, 30 Jan 2019 05:01:41 -0800 Subject: [PATCH 021/215] PERF: significant speedup in sparse init and ops by using numpy in check_integrity (#24985) --- doc/source/whatsnew/v0.25.0.rst | 9 ++++++++- pandas/_libs/sparse.pyx | 15 +++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index d0ddb6e09d555..a9fa8b2174dd0 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -23,6 +23,13 @@ Other Enhancements - - +.. _whatsnew_0250.performance: + +Performance Improvements +~~~~~~~~~~~~~~~~~~~~~~~~ + - Significant speedup in `SparseArray` initialization that benefits most operations, fixing performance regression introduced in v0.20.0 (:issue:`24985`) + + .. _whatsnew_0250.api_breaking: @@ -187,7 +194,7 @@ Reshaping Sparse ^^^^^^ -- +- Significant speedup in `SparseArray` initialization that benefits most operations, fixing performance regression introduced in v0.20.0 (:issue:`24985`) - - diff --git a/pandas/_libs/sparse.pyx b/pandas/_libs/sparse.pyx index f5980998f6db4..5471c8184e458 100644 --- a/pandas/_libs/sparse.pyx +++ b/pandas/_libs/sparse.pyx @@ -72,9 +72,6 @@ cdef class IntIndex(SparseIndex): A ValueError is raised if any of these conditions is violated. """ - cdef: - int32_t index, prev = -1 - if self.npoints > self.length: msg = ("Too many indices. Expected " "{exp} but found {act}").format( @@ -86,17 +83,15 @@ cdef class IntIndex(SparseIndex): if self.npoints == 0: return - if min(self.indices) < 0: + if self.indices.min() < 0: raise ValueError("No index can be less than zero") - if max(self.indices) >= self.length: + if self.indices.max() >= self.length: raise ValueError("All indices must be less than the length") - for index in self.indices: - if prev != -1 and index <= prev: - raise ValueError("Indices must be strictly increasing") - - prev = index + monotonic = np.all(self.indices[:-1] < self.indices[1:]) + if not monotonic: + raise ValueError("Indices must be strictly increasing") def equals(self, other): if not isinstance(other, IntIndex): From 6220d22b45eb67dc2b5eda1c62a0ce97952e0728 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 30 Jan 2019 15:17:30 -0600 Subject: [PATCH 022/215] BUG: Fixed merging on tz-aware (#25033) --- doc/source/whatsnew/v0.24.1.rst | 1 + pandas/core/internals/concat.py | 6 ++++-- pandas/tests/reshape/merge/test_merge.py | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 57fdff041db28..047404e93914b 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -23,6 +23,7 @@ Fixed Regressions - Bug in :meth:`DataFrame.itertuples` with ``records`` orient raising an ``AttributeError`` when the ``DataFrame`` contained more than 255 columns (:issue:`24939`) - Bug in :meth:`DataFrame.itertuples` orient converting integer column names to strings prepended with an underscore (:issue:`24940`) - Fixed regression in :class:`Index.intersection` incorrectly sorting the values by default (:issue:`24959`). +- Fixed regression in :func:`merge` when merging an empty ``DataFrame`` with multiple timezone-aware columns on one of the timezone-aware columns (:issue:`25014`). .. _whatsnew_0241.enhancements: diff --git a/pandas/core/internals/concat.py b/pandas/core/internals/concat.py index 4a16707a376e9..640587b7f9f31 100644 --- a/pandas/core/internals/concat.py +++ b/pandas/core/internals/concat.py @@ -183,7 +183,7 @@ def get_reindexed_values(self, empty_dtype, upcasted_na): is_datetime64tz_dtype(empty_dtype)): if self.block is None: array = empty_dtype.construct_array_type() - return array(np.full(self.shape[1], fill_value), + return array(np.full(self.shape[1], fill_value.value), dtype=empty_dtype) pass elif getattr(self.block, 'is_categorical', False): @@ -335,8 +335,10 @@ def get_empty_dtype_and_na(join_units): elif 'category' in upcast_classes: return np.dtype(np.object_), np.nan elif 'datetimetz' in upcast_classes: + # GH-25014. We use NaT instead of iNaT, since this eventually + # ends up in DatetimeArray.take, which does not allow iNaT. dtype = upcast_classes['datetimetz'] - return dtype[0], tslibs.iNaT + return dtype[0], tslibs.NaT elif 'datetime' in upcast_classes: return np.dtype('M8[ns]'), tslibs.iNaT elif 'timedelta' in upcast_classes: diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index c17c301968269..a0a20d1da6cef 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -616,6 +616,24 @@ def test_merge_on_datetime64tz(self): assert result['value_x'].dtype == 'datetime64[ns, US/Eastern]' assert result['value_y'].dtype == 'datetime64[ns, US/Eastern]' + def test_merge_on_datetime64tz_empty(self): + # https://github.com/pandas-dev/pandas/issues/25014 + dtz = pd.DatetimeTZDtype(tz='UTC') + right = pd.DataFrame({'date': [pd.Timestamp('2018', tz=dtz.tz)], + 'value': [4.0], + 'date2': [pd.Timestamp('2019', tz=dtz.tz)]}, + columns=['date', 'value', 'date2']) + left = right[:0] + result = left.merge(right, on='date') + expected = pd.DataFrame({ + 'value_x': pd.Series(dtype=float), + 'date2_x': pd.Series(dtype=dtz), + 'date': pd.Series(dtype=dtz), + 'value_y': pd.Series(dtype=float), + 'date2_y': pd.Series(dtype=dtz), + }, columns=['value_x', 'date2_x', 'date', 'value_y', 'date2_y']) + tm.assert_frame_equal(result, expected) + def test_merge_datetime64tz_with_dst_transition(self): # GH 18885 df1 = pd.DataFrame(pd.date_range( From 32c0a5d7c0c3065f5f694b2550fcf94a1030d2b9 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 30 Jan 2019 15:18:19 -0600 Subject: [PATCH 023/215] Test nested PandasArray (#24993) --- pandas/core/arrays/numpy_.py | 2 +- pandas/tests/extension/numpy_/__init__.py | 0 pandas/tests/extension/numpy_/conftest.py | 38 +++ .../extension/{ => numpy_}/test_numpy.py | 36 +-- .../extension/numpy_/test_numpy_nested.py | 286 ++++++++++++++++++ 5 files changed, 326 insertions(+), 36 deletions(-) create mode 100644 pandas/tests/extension/numpy_/__init__.py create mode 100644 pandas/tests/extension/numpy_/conftest.py rename pandas/tests/extension/{ => numpy_}/test_numpy.py (84%) create mode 100644 pandas/tests/extension/numpy_/test_numpy_nested.py diff --git a/pandas/core/arrays/numpy_.py b/pandas/core/arrays/numpy_.py index 47517782e2bbf..791ff44303e96 100644 --- a/pandas/core/arrays/numpy_.py +++ b/pandas/core/arrays/numpy_.py @@ -222,7 +222,7 @@ def __getitem__(self, item): item = item._ndarray result = self._ndarray[item] - if not lib.is_scalar(result): + if not lib.is_scalar(item): result = type(self)(result) return result diff --git a/pandas/tests/extension/numpy_/__init__.py b/pandas/tests/extension/numpy_/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/pandas/tests/extension/numpy_/conftest.py b/pandas/tests/extension/numpy_/conftest.py new file mode 100644 index 0000000000000..daa93571c2957 --- /dev/null +++ b/pandas/tests/extension/numpy_/conftest.py @@ -0,0 +1,38 @@ +import numpy as np +import pytest + +from pandas.core.arrays.numpy_ import PandasArray + + +@pytest.fixture +def allow_in_pandas(monkeypatch): + """ + A monkeypatch to tell pandas to let us in. + + By default, passing a PandasArray to an index / series / frame + constructor will unbox that PandasArray to an ndarray, and treat + it as a non-EA column. We don't want people using EAs without + reason. + + The mechanism for this is a check against ABCPandasArray + in each constructor. + + But, for testing, we need to allow them in pandas. So we patch + the _typ of PandasArray, so that we evade the ABCPandasArray + check. + """ + with monkeypatch.context() as m: + m.setattr(PandasArray, '_typ', 'extension') + yield + + +@pytest.fixture +def na_value(): + return np.nan + + +@pytest.fixture +def na_cmp(): + def cmp(a, b): + return np.isnan(a) and np.isnan(b) + return cmp diff --git a/pandas/tests/extension/test_numpy.py b/pandas/tests/extension/numpy_/test_numpy.py similarity index 84% rename from pandas/tests/extension/test_numpy.py rename to pandas/tests/extension/numpy_/test_numpy.py index 7ca6882c7441b..4c93d5ee0b9d7 100644 --- a/pandas/tests/extension/test_numpy.py +++ b/pandas/tests/extension/numpy_/test_numpy.py @@ -6,7 +6,7 @@ from pandas.core.arrays.numpy_ import PandasArray, PandasDtype import pandas.util.testing as tm -from . import base +from .. import base @pytest.fixture @@ -14,28 +14,6 @@ def dtype(): return PandasDtype(np.dtype('float')) -@pytest.fixture -def allow_in_pandas(monkeypatch): - """ - A monkeypatch to tells pandas to let us in. - - By default, passing a PandasArray to an index / series / frame - constructor will unbox that PandasArray to an ndarray, and treat - it as a non-EA column. We don't want people using EAs without - reason. - - The mechanism for this is a check against ABCPandasArray - in each constructor. - - But, for testing, we need to allow them in pandas. So we patch - the _typ of PandasArray, so that we evade the ABCPandasArray - check. - """ - with monkeypatch.context() as m: - m.setattr(PandasArray, '_typ', 'extension') - yield - - @pytest.fixture def data(allow_in_pandas, dtype): return PandasArray(np.arange(1, 101, dtype=dtype._dtype)) @@ -46,18 +24,6 @@ def data_missing(allow_in_pandas): return PandasArray(np.array([np.nan, 1.0])) -@pytest.fixture -def na_value(): - return np.nan - - -@pytest.fixture -def na_cmp(): - def cmp(a, b): - return np.isnan(a) and np.isnan(b) - return cmp - - @pytest.fixture def data_for_sorting(allow_in_pandas): """Length-3 array with a known sort order. diff --git a/pandas/tests/extension/numpy_/test_numpy_nested.py b/pandas/tests/extension/numpy_/test_numpy_nested.py new file mode 100644 index 0000000000000..cf9b34dd08798 --- /dev/null +++ b/pandas/tests/extension/numpy_/test_numpy_nested.py @@ -0,0 +1,286 @@ +""" +Tests for PandasArray with nested data. Users typically won't create +these objects via `pd.array`, but they can show up through `.array` +on a Series with nested data. + +We partition these tests into their own file, as many of the base +tests fail, as they aren't appropriate for nested data. It is easier +to have a seperate file with its own data generating fixtures, than +trying to skip based upon the value of a fixture. +""" +import pytest + +import pandas as pd +from pandas.core.arrays.numpy_ import PandasArray, PandasDtype + +from .. import base + +# For NumPy <1.16, np.array([np.nan, (1,)]) raises +# ValueError: setting an array element with a sequence. +np = pytest.importorskip('numpy', minversion='1.16.0') + + +@pytest.fixture +def dtype(): + return PandasDtype(np.dtype('object')) + + +@pytest.fixture +def data(allow_in_pandas, dtype): + return pd.Series([(i,) for i in range(100)]).array + + +@pytest.fixture +def data_missing(allow_in_pandas): + return PandasArray(np.array([np.nan, (1,)])) + + +@pytest.fixture +def data_for_sorting(allow_in_pandas): + """Length-3 array with a known sort order. + + This should be three items [B, C, A] with + A < B < C + """ + # Use an empty tuple for first element, then remove, + # to disable np.array's shape inference. + return PandasArray( + np.array([(), (2,), (3,), (1,)])[1:] + ) + + +@pytest.fixture +def data_missing_for_sorting(allow_in_pandas): + """Length-3 array with a known sort order. + + This should be three items [B, NA, A] with + A < B and NA missing. + """ + return PandasArray( + np.array([(1,), np.nan, (0,)]) + ) + + +@pytest.fixture +def data_for_grouping(allow_in_pandas): + """Data for factorization, grouping, and unique tests. + + Expected to be like [B, B, NA, NA, A, A, B, C] + + Where A < B < C and NA is missing + """ + a, b, c = (1,), (2,), (3,) + return PandasArray(np.array( + [b, b, np.nan, np.nan, a, a, b, c] + )) + + +skip_nested = pytest.mark.skip(reason="Skipping for nested PandasArray") + + +class BaseNumPyTests(object): + pass + + +class TestCasting(BaseNumPyTests, base.BaseCastingTests): + + @skip_nested + def test_astype_str(self, data): + pass + + +class TestConstructors(BaseNumPyTests, base.BaseConstructorsTests): + @pytest.mark.skip(reason="We don't register our dtype") + # We don't want to register. This test should probably be split in two. + def test_from_dtype(self, data): + pass + + @skip_nested + def test_array_from_scalars(self, data): + pass + + +class TestDtype(BaseNumPyTests, base.BaseDtypeTests): + + @pytest.mark.skip(reason="Incorrect expected.") + # we unsurprisingly clash with a NumPy name. + def test_check_dtype(self, data): + pass + + +class TestGetitem(BaseNumPyTests, base.BaseGetitemTests): + + @skip_nested + def test_getitem_scalar(self, data): + pass + + @skip_nested + def test_take_series(self, data): + pass + + +class TestGroupby(BaseNumPyTests, base.BaseGroupbyTests): + @skip_nested + def test_groupby_extension_apply(self, data_for_grouping, op): + pass + + +class TestInterface(BaseNumPyTests, base.BaseInterfaceTests): + @skip_nested + def test_array_interface(self, data): + # NumPy array shape inference + pass + + +class TestMethods(BaseNumPyTests, base.BaseMethodsTests): + + @pytest.mark.skip(reason="TODO: remove?") + def test_value_counts(self, all_data, dropna): + pass + + @pytest.mark.skip(reason="Incorrect expected") + # We have a bool dtype, so the result is an ExtensionArray + # but expected is not + def test_combine_le(self, data_repeated): + super(TestMethods, self).test_combine_le(data_repeated) + + @skip_nested + def test_combine_add(self, data_repeated): + # Not numeric + pass + + @skip_nested + def test_shift_fill_value(self, data): + # np.array shape inference. Shift implementation fails. + super().test_shift_fill_value(data) + + @skip_nested + def test_unique(self, data, box, method): + # Fails creating expected + pass + + @skip_nested + def test_fillna_copy_frame(self, data_missing): + # The "scalar" for this array isn't a scalar. + pass + + @skip_nested + def test_fillna_copy_series(self, data_missing): + # The "scalar" for this array isn't a scalar. + pass + + @skip_nested + def test_hash_pandas_object_works(self, data, as_frame): + # ndarray of tuples not hashable + pass + + @skip_nested + def test_searchsorted(self, data_for_sorting, as_series): + # Test setup fails. + pass + + @skip_nested + def test_where_series(self, data, na_value, as_frame): + # Test setup fails. + pass + + @skip_nested + def test_repeat(self, data, repeats, as_series, use_numpy): + # Fails creating expected + pass + + +class TestPrinting(BaseNumPyTests, base.BasePrintingTests): + pass + + +class TestMissing(BaseNumPyTests, base.BaseMissingTests): + + @skip_nested + def test_fillna_scalar(self, data_missing): + # Non-scalar "scalar" values. + pass + + @skip_nested + def test_fillna_series_method(self, data_missing, method): + # Non-scalar "scalar" values. + pass + + @skip_nested + def test_fillna_series(self, data_missing): + # Non-scalar "scalar" values. + pass + + @skip_nested + def test_fillna_frame(self, data_missing): + # Non-scalar "scalar" values. + pass + + +class TestReshaping(BaseNumPyTests, base.BaseReshapingTests): + + @pytest.mark.skip("Incorrect parent test") + # not actually a mixed concat, since we concat int and int. + def test_concat_mixed_dtypes(self, data): + super(TestReshaping, self).test_concat_mixed_dtypes(data) + + @skip_nested + def test_merge(self, data, na_value): + # Fails creating expected + pass + + @skip_nested + def test_merge_on_extension_array(self, data): + # Fails creating expected + pass + + @skip_nested + def test_merge_on_extension_array_duplicates(self, data): + # Fails creating expected + pass + + +class TestSetitem(BaseNumPyTests, base.BaseSetitemTests): + + @skip_nested + def test_setitem_scalar_series(self, data, box_in_series): + pass + + @skip_nested + def test_setitem_sequence(self, data, box_in_series): + pass + + @skip_nested + def test_setitem_sequence_mismatched_length_raises(self, data, as_array): + pass + + @skip_nested + def test_setitem_sequence_broadcasts(self, data, box_in_series): + pass + + @skip_nested + def test_setitem_loc_scalar_mixed(self, data): + pass + + @skip_nested + def test_setitem_loc_scalar_multiple_homogoneous(self, data): + pass + + @skip_nested + def test_setitem_iloc_scalar_mixed(self, data): + pass + + @skip_nested + def test_setitem_iloc_scalar_multiple_homogoneous(self, data): + pass + + @skip_nested + def test_setitem_mask_broadcast(self, data, setter): + pass + + @skip_nested + def test_setitem_scalar_key_sequence_raise(self, data): + pass + + +# Skip Arithmetics, NumericReduce, BooleanReduce, Parsing From 515b0fa77e788c7fc15c9966e502ee99585fed39 Mon Sep 17 00:00:00 2001 From: Johnny Chiu Date: Wed, 30 Jan 2019 13:56:43 -0800 Subject: [PATCH 024/215] DOC: fix error in documentation #24981 (#25038) --- doc/source/user_guide/groupby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/groupby.rst b/doc/source/user_guide/groupby.rst index 953f40d1afebe..2c2e5c5425216 100644 --- a/doc/source/user_guide/groupby.rst +++ b/doc/source/user_guide/groupby.rst @@ -15,7 +15,7 @@ steps: Out of these, the split step is the most straightforward. In fact, in many situations we may wish to split the data set into groups and do something with -those groups. In the apply step, we might wish to one of the +those groups. In the apply step, we might wish to do one of the following: * **Aggregation**: compute a summary statistic (or statistics) for each From 4cbee179bd9ced459ef7865297110ec7e86f213f Mon Sep 17 00:00:00 2001 From: Christopher Whelan Date: Wed, 30 Jan 2019 14:29:34 -0800 Subject: [PATCH 025/215] BUG: support dtypes in column_dtypes for to_records() (#24895) --- doc/source/whatsnew/v0.25.0.rst | 3 +-- pandas/core/frame.py | 3 ++- pandas/tests/frame/test_convert_to.py | 33 +++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index a9fa8b2174dd0..867007b2ba7f5 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -187,7 +187,7 @@ Reshaping ^^^^^^^^^ - Bug in :func:`merge` when merging by index name would sometimes result in an incorrectly numbered index (:issue:`24212`) -- +- :func:`to_records` now accepts dtypes to its `column_dtypes` parameter (:issue:`24895`) - @@ -213,4 +213,3 @@ Contributors ~~~~~~~~~~~~ .. contributors:: v0.24.x..HEAD - diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 28c6f3c23a3ce..2049a8aa960bf 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -1719,7 +1719,8 @@ def to_records(self, index=True, convert_datetime64=None, # string naming a type. if dtype_mapping is None: formats.append(v.dtype) - elif isinstance(dtype_mapping, (type, compat.string_types)): + elif isinstance(dtype_mapping, (type, np.dtype, + compat.string_types)): formats.append(dtype_mapping) else: element = "row" if i < index_len else "column" diff --git a/pandas/tests/frame/test_convert_to.py b/pandas/tests/frame/test_convert_to.py index 7b98395dd6dec..601a4c6b72fe3 100644 --- a/pandas/tests/frame/test_convert_to.py +++ b/pandas/tests/frame/test_convert_to.py @@ -10,7 +10,9 @@ from pandas.compat import long -from pandas import DataFrame, MultiIndex, Series, Timestamp, compat, date_range +from pandas import ( + CategoricalDtype, DataFrame, MultiIndex, Series, Timestamp, compat, + date_range) from pandas.tests.frame.common import TestData import pandas.util.testing as tm @@ -220,6 +222,12 @@ def test_to_records_with_categorical(self): dtype=[("index", " Date: Thu, 31 Jan 2019 13:25:54 +0100 Subject: [PATCH 026/215] Makes example from docstring work (#25035) --- pandas/core/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index a351233a77465..cff685c2ad7cb 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -6601,7 +6601,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, 'barycentric', 'polynomial': Passed to `scipy.interpolate.interp1d`. Both 'polynomial' and 'spline' require that you also specify an `order` (int), - e.g. ``df.interpolate(method='polynomial', order=4)``. + e.g. ``df.interpolate(method='polynomial', order=5)``. These use the numerical values of the index. * 'krogh', 'piecewise_polynomial', 'spline', 'pchip', 'akima': Wrappers around the SciPy interpolation methods of similar From c4cc9fc01488a37957bea8e1a37fed75ca07045b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 31 Jan 2019 04:27:49 -0800 Subject: [PATCH 027/215] CLN: typo fixups (#25028) --- pandas/_libs/interval.pyx | 1 - pandas/_libs/tslibs/nattype.pyx | 2 +- pandas/_libs/tslibs/timestamps.pyx | 4 ++-- pandas/core/internals/blocks.py | 14 +++----------- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/pandas/_libs/interval.pyx b/pandas/_libs/interval.pyx index 3147f36dcc835..eb511b1adb28a 100644 --- a/pandas/_libs/interval.pyx +++ b/pandas/_libs/interval.pyx @@ -18,7 +18,6 @@ cnp.import_array() cimport pandas._libs.util as util -util.import_array() from pandas._libs.hashtable cimport Int64Vector, Int64VectorData diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index a55d15a7c4e85..92cbcce6c7042 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -382,7 +382,7 @@ class NaTType(_NaT): ) combine = _make_error_func('combine', # noqa:E128 """ - Timsetamp.combine(date, time) + Timestamp.combine(date, time) date, time -> datetime with same date and time fields """ diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index fe0564cb62c30..3e6763e226a4a 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -197,7 +197,7 @@ def round_nsint64(values, mode, freq): # This is PITA. Because we inherit from datetime, which has very specific # construction requirements, we need to do object instantiation in python -# (see Timestamp class above). This will serve as a C extension type that +# (see Timestamp class below). This will serve as a C extension type that # shadows the python class, where we do any heavy lifting. cdef class _Timestamp(datetime): @@ -670,7 +670,7 @@ class Timestamp(_Timestamp): @classmethod def combine(cls, date, time): """ - Timsetamp.combine(date, time) + Timestamp.combine(date, time) date, time -> datetime with same date and time fields """ diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index df764aa4ba666..36144c31dfef9 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -2072,17 +2072,9 @@ def get_values(self, dtype=None): return object dtype as boxed values, such as Timestamps/Timedelta """ if is_object_dtype(dtype): - values = self.values - - if self.ndim > 1: - values = values.ravel() - - values = lib.map_infer(values, self._box_func) - - if self.ndim > 1: - values = values.reshape(self.values.shape) - - return values + values = self.values.ravel() + result = self._holder(values).astype(object) + return result.reshape(self.values.shape) return self.values From 534bce98afe890bc8e3d814671ae88fdfd4c21f7 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Thu, 31 Jan 2019 04:29:32 -0800 Subject: [PATCH 028/215] BUG: to_datetime(strs, utc=True) used previous UTC offset (#25020) --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/_libs/tslib.pyx | 2 ++ pandas/tests/indexes/datetimes/test_tools.py | 23 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 867007b2ba7f5..24e3b42859416 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -103,7 +103,7 @@ Timedelta Timezones ^^^^^^^^^ -- +- Bug in :func:`to_datetime` with ``utc=True`` and datetime strings that would apply previously parsed UTC offsets to subsequent arguments (:issue:`24992`) - - diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index 798e338d5581b..f932e236b5218 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -645,6 +645,8 @@ cpdef array_to_datetime(ndarray[object] values, str errors='raise', out_tzoffset_vals.add(out_tzoffset * 60.) tz = pytz.FixedOffset(out_tzoffset) value = tz_convert_single(value, tz, UTC) + out_local = 0 + out_tzoffset = 0 else: # Add a marker for naive string, to track if we are # parsing mixed naive and aware strings diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index 38f5eab15041f..b94935d2521eb 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -714,6 +714,29 @@ def test_iso_8601_strings_with_different_offsets(self): NaT], tz='UTC') tm.assert_index_equal(result, expected) + def test_iss8601_strings_mixed_offsets_with_naive(self): + # GH 24992 + result = pd.to_datetime([ + '2018-11-28T00:00:00', + '2018-11-28T00:00:00+12:00', + '2018-11-28T00:00:00', + '2018-11-28T00:00:00+06:00', + '2018-11-28T00:00:00' + ], utc=True) + expected = pd.to_datetime([ + '2018-11-28T00:00:00', + '2018-11-27T12:00:00', + '2018-11-28T00:00:00', + '2018-11-27T18:00:00', + '2018-11-28T00:00:00' + ], utc=True) + tm.assert_index_equal(result, expected) + + items = ['2018-11-28T00:00:00+12:00', '2018-11-28T00:00:00'] + result = pd.to_datetime(items, utc=True) + expected = pd.to_datetime(list(reversed(items)), utc=True)[::-1] + tm.assert_index_equal(result, expected) + def test_non_iso_strings_with_tz_offset(self): result = to_datetime(['March 1, 2018 12:00:00+0400'] * 2) expected = DatetimeIndex([datetime(2018, 3, 1, 12, From 149138ef008a26bca04f4b698e15ff8fd3ce4663 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Thu, 31 Jan 2019 04:46:45 -0800 Subject: [PATCH 029/215] BUG: Better handle larger numbers in to_numeric (#24956) * BUG: Better handle larger numbers in to_numeric * Warn about lossiness when passing really large numbers that exceed (u)int64 ranges. * Coerce negative numbers to float when requested instead of crashing and returning object. * Consistently parse numbers as integers / floats, even if we know that the resulting container has to be float. This is to ensure consistent error behavior when inputs numbers are too large. Closes gh-24910. * MAINT: Address comments --- doc/source/whatsnew/v0.25.0.rst | 2 + pandas/_libs/lib.pyx | 25 +++-- pandas/core/tools/numeric.py | 8 ++ pandas/tests/tools/test_numeric.py | 148 ++++++++++++++++++++++++++--- 4 files changed, 158 insertions(+), 25 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 24e3b42859416..939fb8b9415bd 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -110,6 +110,8 @@ Timezones Numeric ^^^^^^^ +- Bug in :meth:`to_numeric` in which large negative numbers were being improperly handled (:issue:`24910`) +- Bug in :meth:`to_numeric` in which numbers were being coerced to float, even though ``errors`` was not ``coerce`` (:issue:`24910`) - - - diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx index 4745916eb0ce2..4a3440e14ba14 100644 --- a/pandas/_libs/lib.pyx +++ b/pandas/_libs/lib.pyx @@ -1828,7 +1828,7 @@ def maybe_convert_numeric(ndarray[object] values, set na_values, except (ValueError, OverflowError, TypeError): pass - # otherwise, iterate and do full infererence + # Otherwise, iterate and do full inference. cdef: int status, maybe_int Py_ssize_t i, n = values.size @@ -1865,10 +1865,10 @@ def maybe_convert_numeric(ndarray[object] values, set na_values, else: seen.float_ = True - if val <= oINT64_MAX: + if oINT64_MIN <= val <= oINT64_MAX: ints[i] = val - if seen.sint_ and seen.uint_: + if val < oINT64_MIN or (seen.sint_ and seen.uint_): seen.float_ = True elif util.is_bool_object(val): @@ -1910,23 +1910,28 @@ def maybe_convert_numeric(ndarray[object] values, set na_values, else: seen.saw_int(as_int) - if not (seen.float_ or as_int in na_values): + if as_int not in na_values: if as_int < oINT64_MIN or as_int > oUINT64_MAX: - raise ValueError('Integer out of range.') + if seen.coerce_numeric: + seen.float_ = True + else: + raise ValueError("Integer out of range.") + else: + if as_int >= 0: + uints[i] = as_int - if as_int >= 0: - uints[i] = as_int - if as_int <= oINT64_MAX: - ints[i] = as_int + if as_int <= oINT64_MAX: + ints[i] = as_int seen.float_ = seen.float_ or (seen.uint_ and seen.sint_) else: seen.float_ = True except (TypeError, ValueError) as e: if not seen.coerce_numeric: - raise type(e)(str(e) + ' at position {pos}'.format(pos=i)) + raise type(e)(str(e) + " at position {pos}".format(pos=i)) elif "uint64" in str(e): # Exception from check functions. raise + seen.saw_null() floats[i] = NaN diff --git a/pandas/core/tools/numeric.py b/pandas/core/tools/numeric.py index 79d8ee38637f9..24f3e6753e500 100644 --- a/pandas/core/tools/numeric.py +++ b/pandas/core/tools/numeric.py @@ -19,6 +19,14 @@ def to_numeric(arg, errors='raise', downcast=None): depending on the data supplied. Use the `downcast` parameter to obtain other dtypes. + Please note that precision loss may occur if really large numbers + are passed in. Due to the internal limitations of `ndarray`, if + numbers smaller than `-9223372036854775808` (np.iinfo(np.int64).min) + or larger than `18446744073709551615` (np.iinfo(np.uint64).max) are + passed in, it is very likely they will be converted to float so that + they can stored in an `ndarray`. These warnings apply similarly to + `Series` since it internally leverages `ndarray`. + Parameters ---------- arg : scalar, list, tuple, 1-d array, or Series diff --git a/pandas/tests/tools/test_numeric.py b/pandas/tests/tools/test_numeric.py index 3822170d884aa..97e1dc2f6aefc 100644 --- a/pandas/tests/tools/test_numeric.py +++ b/pandas/tests/tools/test_numeric.py @@ -4,11 +4,50 @@ from numpy import iinfo import pytest +import pandas.compat as compat + import pandas as pd from pandas import DataFrame, Index, Series, to_numeric from pandas.util import testing as tm +@pytest.fixture(params=[None, "ignore", "raise", "coerce"]) +def errors(request): + return request.param + + +@pytest.fixture(params=[True, False]) +def signed(request): + return request.param + + +@pytest.fixture(params=[lambda x: x, str], ids=["identity", "str"]) +def transform(request): + return request.param + + +@pytest.fixture(params=[ + 47393996303418497800, + 100000000000000000000 +]) +def large_val(request): + return request.param + + +@pytest.fixture(params=[True, False]) +def multiple_elts(request): + return request.param + + +@pytest.fixture(params=[ + (lambda x: Index(x, name="idx"), tm.assert_index_equal), + (lambda x: Series(x, name="ser"), tm.assert_series_equal), + (lambda x: np.array(Index(x).values), tm.assert_numpy_array_equal) +]) +def transform_assert_equal(request): + return request.param + + @pytest.mark.parametrize("input_kwargs,result_kwargs", [ (dict(), dict(dtype=np.int64)), (dict(errors="coerce", downcast="integer"), dict(dtype=np.int8)) @@ -172,7 +211,6 @@ def test_all_nan(): tm.assert_series_equal(result, expected) -@pytest.mark.parametrize("errors", [None, "ignore", "raise", "coerce"]) def test_type_check(errors): # see gh-11776 df = DataFrame({"a": [1, -3.14, 7], "b": ["4", "5", "6"]}) @@ -183,11 +221,100 @@ def test_type_check(errors): to_numeric(df, **kwargs) -@pytest.mark.parametrize("val", [ - 1, 1.1, "1", "1.1", -1.5, "-1.5" -]) -def test_scalar(val): - assert to_numeric(val) == float(val) +@pytest.mark.parametrize("val", [1, 1.1, 20001]) +def test_scalar(val, signed, transform): + val = -val if signed else val + assert to_numeric(transform(val)) == float(val) + + +def test_really_large_scalar(large_val, signed, transform, errors): + # see gh-24910 + kwargs = dict(errors=errors) if errors is not None else dict() + val = -large_val if signed else large_val + + val = transform(val) + val_is_string = isinstance(val, str) + + if val_is_string and errors in (None, "raise"): + msg = "Integer out of range. at position 0" + with pytest.raises(ValueError, match=msg): + to_numeric(val, **kwargs) + else: + expected = float(val) if (errors == "coerce" and + val_is_string) else val + assert tm.assert_almost_equal(to_numeric(val, **kwargs), expected) + + +def test_really_large_in_arr(large_val, signed, transform, + multiple_elts, errors): + # see gh-24910 + kwargs = dict(errors=errors) if errors is not None else dict() + val = -large_val if signed else large_val + val = transform(val) + + extra_elt = "string" + arr = [val] + multiple_elts * [extra_elt] + + val_is_string = isinstance(val, str) + coercing = errors == "coerce" + + if errors in (None, "raise") and (val_is_string or multiple_elts): + if val_is_string: + msg = "Integer out of range. at position 0" + else: + msg = 'Unable to parse string "string" at position 1' + + with pytest.raises(ValueError, match=msg): + to_numeric(arr, **kwargs) + else: + result = to_numeric(arr, **kwargs) + + exp_val = float(val) if (coercing and val_is_string) else val + expected = [exp_val] + + if multiple_elts: + if coercing: + expected.append(np.nan) + exp_dtype = float + else: + expected.append(extra_elt) + exp_dtype = object + else: + exp_dtype = float if isinstance(exp_val, ( + int, compat.long, float)) else object + + tm.assert_almost_equal(result, np.array(expected, dtype=exp_dtype)) + + +def test_really_large_in_arr_consistent(large_val, signed, + multiple_elts, errors): + # see gh-24910 + # + # Even if we discover that we have to hold float, does not mean + # we should be lenient on subsequent elements that fail to be integer. + kwargs = dict(errors=errors) if errors is not None else dict() + arr = [str(-large_val if signed else large_val)] + + if multiple_elts: + arr.insert(0, large_val) + + if errors in (None, "raise"): + index = int(multiple_elts) + msg = "Integer out of range. at position {index}".format(index=index) + + with pytest.raises(ValueError, match=msg): + to_numeric(arr, **kwargs) + else: + result = to_numeric(arr, **kwargs) + + if errors == "coerce": + expected = [float(i) for i in arr] + exp_dtype = float + else: + expected = arr + exp_dtype = object + + tm.assert_almost_equal(result, np.array(expected, dtype=exp_dtype)) @pytest.mark.parametrize("errors,checker", [ @@ -205,15 +332,6 @@ def test_scalar_fail(errors, checker): assert checker(to_numeric(scalar, errors=errors)) -@pytest.fixture(params=[ - (lambda x: Index(x, name="idx"), tm.assert_index_equal), - (lambda x: Series(x, name="ser"), tm.assert_series_equal), - (lambda x: np.array(Index(x).values), tm.assert_numpy_array_equal) -]) -def transform_assert_equal(request): - return request.param - - @pytest.mark.parametrize("data", [ [1, 2, 3], [1., np.nan, 3, np.nan] From da5f5eb6c11140a2bb9f84049d30ba7d89bdbfd0 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Thu, 31 Jan 2019 10:02:37 -0600 Subject: [PATCH 030/215] BUG: avoid usage in_qtconsole for recent IPython versions (#25039) * Drop IPython<4.0 compat * Revert "Drop IPython<4.0 compat" This reverts commit 0cb0452b31431143ba22b7ad41bf4d3d9d878522. * update a * whatsnew --- doc/source/whatsnew/v0.24.1.rst | 2 +- pandas/core/frame.py | 13 ++++++++++--- pandas/tests/io/formats/test_format.py | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 047404e93914b..521319c55a503 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -83,7 +83,7 @@ Bug Fixes **Other** -- +- Fixed AttributeError when printing a DataFrame's HTML repr after accessing the IPython config object (:issue:`25036`) - .. _whatsnew_0.241.contributors: diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 2049a8aa960bf..78c9f2aa96472 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -17,6 +17,7 @@ import itertools import sys import warnings +from distutils.version import LooseVersion from textwrap import dedent import numpy as np @@ -646,9 +647,15 @@ def _repr_html_(self): # XXX: In IPython 3.x and above, the Qt console will not attempt to # display HTML, so this check can be removed when support for # IPython 2.x is no longer needed. - if console.in_qtconsole(): - # 'HTML output is disabled in QtConsole' - return None + try: + import IPython + except ImportError: + pass + else: + if LooseVersion(IPython.__version__) < LooseVersion('3.0'): + if console.in_qtconsole(): + # 'HTML output is disabled in QtConsole' + return None if self._info_repr(): buf = StringIO(u("")) diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 5d922ccaf1fd5..b0cf5a2f17609 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -12,6 +12,7 @@ import os import re import sys +import textwrap import warnings import dateutil @@ -2777,3 +2778,17 @@ def test_format_percentiles(): fmt.format_percentiles([2, 0.1, 0.5]) with pytest.raises(ValueError, match=msg): fmt.format_percentiles([0.1, 0.5, 'a']) + + +def test_repr_html_ipython_config(ip): + code = textwrap.dedent("""\ + import pandas as pd + df = pd.DataFrame({"A": [1, 2]}) + df._repr_html_() + + cfg = get_ipython().config + cfg['IPKernelApp']['parent_appname'] + df._repr_html_() + """) + result = ip.run_cell(code) + assert not result.error_in_exec From ea013a254847fbc5551e3ec91a468370c7118589 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Thu, 31 Jan 2019 22:24:57 +0100 Subject: [PATCH 031/215] REGR: fix read_sql delegation for queries on MySQL/pymysql (#25024) --- doc/source/whatsnew/v0.24.1.rst | 1 + pandas/io/sql.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 521319c55a503..222963a7ff71a 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -22,6 +22,7 @@ Fixed Regressions - Bug in :meth:`DataFrame.itertuples` with ``records`` orient raising an ``AttributeError`` when the ``DataFrame`` contained more than 255 columns (:issue:`24939`) - Bug in :meth:`DataFrame.itertuples` orient converting integer column names to strings prepended with an underscore (:issue:`24940`) +- Fixed regression in :func:`read_sql` when passing certain queries with MySQL/pymysql (:issue:`24988`). - Fixed regression in :class:`Index.intersection` incorrectly sorting the values by default (:issue:`24959`). - Fixed regression in :func:`merge` when merging an empty ``DataFrame`` with multiple timezone-aware columns on one of the timezone-aware columns (:issue:`25014`). diff --git a/pandas/io/sql.py b/pandas/io/sql.py index 5d1163b3e0024..aaface5415384 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -381,7 +381,8 @@ def read_sql(sql, con, index_col=None, coerce_float=True, params=None, try: _is_table_name = pandas_sql.has_table(sql) - except (ImportError, AttributeError): + except Exception: + # using generic exception to catch errors from sql drivers (GH24988) _is_table_name = False if _is_table_name: From 5c40cf21e23cf6e9137c7073e927f745f4dd6bcb Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Fri, 1 Feb 2019 06:27:15 -0600 Subject: [PATCH 032/215] DOC: Start 0.24.2.rst (#25026) [ci skip] --- doc/source/whatsnew/v0.24.2.rst | 99 +++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 doc/source/whatsnew/v0.24.2.rst diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst new file mode 100644 index 0000000000000..cba21ce7ee1e6 --- /dev/null +++ b/doc/source/whatsnew/v0.24.2.rst @@ -0,0 +1,99 @@ +:orphan: + +.. _whatsnew_0242: + +Whats New in 0.24.2 (February XX, 2019) +--------------------------------------- + +.. warning:: + + The 0.24.x series of releases will be the last to support Python 2. Future feature + releases will support Python 3 only. See :ref:`install.dropping-27` for more. + +{{ header }} + +These are the changes in pandas 0.24.2. See :ref:`release` for a full changelog +including other versions of pandas. + +.. _whatsnew_0242.regressions: + +Fixed Regressions +^^^^^^^^^^^^^^^^^ + +- +- +- + +.. _whatsnew_0242.enhancements: + +Enhancements +^^^^^^^^^^^^ + +- +- + +.. _whatsnew_0242.bug_fixes: + +Bug Fixes +~~~~~~~~~ + +**Conversion** + +- +- +- + +**Indexing** + +- +- +- + +**I/O** + +- +- +- + +**Categorical** + +- +- +- + +**Timezones** + +- +- +- + +**Timedelta** + +- +- +- + +**Reshaping** + +- +- +- + +**Visualization** + +- +- +- + +**Other** + +- +- +- + +.. _whatsnew_0.242.contributors: + +Contributors +~~~~~~~~~~~~ + +.. contributors:: v0.24.1..v0.24.2 \ No newline at end of file From a0623af751802598bfdf0cd533b35cfbc3e0e2ff Mon Sep 17 00:00:00 2001 From: Jeremy Schendel Date: Fri, 1 Feb 2019 11:23:20 -0700 Subject: [PATCH 033/215] REGR: rename_axis with None should remove axis name (#25069) --- doc/source/whatsnew/v0.24.1.rst | 1 + pandas/core/generic.py | 22 ++++++++++++++++------ pandas/tests/frame/test_alter_axes.py | 20 ++++++++++++++++++++ pandas/tests/series/test_alter_axes.py | 11 +++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 222963a7ff71a..c8b5417a5b77f 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -25,6 +25,7 @@ Fixed Regressions - Fixed regression in :func:`read_sql` when passing certain queries with MySQL/pymysql (:issue:`24988`). - Fixed regression in :class:`Index.intersection` incorrectly sorting the values by default (:issue:`24959`). - Fixed regression in :func:`merge` when merging an empty ``DataFrame`` with multiple timezone-aware columns on one of the timezone-aware columns (:issue:`25014`). +- Fixed regression in :meth:`Series.rename_axis` and :meth:`DataFrame.rename_axis` where passing ``None`` failed to remove the axis name (:issue:`25034`) .. _whatsnew_0241.enhancements: diff --git a/pandas/core/generic.py b/pandas/core/generic.py index cff685c2ad7cb..0312ed6ecf3bf 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -61,6 +61,10 @@ by : str or list of str Name or list of names to sort by""") +# sentinel value to use as kwarg in place of None when None has special meaning +# and needs to be distinguished from a user explicitly passing None. +sentinel = object() + def _single_replace(self, to_replace, method, inplace, limit): """ @@ -290,11 +294,16 @@ def _construct_axes_dict_for_slice(self, axes=None, **kwargs): d.update(kwargs) return d - def _construct_axes_from_arguments(self, args, kwargs, require_all=False): + def _construct_axes_from_arguments( + self, args, kwargs, require_all=False, sentinel=None): """Construct and returns axes if supplied in args/kwargs. If require_all, raise if all axis arguments are not supplied return a tuple of (axes, kwargs). + + sentinel specifies the default parameter when an axis is not + supplied; useful to distinguish when a user explicitly passes None + in scenarios where None has special meaning. """ # construct the args @@ -322,7 +331,7 @@ def _construct_axes_from_arguments(self, args, kwargs, require_all=False): raise TypeError("not enough/duplicate arguments " "specified!") - axes = {a: kwargs.pop(a, None) for a in self._AXIS_ORDERS} + axes = {a: kwargs.pop(a, sentinel) for a in self._AXIS_ORDERS} return axes, kwargs @classmethod @@ -1089,7 +1098,7 @@ def rename(self, *args, **kwargs): @rewrite_axis_style_signature('mapper', [('copy', True), ('inplace', False)]) - def rename_axis(self, mapper=None, **kwargs): + def rename_axis(self, mapper=sentinel, **kwargs): """ Set the name of the axis for the index or columns. @@ -1218,7 +1227,8 @@ class name cat 4 0 monkey 2 2 """ - axes, kwargs = self._construct_axes_from_arguments((), kwargs) + axes, kwargs = self._construct_axes_from_arguments( + (), kwargs, sentinel=sentinel) copy = kwargs.pop('copy', True) inplace = kwargs.pop('inplace', False) axis = kwargs.pop('axis', 0) @@ -1231,7 +1241,7 @@ class name inplace = validate_bool_kwarg(inplace, 'inplace') - if (mapper is not None): + if (mapper is not sentinel): # Use v0.23 behavior if a scalar or list non_mapper = is_scalar(mapper) or (is_list_like(mapper) and not is_dict_like(mapper)) @@ -1254,7 +1264,7 @@ class name for axis in lrange(self._AXIS_LEN): v = axes.get(self._AXIS_NAMES[axis]) - if v is None: + if v is sentinel: continue non_mapper = is_scalar(v) or (is_list_like(v) and not is_dict_like(v)) diff --git a/pandas/tests/frame/test_alter_axes.py b/pandas/tests/frame/test_alter_axes.py index c2355742199dc..2d1afa2281d44 100644 --- a/pandas/tests/frame/test_alter_axes.py +++ b/pandas/tests/frame/test_alter_axes.py @@ -600,6 +600,26 @@ def test_rename_axis_mapper(self): with pytest.raises(TypeError, match='bogus'): df.rename_axis(bogus=None) + @pytest.mark.parametrize('kwargs, rename_index, rename_columns', [ + ({'mapper': None, 'axis': 0}, True, False), + ({'mapper': None, 'axis': 1}, False, True), + ({'index': None}, True, False), + ({'columns': None}, False, True), + ({'index': None, 'columns': None}, True, True), + ({}, False, False)]) + def test_rename_axis_none(self, kwargs, rename_index, rename_columns): + # GH 25034 + index = Index(list('abc'), name='foo') + columns = Index(['col1', 'col2'], name='bar') + data = np.arange(6).reshape(3, 2) + df = DataFrame(data, index, columns) + + result = df.rename_axis(**kwargs) + expected_index = index.rename(None) if rename_index else index + expected_columns = columns.rename(None) if rename_columns else columns + expected = DataFrame(data, expected_index, expected_columns) + tm.assert_frame_equal(result, expected) + def test_rename_multiindex(self): tuples_index = [('foo1', 'bar1'), ('foo2', 'bar2')] diff --git a/pandas/tests/series/test_alter_axes.py b/pandas/tests/series/test_alter_axes.py index 04c54bcf8c22c..73adc7d4bf82f 100644 --- a/pandas/tests/series/test_alter_axes.py +++ b/pandas/tests/series/test_alter_axes.py @@ -258,6 +258,17 @@ def test_rename_axis_inplace(self, datetime_series): assert no_return is None tm.assert_series_equal(result, expected) + @pytest.mark.parametrize('kwargs', [{'mapper': None}, {'index': None}, {}]) + def test_rename_axis_none(self, kwargs): + # GH 25034 + index = Index(list('abc'), name='foo') + df = Series([1, 2, 3], index=index) + + result = df.rename_axis(**kwargs) + expected_index = index.rename(None) if kwargs else index + expected = Series([1, 2, 3], index=expected_index) + tm.assert_series_equal(result, expected) + def test_set_axis_inplace_axes(self, axis_series): # GH14636 ser = Series(np.arange(4), index=[1, 3, 5, 7], dtype='int64') From 76f5d2d26d77487e65ce40ed3cc266c5ac8f6359 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Fri, 1 Feb 2019 12:24:36 -0600 Subject: [PATCH 034/215] clarified the documentation for DF.drop_duplicates (#25056) --- pandas/core/frame.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 78c9f2aa96472..ade05ab27093e 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4625,7 +4625,8 @@ def dropna(self, axis=0, how='any', thresh=None, subset=None, def drop_duplicates(self, subset=None, keep='first', inplace=False): """ Return DataFrame with duplicate rows removed, optionally only - considering certain columns. + considering certain columns. Indexes, including time indexes + are ignored. Parameters ---------- From 01483d7384fcbf45e1be47e61cc149d09c241ad4 Mon Sep 17 00:00:00 2001 From: Allen Downey Date: Fri, 1 Feb 2019 13:35:42 -0500 Subject: [PATCH 035/215] Clarification in docstring of Series.value_counts (#25062) --- pandas/core/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index c02ba88ea7fda..7b3152595e4b2 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -1234,7 +1234,7 @@ def value_counts(self, normalize=False, sort=True, ascending=False, If True then the object returned will contain the relative frequencies of the unique values. sort : boolean, default True - Sort by values. + Sort by frequencies. ascending : boolean, default False Sort in ascending order. bins : integer, optional From 6d7bd98e169599a94856f007cec57880a4e91037 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Fri, 1 Feb 2019 10:40:55 -0800 Subject: [PATCH 036/215] ENH: Support fold argument in Timestamp.replace (#25046) --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/_libs/tslibs/nattype.pyx | 1 - pandas/_libs/tslibs/timestamps.pyx | 17 +++++++++++------ pandas/tests/scalar/timestamp/test_unary_ops.py | 15 ++++++++++++++- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 939fb8b9415bd..304a9dd117e45 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -19,7 +19,7 @@ including other versions of pandas. Other Enhancements ^^^^^^^^^^^^^^^^^^ -- +- :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`) - - diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index 92cbcce6c7042..f2de3fda3be15 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -669,7 +669,6 @@ class NaTType(_NaT): nanosecond : int, optional tzinfo : tz-convertible, optional fold : int, optional, default is 0 - added in 3.6, NotImplemented Returns ------- diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 3e6763e226a4a..a9be60214080a 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import sys import warnings from cpython cimport (PyObject_RichCompareBool, PyObject_RichCompare, @@ -43,10 +44,11 @@ from pandas._libs.tslibs.timezones import UTC # Constants _zero_time = datetime_time(0, 0) _no_input = object() - +PY36 = sys.version_info >= (3, 6) # ---------------------------------------------------------------------- + def maybe_integer_op_deprecated(obj): # GH#22535 add/sub of integers and int-arrays is deprecated if obj.freq is not None: @@ -1195,7 +1197,6 @@ class Timestamp(_Timestamp): nanosecond : int, optional tzinfo : tz-convertible, optional fold : int, optional, default is 0 - added in 3.6, NotImplemented Returns ------- @@ -1252,12 +1253,16 @@ class Timestamp(_Timestamp): # see GH#18319 ts_input = _tzinfo.localize(datetime(dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, - dts.us)) + dts.us), + is_dst=not bool(fold)) _tzinfo = ts_input.tzinfo else: - ts_input = datetime(dts.year, dts.month, dts.day, - dts.hour, dts.min, dts.sec, dts.us, - tzinfo=_tzinfo) + kwargs = {'year': dts.year, 'month': dts.month, 'day': dts.day, + 'hour': dts.hour, 'minute': dts.min, 'second': dts.sec, + 'microsecond': dts.us, 'tzinfo': _tzinfo} + if PY36: + kwargs['fold'] = fold + ts_input = datetime(**kwargs) ts = convert_datetime_to_tsobject(ts_input, _tzinfo) value = ts.value + (dts.ps // 1000) diff --git a/pandas/tests/scalar/timestamp/test_unary_ops.py b/pandas/tests/scalar/timestamp/test_unary_ops.py index 3f9a30d254126..adcf66200a672 100644 --- a/pandas/tests/scalar/timestamp/test_unary_ops.py +++ b/pandas/tests/scalar/timestamp/test_unary_ops.py @@ -8,7 +8,7 @@ from pandas._libs.tslibs import conversion from pandas._libs.tslibs.frequencies import INVALID_FREQ_ERR_MSG -from pandas.compat import PY3 +from pandas.compat import PY3, PY36 import pandas.util._test_decorators as td from pandas import NaT, Timestamp @@ -329,6 +329,19 @@ def test_replace_dst_border(self): expected = Timestamp('2013-11-3 03:00:00', tz='America/Chicago') assert result == expected + @pytest.mark.skipif(not PY36, reason='Fold not available until PY3.6') + @pytest.mark.parametrize('fold', [0, 1]) + @pytest.mark.parametrize('tz', ['dateutil/Europe/London', 'Europe/London']) + def test_replace_dst_fold(self, fold, tz): + # GH 25017 + d = datetime(2019, 10, 27, 2, 30) + ts = Timestamp(d, tz=tz) + result = ts.replace(hour=1, fold=fold) + expected = Timestamp(datetime(2019, 10, 27, 1, 30)).tz_localize( + tz, ambiguous=not fold + ) + assert result == expected + # -------------------------------------------------------------- # Timestamp.normalize From 5e224fb8b474df8e7d8053bfbae171f500a65f54 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Fri, 1 Feb 2019 10:50:35 -0800 Subject: [PATCH 037/215] CLN: to_pickle internals (#25044) --- pandas/compat/pickle_compat.py | 3 +- pandas/io/pickle.py | 73 ++++++++++------------------------ 2 files changed, 22 insertions(+), 54 deletions(-) diff --git a/pandas/compat/pickle_compat.py b/pandas/compat/pickle_compat.py index 61295b8249f58..8f16f8154b952 100644 --- a/pandas/compat/pickle_compat.py +++ b/pandas/compat/pickle_compat.py @@ -201,7 +201,7 @@ def load_newobj_ex(self): pass -def load(fh, encoding=None, compat=False, is_verbose=False): +def load(fh, encoding=None, is_verbose=False): """load a pickle, with a provided encoding if compat is True: @@ -212,7 +212,6 @@ def load(fh, encoding=None, compat=False, is_verbose=False): ---------- fh : a filelike object encoding : an optional encoding - compat : provide Series compatibility mode, boolean, default False is_verbose : show exception output """ diff --git a/pandas/io/pickle.py b/pandas/io/pickle.py index 789f55a62dc58..ab4a266853a78 100644 --- a/pandas/io/pickle.py +++ b/pandas/io/pickle.py @@ -1,8 +1,7 @@ """ pickle compat """ import warnings -import numpy as np -from numpy.lib.format import read_array, write_array +from numpy.lib.format import read_array from pandas.compat import PY3, BytesIO, cPickle as pkl, pickle_compat as pc @@ -76,6 +75,7 @@ def to_pickle(obj, path, compression='infer', protocol=pkl.HIGHEST_PROTOCOL): try: f.write(pkl.dumps(obj, protocol=protocol)) finally: + f.close() for _f in fh: _f.close() @@ -138,63 +138,32 @@ def read_pickle(path, compression='infer'): >>> os.remove("./dummy.pkl") """ path = _stringify_path(path) + f, fh = _get_handle(path, 'rb', compression=compression, is_text=False) + + # 1) try with cPickle + # 2) try with the compat pickle to handle subclass changes + # 3) pass encoding only if its not None as py2 doesn't handle the param - def read_wrapper(func): - # wrapper file handle open/close operation - f, fh = _get_handle(path, 'rb', - compression=compression, - is_text=False) - try: - return func(f) - finally: - for _f in fh: - _f.close() - - def try_read(path, encoding=None): - # try with cPickle - # try with current pickle, if we have a Type Error then - # try with the compat pickle to handle subclass changes - # pass encoding only if its not None as py2 doesn't handle - # the param - - # cpickle - # GH 6899 - try: - with warnings.catch_warnings(record=True): - # We want to silence any warnings about, e.g. moved modules. - warnings.simplefilter("ignore", Warning) - return read_wrapper(lambda f: pkl.load(f)) - except Exception: # noqa: E722 - # reg/patched pickle - # compat not used in pandas/compat/pickle_compat.py::load - # TODO: remove except block OR modify pc.load to use compat - try: - return read_wrapper( - lambda f: pc.load(f, encoding=encoding, compat=False)) - # compat pickle - except Exception: # noqa: E722 - return read_wrapper( - lambda f: pc.load(f, encoding=encoding, compat=True)) try: - return try_read(path) + with warnings.catch_warnings(record=True): + # We want to silence any warnings about, e.g. moved modules. + warnings.simplefilter("ignore", Warning) + return pkl.load(f) except Exception: # noqa: E722 - if PY3: - return try_read(path, encoding='latin1') - raise - + try: + return pc.load(f, encoding=None) + except Exception: # noqa: E722 + if PY3: + return pc.load(f, encoding='latin1') + raise + finally: + f.close() + for _f in fh: + _f.close() # compat with sparse pickle / unpickle -def _pickle_array(arr): - arr = arr.view(np.ndarray) - - buf = BytesIO() - write_array(buf, arr) - - return buf.getvalue() - - def _unpickle_array(bytes): arr = read_array(BytesIO(bytes)) From d4b42c8a1a26d716671a17009e62c34147fba6dc Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Fri, 1 Feb 2019 12:03:22 -0800 Subject: [PATCH 038/215] Implement+Test Tick.__rtruediv__ (#24832) --- pandas/_libs/tslibs/offsets.pyx | 16 ++++++++++++++ pandas/tests/tseries/offsets/test_offsets.py | 20 +++++++++++++++++ pandas/tests/tseries/offsets/test_ticks.py | 23 ++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index 856aa52f82cf5..e28462f7103b9 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -18,6 +18,7 @@ from numpy cimport int64_t cnp.import_array() +from pandas._libs.tslibs cimport util from pandas._libs.tslibs.util cimport is_string_object, is_integer_object from pandas._libs.tslibs.ccalendar import MONTHS, DAYS @@ -408,6 +409,10 @@ class _BaseOffset(object): return self.apply(other) def __mul__(self, other): + if hasattr(other, "_typ"): + return NotImplemented + if util.is_array(other): + return np.array([self * x for x in other]) return type(self)(n=other * self.n, normalize=self.normalize, **self.kwds) @@ -458,6 +463,9 @@ class _BaseOffset(object): TypeError if `int(n)` raises ValueError if n != int(n) """ + if util.is_timedelta64_object(n): + raise TypeError('`n` argument must be an integer, ' + 'got {ntype}'.format(ntype=type(n))) try: nint = int(n) except (ValueError, TypeError): @@ -533,12 +541,20 @@ class _Tick(object): can do isinstance checks on _Tick and avoid importing tseries.offsets """ + # ensure that reversed-ops with numpy scalars return NotImplemented + __array_priority__ = 1000 + def __truediv__(self, other): result = self.delta.__truediv__(other) return _wrap_timedelta_result(result) + def __rtruediv__(self, other): + result = self.delta.__rtruediv__(other) + return _wrap_timedelta_result(result) + if PY2: __div__ = __truediv__ + __rdiv__ = __rtruediv__ # ---------------------------------------------------------------------- diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index ac3955970587f..621572da57541 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -257,6 +257,26 @@ def test_offset_n(self, offset_types): mul_offset = offset * 3 assert mul_offset.n == 3 + def test_offset_timedelta64_arg(self, offset_types): + # check that offset._validate_n raises TypeError on a timedelt64 + # object + off = self._get_offset(offset_types) + + td64 = np.timedelta64(4567, 's') + with pytest.raises(TypeError, match="argument must be an integer"): + type(off)(n=td64, **off.kwds) + + def test_offset_mul_ndarray(self, offset_types): + off = self._get_offset(offset_types) + + expected = np.array([[off, off * 2], [off * 3, off * 4]]) + + result = np.array([[1, 2], [3, 4]]) * off + tm.assert_numpy_array_equal(result, expected) + + result = off * np.array([[1, 2], [3, 4]]) + tm.assert_numpy_array_equal(result, expected) + def test_offset_freqstr(self, offset_types): offset = self._get_offset(offset_types) diff --git a/pandas/tests/tseries/offsets/test_ticks.py b/pandas/tests/tseries/offsets/test_ticks.py index f4b012ec1897f..9a8251201f75f 100644 --- a/pandas/tests/tseries/offsets/test_ticks.py +++ b/pandas/tests/tseries/offsets/test_ticks.py @@ -11,6 +11,7 @@ import pytest from pandas import Timedelta, Timestamp +import pandas.util.testing as tm from pandas.tseries import offsets from pandas.tseries.offsets import Hour, Micro, Milli, Minute, Nano, Second @@ -262,6 +263,28 @@ def test_tick_division(cls): assert result.delta == off.delta / .001 +@pytest.mark.parametrize('cls', tick_classes) +def test_tick_rdiv(cls): + off = cls(10) + delta = off.delta + td64 = delta.to_timedelta64() + + with pytest.raises(TypeError): + 2 / off + with pytest.raises(TypeError): + 2.0 / off + + assert (td64 * 2.5) / off == 2.5 + + if cls is not Nano: + # skip pytimedelta for Nano since it gets dropped + assert (delta.to_pytimedelta() * 2) / off == 2 + + result = np.array([2 * td64, td64]) / off + expected = np.array([2., 1.]) + tm.assert_numpy_array_equal(result, expected) + + @pytest.mark.parametrize('cls1', tick_classes) @pytest.mark.parametrize('cls2', tick_classes) def test_tick_zero(cls1, cls2): From 25ff4729243d69bada4eaf1eeeebc7ec41418977 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Fri, 1 Feb 2019 21:07:06 +0100 Subject: [PATCH 039/215] API: change Index set ops sort=True -> sort=None (#25063) --- doc/source/whatsnew/v0.24.1.rst | 34 +++- pandas/_libs/lib.pyx | 3 +- pandas/core/indexes/api.py | 2 +- pandas/core/indexes/base.py | 82 ++++++-- pandas/core/indexes/datetimes.py | 6 +- pandas/core/indexes/interval.py | 2 +- pandas/core/indexes/multi.py | 43 +++- pandas/core/indexes/range.py | 8 +- pandas/tests/indexes/common.py | 2 +- pandas/tests/indexes/datetimes/test_setops.py | 12 +- .../tests/indexes/interval/test_interval.py | 24 +-- pandas/tests/indexes/multi/test_set_ops.py | 155 ++++++++++++-- pandas/tests/indexes/period/test_period.py | 2 +- pandas/tests/indexes/period/test_setops.py | 20 +- pandas/tests/indexes/test_base.py | 189 ++++++++++++++---- pandas/tests/indexes/test_range.py | 2 +- .../indexes/timedeltas/test_timedelta.py | 8 +- 17 files changed, 465 insertions(+), 129 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index c8b5417a5b77f..1ee4397960b0b 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -15,10 +15,40 @@ Whats New in 0.24.1 (February XX, 2019) These are the changes in pandas 0.24.1. See :ref:`release` for a full changelog including other versions of pandas. +.. _whatsnew_0241.api: + +API Changes +~~~~~~~~~~~ + +Changing the ``sort`` parameter for :class:`Index` set operations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The default ``sort`` value for :meth:`Index.union` has changed from ``True`` to ``None`` (:issue:`24959`). +The default *behavior*, however, remains the same: the result is sorted, unless + +1. ``self`` and ``other`` are identical +2. ``self`` or ``other`` is empty +3. ``self`` or ``other`` contain values that can not be compared (a ``RuntimeWarning`` is raised). + +This change will allow ``sort=True`` to mean "always sort" in a future release. + +The same change applies to :meth:`Index.difference` and :meth:`Index.symmetric_difference`, which +would not sort the result when the values could not be compared. + +The `sort` option for :meth:`Index.intersection` has changed in three ways. + +1. The default has changed from ``True`` to ``False``, to restore the + pandas 0.23.4 and earlier behavior of not sorting by default. +2. The behavior of ``sort=True`` can now be obtained with ``sort=None``. + This will sort the result only if the values in ``self`` and ``other`` + are not identical. +3. The value ``sort=True`` is no longer allowed. A future version of pandas + will properly support ``sort=True`` meaning "always sort". + .. _whatsnew_0241.regressions: Fixed Regressions -^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~ - Bug in :meth:`DataFrame.itertuples` with ``records`` orient raising an ``AttributeError`` when the ``DataFrame`` contained more than 255 columns (:issue:`24939`) - Bug in :meth:`DataFrame.itertuples` orient converting integer column names to strings prepended with an underscore (:issue:`24940`) @@ -30,7 +60,7 @@ Fixed Regressions .. _whatsnew_0241.enhancements: Enhancements -^^^^^^^^^^^^ +~~~~~~~~~~~~ .. _whatsnew_0241.bug_fixes: diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx index 4a3440e14ba14..f8875d60049b1 100644 --- a/pandas/_libs/lib.pyx +++ b/pandas/_libs/lib.pyx @@ -233,10 +233,11 @@ def fast_unique_multiple(list arrays, sort: bool=True): if val not in table: table[val] = stub uniques.append(val) - if sort: + if sort is None: try: uniques.sort() except Exception: + # TODO: RuntimeWarning? pass return uniques diff --git a/pandas/core/indexes/api.py b/pandas/core/indexes/api.py index 684a19c56c92f..6299fc482d0df 100644 --- a/pandas/core/indexes/api.py +++ b/pandas/core/indexes/api.py @@ -112,7 +112,7 @@ def _get_combined_index(indexes, intersect=False, sort=False): elif intersect: index = indexes[0] for other in indexes[1:]: - index = index.intersection(other, sort=sort) + index = index.intersection(other) else: index = _union_indexes(indexes, sort=sort) index = ensure_index(index) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 3d176012df22b..2fa034670e885 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -2245,18 +2245,37 @@ def _get_reconciled_name_object(self, other): return self._shallow_copy(name=name) return self - def union(self, other, sort=True): + def _validate_sort_keyword(self, sort): + if sort not in [None, False]: + raise ValueError("The 'sort' keyword only takes the values of " + "None or False; {0} was passed.".format(sort)) + + def union(self, other, sort=None): """ Form the union of two Index objects. Parameters ---------- other : Index or array-like - sort : bool, default True - Sort the resulting index if possible + sort : bool or None, default None + Whether to sort the resulting Index. + + * None : Sort the result, except when + + 1. `self` and `other` are equal. + 2. `self` or `other` has length 0. + 3. Some values in `self` or `other` cannot be compared. + A RuntimeWarning is issued in this case. + + * False : do not sort the result. .. versionadded:: 0.24.0 + .. versionchanged:: 0.24.1 + + Changed the default value from ``True`` to ``None`` + (without change in behaviour). + Returns ------- union : Index @@ -2269,6 +2288,7 @@ def union(self, other, sort=True): >>> idx1.union(idx2) Int64Index([1, 2, 3, 4, 5, 6], dtype='int64') """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) other = ensure_index(other) @@ -2319,7 +2339,7 @@ def union(self, other, sort=True): else: result = lvals - if sort: + if sort is None: try: result = sorting.safe_sort(result) except TypeError as e: @@ -2342,14 +2362,19 @@ def intersection(self, other, sort=False): Parameters ---------- other : Index or array-like - sort : bool, default False - Sort the resulting index if possible + sort : False or None, default False + Whether to sort the resulting index. + + * False : do not sort the result. + * None : sort the result, except when `self` and `other` are equal + or when the values cannot be compared. .. versionadded:: 0.24.0 .. versionchanged:: 0.24.1 - Changed the default from ``True`` to ``False``. + Changed the default from ``True`` to ``False``, to match + the behaviour of 0.23.4 and earlier. Returns ------- @@ -2363,6 +2388,7 @@ def intersection(self, other, sort=False): >>> idx1.intersection(idx2) Int64Index([3, 4], dtype='int64') """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) other = ensure_index(other) @@ -2402,7 +2428,7 @@ def intersection(self, other, sort=False): taken = other.take(indexer) - if sort: + if sort is None: taken = sorting.safe_sort(taken.values) if self.name != other.name: name = None @@ -2415,7 +2441,7 @@ def intersection(self, other, sort=False): return taken - def difference(self, other, sort=True): + def difference(self, other, sort=None): """ Return a new Index with elements from the index that are not in `other`. @@ -2425,11 +2451,22 @@ def difference(self, other, sort=True): Parameters ---------- other : Index or array-like - sort : bool, default True - Sort the resulting index if possible + sort : False or None, default None + Whether to sort the resulting index. By default, the + values are attempted to be sorted, but any TypeError from + incomparable elements is caught by pandas. + + * None : Attempt to sort the result, but catch any TypeErrors + from comparing incomparable elements. + * False : Do not sort the result. .. versionadded:: 0.24.0 + .. versionchanged:: 0.24.1 + + Changed the default value from ``True`` to ``None`` + (without change in behaviour). + Returns ------- difference : Index @@ -2444,6 +2481,7 @@ def difference(self, other, sort=True): >>> idx1.difference(idx2, sort=False) Int64Index([2, 1], dtype='int64') """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) if self.equals(other): @@ -2460,7 +2498,7 @@ def difference(self, other, sort=True): label_diff = np.setdiff1d(np.arange(this.size), indexer, assume_unique=True) the_diff = this.values.take(label_diff) - if sort: + if sort is None: try: the_diff = sorting.safe_sort(the_diff) except TypeError: @@ -2468,7 +2506,7 @@ def difference(self, other, sort=True): return this._shallow_copy(the_diff, name=result_name, freq=None) - def symmetric_difference(self, other, result_name=None, sort=True): + def symmetric_difference(self, other, result_name=None, sort=None): """ Compute the symmetric difference of two Index objects. @@ -2476,11 +2514,22 @@ def symmetric_difference(self, other, result_name=None, sort=True): ---------- other : Index or array-like result_name : str - sort : bool, default True - Sort the resulting index if possible + sort : False or None, default None + Whether to sort the resulting index. By default, the + values are attempted to be sorted, but any TypeError from + incomparable elements is caught by pandas. + + * None : Attempt to sort the result, but catch any TypeErrors + from comparing incomparable elements. + * False : Do not sort the result. .. versionadded:: 0.24.0 + .. versionchanged:: 0.24.1 + + Changed the default value from ``True`` to ``None`` + (without change in behaviour). + Returns ------- symmetric_difference : Index @@ -2504,6 +2553,7 @@ def symmetric_difference(self, other, result_name=None, sort=True): >>> idx1 ^ idx2 Int64Index([1, 5], dtype='int64') """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) other, result_name_update = self._convert_can_do_setop(other) if result_name is None: @@ -2524,7 +2574,7 @@ def symmetric_difference(self, other, result_name=None, sort=True): right_diff = other.values.take(right_indexer) the_diff = _concat._concat_compat([left_diff, right_diff]) - if sort: + if sort is None: try: the_diff = sorting.safe_sort(the_diff) except TypeError: diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index ef941ab87ba12..9c46860eb49d6 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -602,19 +602,21 @@ def intersection(self, other, sort=False): Parameters ---------- other : DatetimeIndex or array-like - sort : bool, default True + sort : False or None, default False Sort the resulting index if possible. .. versionadded:: 0.24.0 .. versionchanged:: 0.24.1 - Changed the default from ``True`` to ``False``. + Changed the default to ``False`` to match the behaviour + from before 0.24.0. Returns ------- y : Index or DatetimeIndex """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) if self.equals(other): diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 736de94991181..2c63fe33c57fe 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -1093,7 +1093,7 @@ def equals(self, other): def overlaps(self, other): return self._data.overlaps(other) - def _setop(op_name, sort=True): + def _setop(op_name, sort=None): def func(self, other, sort=sort): other = self._as_like_interval_index(other) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 16af3fe8eef26..14975dbbefa63 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -2879,30 +2879,47 @@ def equal_levels(self, other): return False return True - def union(self, other, sort=True): + def union(self, other, sort=None): """ Form the union of two MultiIndex objects Parameters ---------- other : MultiIndex or array / Index of tuples - sort : bool, default True - Sort the resulting MultiIndex if possible + sort : False or None, default None + Whether to sort the resulting Index. + + * None : Sort the result, except when + + 1. `self` and `other` are equal. + 2. `self` has length 0. + 3. Some values in `self` or `other` cannot be compared. + A RuntimeWarning is issued in this case. + + * False : do not sort the result. .. versionadded:: 0.24.0 + .. versionchanged:: 0.24.1 + + Changed the default value from ``True`` to ``None`` + (without change in behaviour). + Returns ------- Index >>> index.union(index2) """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) other, result_names = self._convert_can_do_setop(other) if len(other) == 0 or self.equals(other): return self + # TODO: Index.union returns other when `len(self)` is 0. + uniq_tuples = lib.fast_unique_multiple([self._ndarray_values, other._ndarray_values], sort=sort) @@ -2917,19 +2934,21 @@ def intersection(self, other, sort=False): Parameters ---------- other : MultiIndex or array / Index of tuples - sort : bool, default True + sort : False or None, default False Sort the resulting MultiIndex if possible .. versionadded:: 0.24.0 .. versionchanged:: 0.24.1 - Changed the default from ``True`` to ``False``. + Changed the default from ``True`` to ``False``, to match + behaviour from before 0.24.0 Returns ------- Index """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) other, result_names = self._convert_can_do_setop(other) @@ -2940,7 +2959,7 @@ def intersection(self, other, sort=False): other_tuples = other._ndarray_values uniq_tuples = set(self_tuples) & set(other_tuples) - if sort: + if sort is None: uniq_tuples = sorted(uniq_tuples) if len(uniq_tuples) == 0: @@ -2951,22 +2970,28 @@ def intersection(self, other, sort=False): return MultiIndex.from_arrays(lzip(*uniq_tuples), sortorder=0, names=result_names) - def difference(self, other, sort=True): + def difference(self, other, sort=None): """ Compute set difference of two MultiIndex objects Parameters ---------- other : MultiIndex - sort : bool, default True + sort : False or None, default None Sort the resulting MultiIndex if possible .. versionadded:: 0.24.0 + .. versionchanged:: 0.24.1 + + Changed the default value from ``True`` to ``None`` + (without change in behaviour). + Returns ------- diff : MultiIndex """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) other, result_names = self._convert_can_do_setop(other) @@ -2986,7 +3011,7 @@ def difference(self, other, sort=True): label_diff = np.setdiff1d(np.arange(this.size), indexer, assume_unique=True) difference = this.values.take(label_diff) - if sort: + if sort is None: difference = sorted(difference) if len(difference) == 0: diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index e17a6a682af40..5aafe9734b6a0 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -350,19 +350,21 @@ def intersection(self, other, sort=False): Parameters ---------- other : Index or array-like - sort : bool, default True + sort : False or None, default False Sort the resulting index if possible .. versionadded:: 0.24.0 .. versionchanged:: 0.24.1 - Changed the default from ``True`` to ``False``. + Changed the default to ``False`` to match the behaviour + from before 0.24.0. Returns ------- intersection : Index """ + self._validate_sort_keyword(sort) if self.equals(other): return self._get_reconciled_name_object(other) @@ -405,7 +407,7 @@ def intersection(self, other, sort=False): if (self._step < 0 and other._step < 0) is not (new_index._step < 0): new_index = new_index[::-1] - if sort: + if sort is None: new_index = new_index.sort_values() return new_index diff --git a/pandas/tests/indexes/common.py b/pandas/tests/indexes/common.py index d72dccadf0ac0..a838779689c44 100644 --- a/pandas/tests/indexes/common.py +++ b/pandas/tests/indexes/common.py @@ -486,7 +486,7 @@ def test_union_base(self): with pytest.raises(TypeError, match=msg): first.union([1, 2, 3]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_base(self, sort): for name, idx in compat.iteritems(self.indices): first = idx[2:] diff --git a/pandas/tests/indexes/datetimes/test_setops.py b/pandas/tests/indexes/datetimes/test_setops.py index bd37cc815d0f7..19009e45ee83a 100644 --- a/pandas/tests/indexes/datetimes/test_setops.py +++ b/pandas/tests/indexes/datetimes/test_setops.py @@ -138,7 +138,7 @@ def test_intersection2(self): @pytest.mark.parametrize("tz", [None, 'Asia/Tokyo', 'US/Eastern', 'dateutil/US/Pacific']) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection(self, tz, sort): # GH 4690 (with tz) base = date_range('6/1/2000', '6/30/2000', freq='D', name='idx') @@ -187,7 +187,7 @@ def test_intersection(self, tz, sort): for (rng, expected) in [(rng2, expected2), (rng3, expected3), (rng4, expected4)]: result = base.intersection(rng, sort=sort) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) assert result.name == expected.name @@ -212,7 +212,7 @@ def test_intersection_bug_1708(self): assert len(result) == 0 @pytest.mark.parametrize("tz", tz) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference(self, tz, sort): rng_dates = ['1/2/2000', '1/3/2000', '1/1/2000', '1/4/2000', '1/5/2000'] @@ -233,11 +233,11 @@ def test_difference(self, tz, sort): (rng2, other2, expected2), (rng3, other3, expected3)]: result_diff = rng.difference(other, sort) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result_diff, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_freq(self, sort): # GH14323: difference of DatetimeIndex should not preserve frequency @@ -254,7 +254,7 @@ def test_difference_freq(self, sort): tm.assert_index_equal(idx_diff, expected) tm.assert_attr_equal('freq', idx_diff, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_datetimeindex_diff(self, sort): dti1 = date_range(freq='Q-JAN', start=datetime(1997, 12, 31), periods=100) diff --git a/pandas/tests/indexes/interval/test_interval.py b/pandas/tests/indexes/interval/test_interval.py index db69258c1d3d2..f1fd06c9cab6e 100644 --- a/pandas/tests/indexes/interval/test_interval.py +++ b/pandas/tests/indexes/interval/test_interval.py @@ -783,19 +783,19 @@ def test_non_contiguous(self, closed): assert 1.5 not in index - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_union(self, closed, sort): index = self.create_index(closed=closed) other = IntervalIndex.from_breaks(range(5, 13), closed=closed) expected = IntervalIndex.from_breaks(range(13), closed=closed) result = index[::-1].union(other, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, expected) assert tm.equalContents(result, expected) result = other[::-1].union(index, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, expected) assert tm.equalContents(result, expected) @@ -812,19 +812,19 @@ def test_union(self, closed, sort): result = index.union(other, sort=sort) tm.assert_index_equal(result, index) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection(self, closed, sort): index = self.create_index(closed=closed) other = IntervalIndex.from_breaks(range(5, 13), closed=closed) expected = IntervalIndex.from_breaks(range(5, 11), closed=closed) result = index[::-1].intersection(other, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, expected) assert tm.equalContents(result, expected) result = other[::-1].intersection(index, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, expected) assert tm.equalContents(result, expected) @@ -842,14 +842,14 @@ def test_intersection(self, closed, sort): result = index.intersection(other, sort=sort) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference(self, closed, sort): index = IntervalIndex.from_arrays([1, 0, 3, 2], [1, 2, 3, 4], closed=closed) result = index.difference(index[:1], sort=sort) expected = index[1:] - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) @@ -864,19 +864,19 @@ def test_difference(self, closed, sort): result = index.difference(other, sort=sort) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_symmetric_difference(self, closed, sort): index = self.create_index(closed=closed) result = index[1:].symmetric_difference(index[:-1], sort=sort) expected = IntervalIndex([index[0], index[-1]]) - if sort: + if sort is None: tm.assert_index_equal(result, expected) assert tm.equalContents(result, expected) # GH 19101: empty result, same dtype result = index.symmetric_difference(index, sort=sort) expected = IntervalIndex(np.array([], dtype='int64'), closed=closed) - if sort: + if sort is None: tm.assert_index_equal(result, expected) assert tm.equalContents(result, expected) @@ -888,7 +888,7 @@ def test_symmetric_difference(self, closed, sort): @pytest.mark.parametrize('op_name', [ 'union', 'intersection', 'difference', 'symmetric_difference']) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_set_operation_errors(self, closed, op_name, sort): index = self.create_index(closed=closed) set_op = getattr(index, op_name) diff --git a/pandas/tests/indexes/multi/test_set_ops.py b/pandas/tests/indexes/multi/test_set_ops.py index 208d6cf1c639f..41a0e1e59e8a5 100644 --- a/pandas/tests/indexes/multi/test_set_ops.py +++ b/pandas/tests/indexes/multi/test_set_ops.py @@ -9,7 +9,7 @@ @pytest.mark.parametrize("case", [0.5, "xxx"]) -@pytest.mark.parametrize("sort", [True, False]) +@pytest.mark.parametrize("sort", [None, False]) @pytest.mark.parametrize("method", ["intersection", "union", "difference", "symmetric_difference"]) def test_set_ops_error_cases(idx, case, sort, method): @@ -19,13 +19,13 @@ def test_set_ops_error_cases(idx, case, sort, method): getattr(idx, method)(case, sort=sort) -@pytest.mark.parametrize("sort", [True, False]) +@pytest.mark.parametrize("sort", [None, False]) def test_intersection_base(idx, sort): first = idx[:5] second = idx[:3] intersect = first.intersection(second, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(intersect, second.sort_values()) assert tm.equalContents(intersect, second) @@ -34,7 +34,7 @@ def test_intersection_base(idx, sort): for klass in [np.array, Series, list]] for case in cases: result = first.intersection(case, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, second.sort_values()) assert tm.equalContents(result, second) @@ -43,13 +43,13 @@ def test_intersection_base(idx, sort): first.intersection([1, 2, 3], sort=sort) -@pytest.mark.parametrize("sort", [True, False]) +@pytest.mark.parametrize("sort", [None, False]) def test_union_base(idx, sort): first = idx[3:] second = idx[:5] everything = idx union = first.union(second, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(union, everything.sort_values()) assert tm.equalContents(union, everything) @@ -58,7 +58,7 @@ def test_union_base(idx, sort): for klass in [np.array, Series, list]] for case in cases: result = first.union(case, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, everything.sort_values()) assert tm.equalContents(result, everything) @@ -67,13 +67,13 @@ def test_union_base(idx, sort): first.union([1, 2, 3], sort=sort) -@pytest.mark.parametrize("sort", [True, False]) +@pytest.mark.parametrize("sort", [None, False]) def test_difference_base(idx, sort): second = idx[4:] answer = idx[:4] result = idx.difference(second, sort=sort) - if sort: + if sort is None: answer = answer.sort_values() assert result.equals(answer) @@ -91,14 +91,14 @@ def test_difference_base(idx, sort): idx.difference([1, 2, 3], sort=sort) -@pytest.mark.parametrize("sort", [True, False]) +@pytest.mark.parametrize("sort", [None, False]) def test_symmetric_difference(idx, sort): first = idx[1:] second = idx[:-1] answer = idx[[-1, 0]] result = first.symmetric_difference(second, sort=sort) - if sort: + if sort is None: answer = answer.sort_values() tm.assert_index_equal(result, answer) @@ -121,14 +121,14 @@ def test_empty(idx): assert idx[:0].empty -@pytest.mark.parametrize("sort", [True, False]) +@pytest.mark.parametrize("sort", [None, False]) def test_difference(idx, sort): first = idx result = first.difference(idx[-3:], sort=sort) vals = idx[:-3].values - if sort: + if sort is None: vals = sorted(vals) expected = MultiIndex.from_tuples(vals, @@ -189,14 +189,62 @@ def test_difference(idx, sort): first.difference([1, 2, 3, 4, 5], sort=sort) -@pytest.mark.parametrize("sort", [True, False]) +def test_difference_sort_special(): + # GH-24959 + idx = pd.MultiIndex.from_product([[1, 0], ['a', 'b']]) + # sort=None, the default + result = idx.difference([]) + tm.assert_index_equal(result, idx) + + +@pytest.mark.xfail(reason="Not implemented.") +def test_difference_sort_special_true(): + # TODO decide on True behaviour + idx = pd.MultiIndex.from_product([[1, 0], ['a', 'b']]) + result = idx.difference([], sort=True) + expected = pd.MultiIndex.from_product([[0, 1], ['a', 'b']]) + tm.assert_index_equal(result, expected) + + +def test_difference_sort_incomparable(): + # GH-24959 + idx = pd.MultiIndex.from_product([[1, pd.Timestamp('2000'), 2], + ['a', 'b']]) + + other = pd.MultiIndex.from_product([[3, pd.Timestamp('2000'), 4], + ['c', 'd']]) + # sort=None, the default + # MultiIndex.difference deviates here from other difference + # implementations in not catching the TypeError + with pytest.raises(TypeError): + result = idx.difference(other) + + # sort=False + result = idx.difference(other, sort=False) + tm.assert_index_equal(result, idx) + + +@pytest.mark.xfail(reason="Not implemented.") +def test_difference_sort_incomparable_true(): + # TODO decide on True behaviour + # # sort=True, raises + idx = pd.MultiIndex.from_product([[1, pd.Timestamp('2000'), 2], + ['a', 'b']]) + other = pd.MultiIndex.from_product([[3, pd.Timestamp('2000'), 4], + ['c', 'd']]) + + with pytest.raises(TypeError): + idx.difference(other, sort=True) + + +@pytest.mark.parametrize("sort", [None, False]) def test_union(idx, sort): piece1 = idx[:5][::-1] piece2 = idx[3:] the_union = piece1.union(piece2, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(the_union, idx.sort_values()) assert tm.equalContents(the_union, idx) @@ -225,14 +273,14 @@ def test_union(idx, sort): # assert result.equals(result2) -@pytest.mark.parametrize("sort", [True, False]) +@pytest.mark.parametrize("sort", [None, False]) def test_intersection(idx, sort): piece1 = idx[:5][::-1] piece2 = idx[3:] the_int = piece1.intersection(piece2, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(the_int, idx[3:5]) assert tm.equalContents(the_int, idx[3:5]) @@ -249,3 +297,76 @@ def test_intersection(idx, sort): # tuples = _index.values # result = _index & tuples # assert result.equals(tuples) + + +def test_intersect_equal_sort(): + # GH-24959 + idx = pd.MultiIndex.from_product([[1, 0], ['a', 'b']]) + tm.assert_index_equal(idx.intersection(idx, sort=False), idx) + tm.assert_index_equal(idx.intersection(idx, sort=None), idx) + + +@pytest.mark.xfail(reason="Not implemented.") +def test_intersect_equal_sort_true(): + # TODO decide on True behaviour + idx = pd.MultiIndex.from_product([[1, 0], ['a', 'b']]) + sorted_ = pd.MultiIndex.from_product([[0, 1], ['a', 'b']]) + tm.assert_index_equal(idx.intersection(idx, sort=True), sorted_) + + +@pytest.mark.parametrize('slice_', [slice(None), slice(0)]) +def test_union_sort_other_empty(slice_): + # https://github.com/pandas-dev/pandas/issues/24959 + idx = pd.MultiIndex.from_product([[1, 0], ['a', 'b']]) + + # default, sort=None + other = idx[slice_] + tm.assert_index_equal(idx.union(other), idx) + # MultiIndex does not special case empty.union(idx) + # tm.assert_index_equal(other.union(idx), idx) + + # sort=False + tm.assert_index_equal(idx.union(other, sort=False), idx) + + +@pytest.mark.xfail(reason="Not implemented.") +def test_union_sort_other_empty_sort(slice_): + # TODO decide on True behaviour + # # sort=True + idx = pd.MultiIndex.from_product([[1, 0], ['a', 'b']]) + other = idx[:0] + result = idx.union(other, sort=True) + expected = pd.MultiIndex.from_product([[0, 1], ['a', 'b']]) + tm.assert_index_equal(result, expected) + + +def test_union_sort_other_incomparable(): + # https://github.com/pandas-dev/pandas/issues/24959 + idx = pd.MultiIndex.from_product([[1, pd.Timestamp('2000')], ['a', 'b']]) + + # default, sort=None + result = idx.union(idx[:1]) + tm.assert_index_equal(result, idx) + + # sort=False + result = idx.union(idx[:1], sort=False) + tm.assert_index_equal(result, idx) + + +@pytest.mark.xfail(reason="Not implemented.") +def test_union_sort_other_incomparable_sort(): + # TODO decide on True behaviour + # # sort=True + idx = pd.MultiIndex.from_product([[1, pd.Timestamp('2000')], ['a', 'b']]) + with pytest.raises(TypeError, match='Cannot compare'): + idx.union(idx[:1], sort=True) + + +@pytest.mark.parametrize("method", ['union', 'intersection', 'difference', + 'symmetric_difference']) +def test_setops_disallow_true(method): + idx1 = pd.MultiIndex.from_product([['a', 'b'], [1, 2]]) + idx2 = pd.MultiIndex.from_product([['b', 'c'], [1, 2]]) + + with pytest.raises(ValueError, match="The 'sort' keyword only takes"): + getattr(idx1, method)(idx2, sort=True) diff --git a/pandas/tests/indexes/period/test_period.py b/pandas/tests/indexes/period/test_period.py index 464ff7aa5d58d..dc9a32d75d272 100644 --- a/pandas/tests/indexes/period/test_period.py +++ b/pandas/tests/indexes/period/test_period.py @@ -77,7 +77,7 @@ def test_no_millisecond_field(self): with pytest.raises(AttributeError): DatetimeIndex([]).millisecond - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_freq(self, sort): # GH14323: difference of Period MUST preserve frequency # but the ability to union results must be preserved diff --git a/pandas/tests/indexes/period/test_setops.py b/pandas/tests/indexes/period/test_setops.py index a97ab47bcda16..bf29edad4841e 100644 --- a/pandas/tests/indexes/period/test_setops.py +++ b/pandas/tests/indexes/period/test_setops.py @@ -38,7 +38,7 @@ def test_join_does_not_recur(self): df.columns[0], df.columns[1]], object) tm.assert_index_equal(res, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_union(self, sort): # union other1 = pd.period_range('1/1/2000', freq='D', periods=5) @@ -97,11 +97,11 @@ def test_union(self, sort): (rng8, other8, expected8)]: result_union = rng.union(other, sort=sort) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result_union, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_union_misc(self, sort): index = period_range('1/1/2000', '1/20/2000', freq='D') @@ -110,7 +110,7 @@ def test_union_misc(self, sort): # not in order result = _permute(index[:-5]).union(_permute(index[10:]), sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, index) assert tm.equalContents(result, index) @@ -139,7 +139,7 @@ def test_union_dataframe_index(self): exp = pd.period_range('1/1/1980', '1/1/2012', freq='M') tm.assert_index_equal(df.index, exp) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection(self, sort): index = period_range('1/1/2000', '1/20/2000', freq='D') @@ -150,7 +150,7 @@ def test_intersection(self, sort): left = _permute(index[:-5]) right = _permute(index[10:]) result = left.intersection(right, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, index[10:-5]) assert tm.equalContents(result, index[10:-5]) @@ -164,7 +164,7 @@ def test_intersection(self, sort): with pytest.raises(period.IncompatibleFrequency): index.intersection(index3, sort=sort) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection_cases(self, sort): base = period_range('6/1/2000', '6/30/2000', freq='D', name='idx') @@ -210,7 +210,7 @@ def test_intersection_cases(self, sort): for (rng, expected) in [(rng2, expected2), (rng3, expected3), (rng4, expected4)]: result = base.intersection(rng, sort=sort) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) assert result.name == expected.name @@ -224,7 +224,7 @@ def test_intersection_cases(self, sort): result = rng.intersection(rng[0:0]) assert len(result) == 0 - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference(self, sort): # diff period_rng = ['1/3/2000', '1/2/2000', '1/1/2000', '1/5/2000', @@ -276,6 +276,6 @@ def test_difference(self, sort): (rng6, other6, expected6), (rng7, other7, expected7), ]: result_difference = rng.difference(other, sort=sort) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result_difference, expected) diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 20e439de46bde..c99007cef90d4 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -3,6 +3,7 @@ from collections import defaultdict from datetime import datetime, timedelta import math +import operator import sys import numpy as np @@ -684,12 +685,12 @@ def test_empty_fancy_raises(self, attr): # np.ndarray only accepts ndarray of int & bool dtypes, so should Index pytest.raises(IndexError, index.__getitem__, empty_farr) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection(self, sort): first = self.strIndex[:20] second = self.strIndex[:10] intersect = first.intersection(second, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(intersect, second.sort_values()) assert tm.equalContents(intersect, second) @@ -701,7 +702,7 @@ def test_intersection(self, sort): (Index([3, 4, 5, 6, 7], name="index"), True), # preserve same name (Index([3, 4, 5, 6, 7], name="other"), False), # drop diff names (Index([3, 4, 5, 6, 7]), False)]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection_name_preservation(self, index2, keeps_name, sort): index1 = Index([1, 2, 3, 4, 5], name='index') expected = Index([3, 4, 5]) @@ -715,7 +716,7 @@ def test_intersection_name_preservation(self, index2, keeps_name, sort): @pytest.mark.parametrize("first_name,second_name,expected_name", [ ('A', 'A', 'A'), ('A', 'B', None), (None, 'B', None)]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection_name_preservation2(self, first_name, second_name, expected_name, sort): first = self.strIndex[5:20] @@ -728,7 +729,7 @@ def test_intersection_name_preservation2(self, first_name, second_name, @pytest.mark.parametrize("index2,keeps_name", [ (Index([4, 7, 6, 5, 3], name='index'), True), (Index([4, 7, 6, 5, 3], name='other'), False)]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection_monotonic(self, index2, keeps_name, sort): index1 = Index([5, 3, 2, 4, 1], name='index') expected = Index([5, 3, 4]) @@ -737,25 +738,25 @@ def test_intersection_monotonic(self, index2, keeps_name, sort): expected.name = "index" result = index1.intersection(index2, sort=sort) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) @pytest.mark.parametrize("index2,expected_arr", [ (Index(['B', 'D']), ['B']), (Index(['B', 'D', 'A']), ['A', 'B', 'A'])]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection_non_monotonic_non_unique(self, index2, expected_arr, sort): # non-monotonic non-unique index1 = Index(['A', 'B', 'A', 'C']) expected = Index(expected_arr, dtype='object') result = index1.intersection(index2, sort=sort) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersect_str_dates(self, sort): dt_dates = [datetime(2012, 2, 9), datetime(2012, 2, 22)] @@ -770,7 +771,19 @@ def test_intersect_nosort(self): expected = pd.Index(['b', 'a']) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("sort", [True, False]) + def test_intersection_equal_sort(self): + idx = pd.Index(['c', 'a', 'b']) + tm.assert_index_equal(idx.intersection(idx, sort=False), idx) + tm.assert_index_equal(idx.intersection(idx, sort=None), idx) + + @pytest.mark.xfail(reason="Not implemented") + def test_intersection_equal_sort_true(self): + # TODO decide on True behaviour + idx = pd.Index(['c', 'a', 'b']) + sorted_ = pd.Index(['a', 'b', 'c']) + tm.assert_index_equal(idx.intersection(idx, sort=True), sorted_) + + @pytest.mark.parametrize("sort", [None, False]) def test_chained_union(self, sort): # Chained unions handles names correctly i1 = Index([1, 2], name='i1') @@ -787,7 +800,7 @@ def test_chained_union(self, sort): expected = j1.union(j2, sort=sort).union(j3, sort=sort) tm.assert_index_equal(union, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_union(self, sort): # TODO: Replace with fixturesult first = self.strIndex[5:20] @@ -795,13 +808,65 @@ def test_union(self, sort): everything = self.strIndex[:20] union = first.union(second, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(union, everything.sort_values()) assert tm.equalContents(union, everything) + @pytest.mark.parametrize('slice_', [slice(None), slice(0)]) + def test_union_sort_other_special(self, slice_): + # https://github.com/pandas-dev/pandas/issues/24959 + + idx = pd.Index([1, 0, 2]) + # default, sort=None + other = idx[slice_] + tm.assert_index_equal(idx.union(other), idx) + tm.assert_index_equal(other.union(idx), idx) + + # sort=False + tm.assert_index_equal(idx.union(other, sort=False), idx) + + @pytest.mark.xfail(reason="Not implemented") + @pytest.mark.parametrize('slice_', [slice(None), slice(0)]) + def test_union_sort_special_true(self, slice_): + # TODO decide on True behaviour + # sort=True + idx = pd.Index([1, 0, 2]) + # default, sort=None + other = idx[slice_] + + result = idx.union(other, sort=True) + expected = pd.Index([0, 1, 2]) + tm.assert_index_equal(result, expected) + + def test_union_sort_other_incomparable(self): + # https://github.com/pandas-dev/pandas/issues/24959 + idx = pd.Index([1, pd.Timestamp('2000')]) + # default (sort=None) + with tm.assert_produces_warning(RuntimeWarning): + result = idx.union(idx[:1]) + + tm.assert_index_equal(result, idx) + + # sort=None + with tm.assert_produces_warning(RuntimeWarning): + result = idx.union(idx[:1], sort=None) + tm.assert_index_equal(result, idx) + + # sort=False + result = idx.union(idx[:1], sort=False) + tm.assert_index_equal(result, idx) + + @pytest.mark.xfail(reason="Not implemented") + def test_union_sort_other_incomparable_true(self): + # TODO decide on True behaviour + # sort=True + idx = pd.Index([1, pd.Timestamp('2000')]) + with pytest.raises(TypeError, match='.*'): + idx.union(idx[:1], sort=True) + @pytest.mark.parametrize("klass", [ np.array, Series, list]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_union_from_iterables(self, klass, sort): # GH 10149 # TODO: Replace with fixturesult @@ -811,29 +876,30 @@ def test_union_from_iterables(self, klass, sort): case = klass(second.values) result = first.union(case, sort=sort) - if sort: + if sort is None: tm.assert_index_equal(result, everything.sort_values()) assert tm.equalContents(result, everything) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_union_identity(self, sort): # TODO: replace with fixturesult first = self.strIndex[5:20] union = first.union(first, sort=sort) - assert union is first + # i.e. identity is not preserved when sort is True + assert (union is first) is (not sort) union = first.union([], sort=sort) - assert union is first + assert (union is first) is (not sort) union = Index([]).union(first, sort=sort) - assert union is first + assert (union is first) is (not sort) @pytest.mark.parametrize("first_list", [list('ba'), list()]) @pytest.mark.parametrize("second_list", [list('ab'), list()]) @pytest.mark.parametrize("first_name, second_name, expected_name", [ ('A', 'B', None), (None, 'B', None), ('A', None, None)]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_union_name_preservation(self, first_list, second_list, first_name, second_name, expected_name, sort): first = Index(first_list, name=first_name) @@ -842,14 +908,14 @@ def test_union_name_preservation(self, first_list, second_list, first_name, vals = set(first_list).union(second_list) - if sort and len(first_list) > 0 and len(second_list) > 0: + if sort is None and len(first_list) > 0 and len(second_list) > 0: expected = Index(sorted(vals), name=expected_name) tm.assert_index_equal(union, expected) else: expected = Index(vals, name=expected_name) assert tm.equalContents(union, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_union_dt_as_obj(self, sort): # TODO: Replace with fixturesult firstCat = self.strIndex.union(self.dateIndex) @@ -866,6 +932,15 @@ def test_union_dt_as_obj(self, sort): tm.assert_contains_all(self.strIndex, secondCat) tm.assert_contains_all(self.dateIndex, firstCat) + @pytest.mark.parametrize("method", ['union', 'intersection', 'difference', + 'symmetric_difference']) + def test_setops_disallow_true(self, method): + idx1 = pd.Index(['a', 'b']) + idx2 = pd.Index(['b', 'c']) + + with pytest.raises(ValueError, match="The 'sort' keyword only takes"): + getattr(idx1, method)(idx2, sort=True) + def test_map_identity_mapping(self): # GH 12766 # TODO: replace with fixture @@ -987,7 +1062,7 @@ def test_append_empty_preserve_name(self, name, expected): @pytest.mark.parametrize("second_name,expected", [ (None, None), ('name', 'name')]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_name_preservation(self, second_name, expected, sort): # TODO: replace with fixturesult first = self.strIndex[5:20] @@ -1005,7 +1080,7 @@ def test_difference_name_preservation(self, second_name, expected, sort): else: assert result.name == expected - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_empty_arg(self, sort): first = self.strIndex[5:20] first.name == 'name' @@ -1014,7 +1089,7 @@ def test_difference_empty_arg(self, sort): assert tm.equalContents(result, first) assert result.name == first.name - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_identity(self, sort): first = self.strIndex[5:20] first.name == 'name' @@ -1023,7 +1098,7 @@ def test_difference_identity(self, sort): assert len(result) == 0 assert result.name == first.name - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_sort(self, sort): first = self.strIndex[5:20] second = self.strIndex[:10] @@ -1031,12 +1106,12 @@ def test_difference_sort(self, sort): result = first.difference(second, sort) expected = self.strIndex[10:20] - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_symmetric_difference(self, sort): # smoke index1 = Index([5, 2, 3, 4], name='index1') @@ -1045,7 +1120,7 @@ def test_symmetric_difference(self, sort): expected = Index([5, 1]) assert tm.equalContents(result, expected) assert result.name is None - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) @@ -1054,13 +1129,43 @@ def test_symmetric_difference(self, sort): assert tm.equalContents(result, expected) assert result.name is None - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize('opname', ['difference', 'symmetric_difference']) + def test_difference_incomparable(self, opname): + a = pd.Index([3, pd.Timestamp('2000'), 1]) + b = pd.Index([2, pd.Timestamp('1999'), 1]) + op = operator.methodcaller(opname, b) + + # sort=None, the default + result = op(a) + expected = pd.Index([3, pd.Timestamp('2000'), 2, pd.Timestamp('1999')]) + if opname == 'difference': + expected = expected[:2] + tm.assert_index_equal(result, expected) + + # sort=False + op = operator.methodcaller(opname, b, sort=False) + result = op(a) + tm.assert_index_equal(result, expected) + + @pytest.mark.xfail(reason="Not implemented") + @pytest.mark.parametrize('opname', ['difference', 'symmetric_difference']) + def test_difference_incomparable_true(self, opname): + # TODO decide on True behaviour + # # sort=True, raises + a = pd.Index([3, pd.Timestamp('2000'), 1]) + b = pd.Index([2, pd.Timestamp('1999'), 1]) + op = operator.methodcaller(opname, b, sort=True) + + with pytest.raises(TypeError, match='Cannot compare'): + op(a) + + @pytest.mark.parametrize("sort", [None, False]) def test_symmetric_difference_mi(self, sort): index1 = MultiIndex.from_tuples(self.tuples) index2 = MultiIndex.from_tuples([('foo', 1), ('bar', 3)]) result = index1.symmetric_difference(index2, sort=sort) expected = MultiIndex.from_tuples([('bar', 2), ('baz', 3), ('bar', 3)]) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) assert tm.equalContents(result, expected) @@ -1068,18 +1173,18 @@ def test_symmetric_difference_mi(self, sort): @pytest.mark.parametrize("index2,expected", [ (Index([0, 1, np.nan]), Index([2.0, 3.0, 0.0])), (Index([0, 1]), Index([np.nan, 2.0, 3.0, 0.0]))]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_symmetric_difference_missing(self, index2, expected, sort): # GH 13514 change: {nan} - {nan} == {} # (GH 6444, sorting of nans, is no longer an issue) index1 = Index([1, np.nan, 2, 3]) result = index1.symmetric_difference(index2, sort=sort) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_symmetric_difference_non_index(self, sort): index1 = Index([1, 2, 3, 4], name='index1') index2 = np.array([2, 3, 4, 5]) @@ -1093,7 +1198,7 @@ def test_symmetric_difference_non_index(self, sort): assert tm.equalContents(result, expected) assert result.name == 'new_name' - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_type(self, sort): # GH 20040 # If taking difference of a set and itself, it @@ -1104,7 +1209,7 @@ def test_difference_type(self, sort): expected = index.drop(index) tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection_difference(self, sort): # GH 20040 # Test that the intersection of an index with an @@ -1607,11 +1712,11 @@ def test_drop_tuple(self, values, to_drop): ('intersection', np.array([(1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')], dtype=[('num', int), ('let', 'a1')]), - True), + None), ('union', np.array([(1, 'A'), (1, 'B'), (1, 'C'), (2, 'A'), (2, 'B'), (2, 'C')], dtype=[('num', int), ('let', 'a1')]), - True) + None) ]) def test_tuple_union_bug(self, method, expected, sort): index1 = Index(np.array([(1, 'A'), (2, 'A'), (1, 'B'), (2, 'B')], @@ -2259,20 +2364,20 @@ def test_unique_na(self): result = idx.unique() tm.assert_index_equal(result, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection_base(self, sort): # (same results for py2 and py3 but sortedness not tested elsewhere) index = self.create_index() first = index[:5] second = index[:3] - expected = Index([0, 1, 'a']) if sort else Index([0, 'a', 1]) + expected = Index([0, 1, 'a']) if sort is None else Index([0, 'a', 1]) result = first.intersection(second, sort=sort) tm.assert_index_equal(result, expected) @pytest.mark.parametrize("klass", [ np.array, Series, list]) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection_different_type_base(self, klass, sort): # GH 10149 index = self.create_index() @@ -2282,7 +2387,7 @@ def test_intersection_different_type_base(self, klass, sort): result = first.intersection(klass(second.values), sort=sort) assert tm.equalContents(result, second) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_base(self, sort): # (same results for py2 and py3 but sortedness not tested elsewhere) index = self.create_index() @@ -2291,7 +2396,7 @@ def test_difference_base(self, sort): result = first.difference(second, sort) expected = Index([0, 'a', 1]) - if sort: + if sort is None: expected = Index(safe_sort(expected)) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/indexes/test_range.py b/pandas/tests/indexes/test_range.py index bbd1e0ccc19b1..96cf83d477376 100644 --- a/pandas/tests/indexes/test_range.py +++ b/pandas/tests/indexes/test_range.py @@ -503,7 +503,7 @@ def test_join_self(self): joined = self.index.join(self.index, how=kind) assert self.index is joined - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_intersection(self, sort): # intersect with Int64Index other = Index(np.arange(1, 6)) diff --git a/pandas/tests/indexes/timedeltas/test_timedelta.py b/pandas/tests/indexes/timedeltas/test_timedelta.py index 547366ec79094..79210705103ab 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta.py @@ -51,7 +51,7 @@ def test_fillna_timedelta(self): [pd.Timedelta('1 day'), 'x', pd.Timedelta('3 day')], dtype=object) tm.assert_index_equal(idx.fillna('x'), exp) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_freq(self, sort): # GH14323: Difference of TimedeltaIndex should not preserve frequency @@ -69,7 +69,7 @@ def test_difference_freq(self, sort): tm.assert_index_equal(idx_diff, expected) tm.assert_attr_equal('freq', idx_diff, expected) - @pytest.mark.parametrize("sort", [True, False]) + @pytest.mark.parametrize("sort", [None, False]) def test_difference_sort(self, sort): index = pd.TimedeltaIndex(["5 days", "3 days", "2 days", "4 days", @@ -80,7 +80,7 @@ def test_difference_sort(self, sort): expected = TimedeltaIndex(["5 days", "0 days"], freq=None) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(idx_diff, expected) @@ -90,7 +90,7 @@ def test_difference_sort(self, sort): idx_diff = index.difference(other, sort) expected = TimedeltaIndex(["1 days", "0 days"], freq=None) - if sort: + if sort is None: expected = expected.sort_values() tm.assert_index_equal(idx_diff, expected) From 89dd4d6e6313a0632e91ec4568b3068ced3472f4 Mon Sep 17 00:00:00 2001 From: David Liu <36486871+david-liu-brattle-1@users.noreply.github.com> Date: Fri, 1 Feb 2019 15:53:56 -0500 Subject: [PATCH 040/215] BUG: to_clipboard text truncated for Python 3 on Windows for UTF-16 text (#25040) --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/io/clipboard/windows.py | 11 ++++++++--- pandas/tests/io/test_clipboard.py | 20 ++++++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 304a9dd117e45..55b9957763ff6 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -165,6 +165,7 @@ MultiIndex I/O ^^^ +- Fixed bug in missing text when using :meth:`to_clipboard` if copying utf-16 characters in Python 3 on Windows (:issue:`25040`) - - - diff --git a/pandas/io/clipboard/windows.py b/pandas/io/clipboard/windows.py index 3d979a61b5f2d..4f5275af693b7 100644 --- a/pandas/io/clipboard/windows.py +++ b/pandas/io/clipboard/windows.py @@ -29,6 +29,7 @@ def init_windows_clipboard(): HINSTANCE, HMENU, BOOL, UINT, HANDLE) windll = ctypes.windll + msvcrt = ctypes.CDLL('msvcrt') safeCreateWindowExA = CheckedCall(windll.user32.CreateWindowExA) safeCreateWindowExA.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD, INT, INT, @@ -71,6 +72,10 @@ def init_windows_clipboard(): safeGlobalUnlock.argtypes = [HGLOBAL] safeGlobalUnlock.restype = BOOL + wcslen = CheckedCall(msvcrt.wcslen) + wcslen.argtypes = [c_wchar_p] + wcslen.restype = UINT + GMEM_MOVEABLE = 0x0002 CF_UNICODETEXT = 13 @@ -129,13 +134,13 @@ def copy_windows(text): # If the hMem parameter identifies a memory object, # the object must have been allocated using the # function with the GMEM_MOVEABLE flag. - count = len(text) + 1 + count = wcslen(text) + 1 handle = safeGlobalAlloc(GMEM_MOVEABLE, count * sizeof(c_wchar)) locked_handle = safeGlobalLock(handle) - ctypes.memmove(c_wchar_p(locked_handle), - c_wchar_p(text), count * sizeof(c_wchar)) + ctypes.memmove(c_wchar_p(locked_handle), c_wchar_p(text), + count * sizeof(c_wchar)) safeGlobalUnlock(handle) safeSetClipboardData(CF_UNICODETEXT, handle) diff --git a/pandas/tests/io/test_clipboard.py b/pandas/tests/io/test_clipboard.py index 8eb26d9f3dec5..565db92210b0a 100644 --- a/pandas/tests/io/test_clipboard.py +++ b/pandas/tests/io/test_clipboard.py @@ -12,6 +12,7 @@ from pandas.util import testing as tm from pandas.util.testing import makeCustomDataframe as mkdf +from pandas.io.clipboard import clipboard_get, clipboard_set from pandas.io.clipboard.exceptions import PyperclipException try: @@ -30,8 +31,8 @@ def build_kwargs(sep, excel): return kwargs -@pytest.fixture(params=['delims', 'utf8', 'string', 'long', 'nonascii', - 'colwidth', 'mixed', 'float', 'int']) +@pytest.fixture(params=['delims', 'utf8', 'utf16', 'string', 'long', + 'nonascii', 'colwidth', 'mixed', 'float', 'int']) def df(request): data_type = request.param @@ -41,6 +42,10 @@ def df(request): elif data_type == 'utf8': return pd.DataFrame({'a': ['µasd', 'Ωœ∑´'], 'b': ['øπ∆˚¬', 'œ∑´®']}) + elif data_type == 'utf16': + return pd.DataFrame({'a': ['\U0001f44d\U0001f44d', + '\U0001f44d\U0001f44d'], + 'b': ['abc', 'def']}) elif data_type == 'string': return mkdf(5, 3, c_idx_type='s', r_idx_type='i', c_idx_names=[None], r_idx_names=[None]) @@ -225,3 +230,14 @@ def test_invalid_encoding(self, df): @pytest.mark.parametrize('enc', ['UTF-8', 'utf-8', 'utf8']) def test_round_trip_valid_encodings(self, enc, df): self.check_round_trip_frame(df, encoding=enc) + + +@pytest.mark.single +@pytest.mark.clipboard +@pytest.mark.skipif(not _DEPS_INSTALLED, + reason="clipboard primitives not installed") +@pytest.mark.parametrize('data', [u'\U0001f44d...', u'Ωœ∑´...', 'abcd...']) +def test_raw_roundtrip(data): + # PR #25040 wide unicode wasn't copied correctly on PY3 on windows + clipboard_set(data) + assert data == clipboard_get() From 3a0fbfd4899b1c9abec4f024d3cccc8970882691 Mon Sep 17 00:00:00 2001 From: Christopher Whelan Date: Fri, 1 Feb 2019 12:56:05 -0800 Subject: [PATCH 041/215] PERF: use new to_records() argument in to_stata() (#25045) --- doc/source/whatsnew/v0.25.0.rst | 12 ++---------- pandas/io/stata.py | 20 +++++--------------- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 55b9957763ff6..09626be713c4f 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -23,14 +23,6 @@ Other Enhancements - - -.. _whatsnew_0250.performance: - -Performance Improvements -~~~~~~~~~~~~~~~~~~~~~~~~ - - Significant speedup in `SparseArray` initialization that benefits most operations, fixing performance regression introduced in v0.20.0 (:issue:`24985`) - - - .. _whatsnew_0250.api_breaking: Backwards incompatible API changes @@ -69,8 +61,8 @@ Removal of prior version deprecations/changes Performance Improvements ~~~~~~~~~~~~~~~~~~~~~~~~ -- -- +- Significant speedup in `SparseArray` initialization that benefits most operations, fixing performance regression introduced in v0.20.0 (:issue:`24985`) +- `DataFrame.to_stata()` is now faster when outputting data with any string or non-native endian columns (:issue:`25045`) - diff --git a/pandas/io/stata.py b/pandas/io/stata.py index 1b0660171ecac..0bd084f4e5df7 100644 --- a/pandas/io/stata.py +++ b/pandas/io/stata.py @@ -2385,32 +2385,22 @@ def _prepare_data(self): data = self._convert_strls(data) # 3. Convert bad string data to '' and pad to correct length - dtypes = [] - data_cols = [] - has_strings = False + dtypes = {} native_byteorder = self._byteorder == _set_endianness(sys.byteorder) for i, col in enumerate(data): typ = typlist[i] if typ <= self._max_string_length: - has_strings = True data[col] = data[col].fillna('').apply(_pad_bytes, args=(typ,)) stype = 'S{type}'.format(type=typ) - dtypes.append(('c' + str(i), stype)) - string = data[col].str.encode(self._encoding) - data_cols.append(string.values.astype(stype)) + dtypes[col] = stype + data[col] = data[col].str.encode(self._encoding).astype(stype) else: - values = data[col].values dtype = data[col].dtype if not native_byteorder: dtype = dtype.newbyteorder(self._byteorder) - dtypes.append(('c' + str(i), dtype)) - data_cols.append(values) - dtypes = np.dtype(dtypes) + dtypes[col] = dtype - if has_strings or not native_byteorder: - self.data = np.fromiter(zip(*data_cols), dtype=dtypes) - else: - self.data = data.to_records(index=False) + self.data = data.to_records(index=False, column_dtypes=dtypes) def _write_data(self): data = self.data From bb43726e1f52a0ddee45fcf485690719f262870d Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Fri, 1 Feb 2019 15:44:54 -0600 Subject: [PATCH 042/215] DOC: Cleanup 0.24.1 whatsnew (#25084) --- doc/source/whatsnew/v0.24.1.rst | 48 ++------------------------------- 1 file changed, 2 insertions(+), 46 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 1ee4397960b0b..bc45a14666ba2 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -13,7 +13,7 @@ Whats New in 0.24.1 (February XX, 2019) {{ header }} These are the changes in pandas 0.24.1. See :ref:`release` for a full changelog -including other versions of pandas. +including other versions of pandas. See :ref:`whatsnew_0240` for the 0.24.0 changelog. .. _whatsnew_0241.api: @@ -57,52 +57,9 @@ Fixed Regressions - Fixed regression in :func:`merge` when merging an empty ``DataFrame`` with multiple timezone-aware columns on one of the timezone-aware columns (:issue:`25014`). - Fixed regression in :meth:`Series.rename_axis` and :meth:`DataFrame.rename_axis` where passing ``None`` failed to remove the axis name (:issue:`25034`) -.. _whatsnew_0241.enhancements: - -Enhancements -~~~~~~~~~~~~ - - -.. _whatsnew_0241.bug_fixes: - -Bug Fixes -~~~~~~~~~ - -**Conversion** - -- -- -- - -**Indexing** - -- -- -- - -**I/O** - -- -- -- - -**Categorical** - -- -- -- - -**Timezones** - -- -- -- - **Timedelta** + - Bug in :func:`to_timedelta` with `box=False` incorrectly returning a ``datetime64`` object instead of a ``timedelta64`` object (:issue:`24961`) -- -- -- **Reshaping** @@ -116,7 +73,6 @@ Bug Fixes **Other** - Fixed AttributeError when printing a DataFrame's HTML repr after accessing the IPython config object (:issue:`25036`) -- .. _whatsnew_0.241.contributors: From 41778b03e2478d72cba941a3b33202695985548b Mon Sep 17 00:00:00 2001 From: Alexander Hendorf Date: Sat, 2 Feb 2019 22:23:16 +0000 Subject: [PATCH 043/215] Fix quotes position in pandas.core, typos and misspelled parameters. (#25093) --- pandas/core/accessor.py | 18 ++++++++------ pandas/core/common.py | 40 ++++++++++++++++++++++-------- pandas/core/internals/blocks.py | 3 ++- pandas/core/missing.py | 28 ++++++++++----------- pandas/core/resample.py | 9 +++---- pandas/core/sparse/frame.py | 16 +++++++++--- pandas/core/sparse/scipy_sparse.py | 6 +++-- 7 files changed, 76 insertions(+), 44 deletions(-) diff --git a/pandas/core/accessor.py b/pandas/core/accessor.py index 961488ff12e58..050749741e7bd 100644 --- a/pandas/core/accessor.py +++ b/pandas/core/accessor.py @@ -16,11 +16,15 @@ class DirNamesMixin(object): ['asobject', 'base', 'data', 'flags', 'itemsize', 'strides']) def _dir_deletions(self): - """ delete unwanted __dir__ for this object """ + """ + Delete unwanted __dir__ for this object. + """ return self._accessors | self._deprecations def _dir_additions(self): - """ add additional __dir__ for this object """ + """ + Add additional __dir__ for this object. + """ rv = set() for accessor in self._accessors: try: @@ -33,7 +37,7 @@ def _dir_additions(self): def __dir__(self): """ Provide method name lookup and completion - Only provide 'public' methods + Only provide 'public' methods. """ rv = set(dir(type(self))) rv = (rv - self._dir_deletions()) | self._dir_additions() @@ -42,7 +46,7 @@ def __dir__(self): class PandasDelegate(object): """ - an abstract base class for delegating methods/properties + An abstract base class for delegating methods/properties. """ def _delegate_property_get(self, name, *args, **kwargs): @@ -65,10 +69,10 @@ def _add_delegate_accessors(cls, delegate, accessors, typ, ---------- cls : the class to add the methods/properties to delegate : the class to get methods/properties & doc-strings - acccessors : string list of accessors to add + accessors : string list of accessors to add typ : 'property' or 'method' overwrite : boolean, default False - overwrite the method/property in the target class if it exists + overwrite the method/property in the target class if it exists. """ def _create_delegator_property(name): @@ -117,7 +121,7 @@ def delegate_names(delegate, accessors, typ, overwrite=False): ---------- delegate : object the class to get methods/properties & doc-strings - acccessors : Sequence[str] + accessors : Sequence[str] List of accessor to add typ : {'property', 'method'} overwrite : boolean, default False diff --git a/pandas/core/common.py b/pandas/core/common.py index b4de0daa13b16..8bdff9d0b1a84 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -32,7 +32,8 @@ class SettingWithCopyWarning(Warning): def flatten(l): - """Flatten an arbitrarily nested sequence. + """ + Flatten an arbitrarily nested sequence. Parameters ---------- @@ -160,12 +161,16 @@ def cast_scalar_indexer(val): def _not_none(*args): - """Returns a generator consisting of the arguments that are not None""" + """ + Returns a generator consisting of the arguments that are not None. + """ return (arg for arg in args if arg is not None) def _any_none(*args): - """Returns a boolean indicating if any argument is None""" + """ + Returns a boolean indicating if any argument is None. + """ for arg in args: if arg is None: return True @@ -173,7 +178,9 @@ def _any_none(*args): def _all_none(*args): - """Returns a boolean indicating if all arguments are None""" + """ + Returns a boolean indicating if all arguments are None. + """ for arg in args: if arg is not None: return False @@ -181,7 +188,9 @@ def _all_none(*args): def _any_not_none(*args): - """Returns a boolean indicating if any argument is not None""" + """ + Returns a boolean indicating if any argument is not None. + """ for arg in args: if arg is not None: return True @@ -189,7 +198,9 @@ def _any_not_none(*args): def _all_not_none(*args): - """Returns a boolean indicating if all arguments are not None""" + """ + Returns a boolean indicating if all arguments are not None. + """ for arg in args: if arg is None: return False @@ -197,7 +208,9 @@ def _all_not_none(*args): def count_not_none(*args): - """Returns the count of arguments that are not None""" + """ + Returns the count of arguments that are not None. + """ return sum(x is not None for x in args) @@ -277,7 +290,9 @@ def maybe_make_list(obj): def is_null_slice(obj): - """ we have a null slice """ + """ + We have a null slice. + """ return (isinstance(obj, slice) and obj.start is None and obj.stop is None and obj.step is None) @@ -291,7 +306,9 @@ def is_true_slices(l): # TODO: used only once in indexing; belongs elsewhere? def is_full_slice(obj, l): - """ we have a full length slice """ + """ + We have a full length slice. + """ return (isinstance(obj, slice) and obj.start == 0 and obj.stop == l and obj.step is None) @@ -316,7 +333,7 @@ def get_callable_name(obj): def apply_if_callable(maybe_callable, obj, **kwargs): """ Evaluate possibly callable input using obj and kwargs if it is callable, - otherwise return as it is + otherwise return as it is. Parameters ---------- @@ -333,7 +350,8 @@ def apply_if_callable(maybe_callable, obj, **kwargs): def dict_compat(d): """ - Helper function to convert datetimelike-keyed dicts to Timestamp-keyed dict + Helper function to convert datetimelike-keyed dicts + to Timestamp-keyed dict. Parameters ---------- diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 36144c31dfef9..43798d64d1172 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -87,7 +87,8 @@ def __init__(self, values, placement, ndim=None): '{mgr}'.format(val=len(self.values), mgr=len(self.mgr_locs))) def _check_ndim(self, values, ndim): - """ndim inference and validation. + """ + ndim inference and validation. Infers ndim from 'values' if not provided to __init__. Validates that values.ndim and ndim are consistent if and only if diff --git a/pandas/core/missing.py b/pandas/core/missing.py index 15538b8196684..cc7bdf95200d1 100644 --- a/pandas/core/missing.py +++ b/pandas/core/missing.py @@ -1,5 +1,5 @@ """ -Routines for filling missing data +Routines for filling missing data. """ from distutils.version import LooseVersion import operator @@ -116,7 +116,7 @@ def interpolate_1d(xvalues, yvalues, method='linear', limit=None, xvalues and yvalues will each be 1-d arrays of the same length. Bounds_error is currently hardcoded to False since non-scipy ones don't - take it as an argumnet. + take it as an argument. """ # Treat the original, non-scipy methods first. @@ -244,9 +244,9 @@ def interpolate_1d(xvalues, yvalues, method='linear', limit=None, def _interpolate_scipy_wrapper(x, y, new_x, method, fill_value=None, bounds_error=False, order=None, **kwargs): """ - passed off to scipy.interpolate.interp1d. method is scipy's kind. + Passed off to scipy.interpolate.interp1d. method is scipy's kind. Returns an array interpolated at new_x. Add any new methods to - the list in _clean_interp_method + the list in _clean_interp_method. """ try: from scipy import interpolate @@ -314,7 +314,7 @@ def _interpolate_scipy_wrapper(x, y, new_x, method, fill_value=None, def _from_derivatives(xi, yi, x, order=None, der=0, extrapolate=False): """ - Convenience function for interpolate.BPoly.from_derivatives + Convenience function for interpolate.BPoly.from_derivatives. Construct a piecewise polynomial in the Bernstein basis, compatible with the specified values and derivatives at breakpoints. @@ -325,7 +325,7 @@ def _from_derivatives(xi, yi, x, order=None, der=0, extrapolate=False): sorted 1D array of x-coordinates yi : array_like or list of array-likes yi[i][j] is the j-th derivative known at xi[i] - orders : None or int or array_like of ints. Default: None. + order: None or int or array_like of ints. Default: None. Specifies the degree of local polynomials. If not None, some derivatives are ignored. der : int or list @@ -344,8 +344,7 @@ def _from_derivatives(xi, yi, x, order=None, der=0, extrapolate=False): Returns ------- y : scalar or array_like - The result, of length R or length M or M by R, - + The result, of length R or length M or M by R. """ import scipy from scipy import interpolate @@ -418,8 +417,9 @@ def _akima_interpolate(xi, yi, x, der=0, axis=0): def interpolate_2d(values, method='pad', axis=0, limit=None, fill_value=None, dtype=None): - """ perform an actual interpolation of values, values will be make 2-d if - needed fills inplace, returns the result + """ + Perform an actual interpolation of values, values will be make 2-d if + needed fills inplace, returns the result. """ transf = (lambda x: x) if axis == 0 else (lambda x: x.T) @@ -533,13 +533,13 @@ def clean_reindex_fill_method(method): def fill_zeros(result, x, y, name, fill): """ - if this is a reversed op, then flip x,y + If this is a reversed op, then flip x,y - if we have an integer value (or array in y) + If we have an integer value (or array in y) and we have 0's, fill them with the fill, - return the result + return the result. - mask the nan's from x + Mask the nan's from x. """ if fill is None or is_float_dtype(result): return result diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 7723827ff478a..a7204fcd9dd20 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -36,7 +36,6 @@ class Resampler(_GroupBy): - """ Class for resampling datetimelike data, a groupby-like operation. See aggregate, transform, and apply functions on this object. @@ -107,7 +106,7 @@ def __iter__(self): Returns ------- Generator yielding sequence of (name, subsetted object) - for each group + for each group. See Also -------- @@ -286,8 +285,8 @@ def transform(self, arg, *args, **kwargs): Parameters ---------- - func : function - To apply to each group. Should return a Series with the same index + arg : function + To apply to each group. Should return a Series with the same index. Returns ------- @@ -423,7 +422,7 @@ def pad(self, limit=None): Returns ------- - an upsampled Series + An upsampled Series. See Also -------- diff --git a/pandas/core/sparse/frame.py b/pandas/core/sparse/frame.py index 586193fe11850..e0af11d13774c 100644 --- a/pandas/core/sparse/frame.py +++ b/pandas/core/sparse/frame.py @@ -194,7 +194,9 @@ def sp_maker(x): return to_manager(sdict, columns, index) def _init_matrix(self, data, index, columns, dtype=None): - """ Init self from ndarray or list of lists """ + """ + Init self from ndarray or list of lists. + """ data = prep_ndarray(data, copy=False) index, columns = self._prep_index(data, index, columns) data = {idx: data[:, i] for i, idx in enumerate(columns)} @@ -202,7 +204,9 @@ def _init_matrix(self, data, index, columns, dtype=None): def _init_spmatrix(self, data, index, columns, dtype=None, fill_value=None): - """ Init self from scipy.sparse matrix """ + """ + Init self from scipy.sparse matrix. + """ index, columns = self._prep_index(data, index, columns) data = data.tocoo() N = len(index) @@ -302,7 +306,9 @@ def __getstate__(self): _default_kind=self._default_kind) def _unpickle_sparse_frame_compat(self, state): - """ original pickle format """ + """ + Original pickle format + """ series, cols, idx, fv, kind = state if not isinstance(cols, Index): # pragma: no cover @@ -338,7 +344,9 @@ def to_dense(self): return DataFrame(data, index=self.index, columns=self.columns) def _apply_columns(self, func): - """ get new SparseDataFrame applying func to each columns """ + """ + Get new SparseDataFrame applying func to each columns + """ new_data = {col: func(series) for col, series in compat.iteritems(self)} diff --git a/pandas/core/sparse/scipy_sparse.py b/pandas/core/sparse/scipy_sparse.py index 2d0ce2d5e5951..8bb79d753e5f9 100644 --- a/pandas/core/sparse/scipy_sparse.py +++ b/pandas/core/sparse/scipy_sparse.py @@ -90,7 +90,8 @@ def _get_index_subset_to_coord_dict(index, subset, sort_labels=False): def _sparse_series_to_coo(ss, row_levels=(0, ), column_levels=(1, ), sort_labels=False): - """ Convert a SparseSeries to a scipy.sparse.coo_matrix using index + """ + Convert a SparseSeries to a scipy.sparse.coo_matrix using index levels row_levels, column_levels as the row and column labels respectively. Returns the sparse_matrix, row and column labels. """ @@ -116,7 +117,8 @@ def _sparse_series_to_coo(ss, row_levels=(0, ), column_levels=(1, ), def _coo_to_sparse_series(A, dense_index=False): - """ Convert a scipy.sparse.coo_matrix to a SparseSeries. + """ + Convert a scipy.sparse.coo_matrix to a SparseSeries. Use the defaults given in the SparseSeries constructor. """ s = Series(A.data, MultiIndex.from_arrays((A.row, A.col))) From fb02c2567e56232605bb288019affb6c383151dc Mon Sep 17 00:00:00 2001 From: Jeremy Schendel Date: Sat, 2 Feb 2019 15:41:27 -0700 Subject: [PATCH 044/215] CLN: Remove sentinel_factory() in favor of object() (#25074) --- pandas/core/common.py | 7 ------- pandas/io/formats/html.py | 5 ++--- pandas/io/formats/style.py | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/pandas/core/common.py b/pandas/core/common.py index 8bdff9d0b1a84..0e92a5056daae 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -401,13 +401,6 @@ def standardize_mapping(into): return into -def sentinel_factory(): - class Sentinel(object): - pass - - return Sentinel() - - def random_state(state=None): """ Helper function for processing random_state arguments. diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index f41749e0a7745..2d8b40016c9af 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -12,7 +12,6 @@ from pandas.core.dtypes.generic import ABCMultiIndex from pandas import compat -import pandas.core.common as com from pandas.core.config import get_option from pandas.io.common import _is_url @@ -190,7 +189,7 @@ def _write_col_header(self, indent): if self.fmt.sparsify: # GH3547 - sentinel = com.sentinel_factory() + sentinel = object() else: sentinel = False levels = self.columns.format(sparsify=sentinel, adjoin=False, @@ -386,7 +385,7 @@ def _write_hierarchical_rows(self, fmt_values, indent): if self.fmt.sparsify: # GH3547 - sentinel = com.sentinel_factory() + sentinel = object() levels = frame.index.format(sparsify=sentinel, adjoin=False, names=False) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 598453eb92d25..61862ee0028d3 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -1322,7 +1322,7 @@ def _get_level_lengths(index, hidden_elements=None): Result is a dictionary of (level, inital_position): span """ - sentinel = com.sentinel_factory() + sentinel = object() levels = index.format(sparsify=sentinel, adjoin=False, names=False) if hidden_elements is None: From f75a220ff1e5e027ef2b070430fd7f4490cdcbf0 Mon Sep 17 00:00:00 2001 From: robbuckley <20515024+robbuckley@users.noreply.github.com> Date: Sat, 2 Feb 2019 22:54:09 +0000 Subject: [PATCH 045/215] TST: remove DST transition scenarios from tc #24689 (#24736) --- .../tests/indexes/datetimes/test_timezones.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_timezones.py b/pandas/tests/indexes/datetimes/test_timezones.py index 8bcc9296cb010..12c1b15733895 100644 --- a/pandas/tests/indexes/datetimes/test_timezones.py +++ b/pandas/tests/indexes/datetimes/test_timezones.py @@ -434,24 +434,19 @@ def test_dti_tz_localize_utc_conversion(self, tz): with pytest.raises(pytz.NonExistentTimeError): rng.tz_localize(tz) - @pytest.mark.parametrize('idx', [ - date_range(start='2014-01-01', end='2014-12-31', freq='M'), - date_range(start='2014-01-01', end='2014-12-31', freq='D'), - date_range(start='2014-01-01', end='2014-03-01', freq='H'), - date_range(start='2014-08-01', end='2014-10-31', freq='T') - ]) - def test_dti_tz_localize_roundtrip(self, tz_aware_fixture, idx): + def test_dti_tz_localize_roundtrip(self, tz_aware_fixture): + # note: this tz tests that a tz-naive index can be localized + # and de-localized successfully, when there are no DST transitions + # in the range. + idx = date_range(start='2014-06-01', end='2014-08-30', freq='15T') tz = tz_aware_fixture localized = idx.tz_localize(tz) - expected = date_range(start=idx[0], end=idx[-1], freq=idx.freq, - tz=tz) - tm.assert_index_equal(localized, expected) + # cant localize a tz-aware object with pytest.raises(TypeError): localized.tz_localize(tz) - reset = localized.tz_localize(None) - tm.assert_index_equal(reset, idx) assert reset.tzinfo is None + tm.assert_index_equal(reset, idx) def test_dti_tz_localize_naive(self): rng = date_range('1/1/2011', periods=100, freq='H') From 800d38b90fe21d5aa87a60470611475fd6d9e604 Mon Sep 17 00:00:00 2001 From: Florian Rathgeber Date: Sun, 3 Feb 2019 06:48:21 -0800 Subject: [PATCH 046/215] BLD: remove spellcheck from Makefile (#25111) --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index d2bd067950fd0..956ff52338839 100644 --- a/Makefile +++ b/Makefile @@ -23,4 +23,3 @@ doc: cd doc; \ python make.py clean; \ python make.py html - python make.py spellcheck From 375c7906e642f0bc386a1d4df2bd46d02284793d Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Sun, 3 Feb 2019 17:43:32 +0100 Subject: [PATCH 047/215] DOC: small clean-up of 0.24.1 whatsnew (#25096) --- doc/source/whatsnew/v0.24.1.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index bc45a14666ba2..2e4d96dcf9f11 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -50,16 +50,19 @@ The `sort` option for :meth:`Index.intersection` has changed in three ways. Fixed Regressions ~~~~~~~~~~~~~~~~~ -- Bug in :meth:`DataFrame.itertuples` with ``records`` orient raising an ``AttributeError`` when the ``DataFrame`` contained more than 255 columns (:issue:`24939`) -- Bug in :meth:`DataFrame.itertuples` orient converting integer column names to strings prepended with an underscore (:issue:`24940`) +- Fixed regression in :meth:`DataFrame.to_dict` with ``records`` orient raising an + ``AttributeError`` when the ``DataFrame`` contained more than 255 columns, or + wrongly converting column names that were not valid python identifiers (:issue:`24939`, :issue:`24940`). - Fixed regression in :func:`read_sql` when passing certain queries with MySQL/pymysql (:issue:`24988`). - Fixed regression in :class:`Index.intersection` incorrectly sorting the values by default (:issue:`24959`). - Fixed regression in :func:`merge` when merging an empty ``DataFrame`` with multiple timezone-aware columns on one of the timezone-aware columns (:issue:`25014`). - Fixed regression in :meth:`Series.rename_axis` and :meth:`DataFrame.rename_axis` where passing ``None`` failed to remove the axis name (:issue:`25034`) +- Fixed regression in :func:`to_timedelta` with `box=False` incorrectly returning a ``datetime64`` object instead of a ``timedelta64`` object (:issue:`24961`) -**Timedelta** +.. _whatsnew_0241.bug_fixes: -- Bug in :func:`to_timedelta` with `box=False` incorrectly returning a ``datetime64`` object instead of a ``timedelta64`` object (:issue:`24961`) +Bug Fixes +~~~~~~~~~ **Reshaping** @@ -69,7 +72,6 @@ Fixed Regressions - Fixed the warning for implicitly registered matplotlib converters not showing. See :ref:`whatsnew_0211.converters` for more (:issue:`24963`). - **Other** - Fixed AttributeError when printing a DataFrame's HTML repr after accessing the IPython config object (:issue:`25036`) From a814ea480e416f14550d9dd9e16c19d69619663f Mon Sep 17 00:00:00 2001 From: Noora Husseini Date: Sun, 3 Feb 2019 16:55:17 +0000 Subject: [PATCH 048/215] DOC: small doc fix to Series.repeat (#25115) --- pandas/core/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index a25aa86a47927..e8c6cf9096948 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1120,7 +1120,7 @@ def repeat(self, repeats, axis=None): Returns ------- - repeated_series : Series + Series Newly created Series with repeated elements. See Also From a488ab8c2a63a93f0a3e314fe5b1ba54b43d4118 Mon Sep 17 00:00:00 2001 From: alimcmaster1 Date: Sun, 3 Feb 2019 17:44:31 +0000 Subject: [PATCH 049/215] TST: tests for categorical apply (#25095) --- pandas/tests/series/test_apply.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 90cf6916df0d1..162a27db34cb1 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -163,6 +163,18 @@ def test_apply_dict_depr(self): with tm.assert_produces_warning(FutureWarning): tsdf.A.agg({'foo': ['sum', 'mean']}) + @pytest.mark.parametrize('series', [ + ['1-1', '1-1', np.NaN], + ['1-1', '1-2', np.NaN]]) + def test_apply_categorical_with_nan_values(self, series): + # GH 20714 bug fixed in: GH 24275 + s = pd.Series(series, dtype='category') + result = s.apply(lambda x: x.split('-')[0]) + result = result.astype(object) + expected = pd.Series(['1', '1', np.NaN], dtype='category') + expected = expected.astype(object) + tm.assert_series_equal(result, expected) + class TestSeriesAggregate(): From 0d3b4f4077eff2dbdeb066bea66f74e3a031b68f Mon Sep 17 00:00:00 2001 From: topper-123 Date: Sun, 3 Feb 2019 17:45:07 +0000 Subject: [PATCH 050/215] CLN: use dtype in constructor (#25098) --- pandas/core/arrays/categorical.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 35b662eaae9a5..6259ead5ed93e 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -2321,8 +2321,7 @@ def _values_for_factorize(self): @classmethod def _from_factorized(cls, uniques, original): return original._constructor(original.categories.take(uniques), - categories=original.categories, - ordered=original.ordered) + dtype=original.dtype) def equals(self, other): """ @@ -2674,9 +2673,7 @@ def _factorize_from_iterable(values): if is_categorical(values): if isinstance(values, (ABCCategoricalIndex, ABCSeries)): values = values._values - categories = CategoricalIndex(values.categories, - categories=values.categories, - ordered=values.ordered) + categories = CategoricalIndex(values.categories, dtype=values.dtype) codes = values.codes else: # The value of ordered is irrelevant since we don't use cat as such, From 14a2da192aee639dc14ccb36aff6b3f08bcdf6ec Mon Sep 17 00:00:00 2001 From: Stijn Van Hoey Date: Sun, 3 Feb 2019 18:58:22 +0100 Subject: [PATCH 051/215] DOC: frame.py doctest fixing (#25097) --- ci/code_checks.sh | 2 +- pandas/core/frame.py | 163 ++++++++++++++++++++++++++---------------- pandas/core/window.py | 23 +++--- 3 files changed, 118 insertions(+), 70 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index c8bfc564e7573..f71d2ce68f800 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -206,7 +206,7 @@ if [[ -z "$CHECK" || "$CHECK" == "doctests" ]]; then MSG='Doctests frame.py' ; echo $MSG pytest -q --doctest-modules pandas/core/frame.py \ - -k"-axes -combine -itertuples -join -pivot_table -query -reindex -reindex_axis -round" + -k" -itertuples -join -reindex -reindex_axis -round" RET=$(($RET + $?)) ; echo $MSG "DONE" MSG='Doctests series.py' ; echo $MSG diff --git a/pandas/core/frame.py b/pandas/core/frame.py index ade05ab27093e..11a5c2c065f93 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -483,7 +483,7 @@ def axes(self): -------- >>> df = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]}) >>> df.axes - [RangeIndex(start=0, stop=2, step=1), Index(['coll', 'col2'], + [RangeIndex(start=0, stop=2, step=1), Index(['col1', 'col2'], dtype='object')] """ return [self.index, self.columns] @@ -3016,28 +3016,30 @@ def query(self, expr, inplace=False, **kwargs): Parameters ---------- - expr : string + expr : str The query string to evaluate. You can refer to variables in the environment by prefixing them with an '@' character like ``@a + b``. inplace : bool Whether the query should modify the data in place or return - a modified copy - - .. versionadded:: 0.18.0 - - kwargs : dict + a modified copy. + **kwargs See the documentation for :func:`pandas.eval` for complete details on the keyword arguments accepted by :meth:`DataFrame.query`. + .. versionadded:: 0.18.0 + Returns ------- - q : DataFrame + DataFrame + DataFrame resulting from the provided query expression. See Also -------- - pandas.eval - DataFrame.eval + eval : Evaluate a string describing operations on + DataFrame columns. + DataFrame.eval : Evaluate a string describing operations on + DataFrame columns. Notes ----- @@ -3076,9 +3078,23 @@ def query(self, expr, inplace=False, **kwargs): Examples -------- - >>> df = pd.DataFrame(np.random.randn(10, 2), columns=list('ab')) - >>> df.query('a > b') - >>> df[df.a > df.b] # same result as the previous expression + >>> df = pd.DataFrame({'A': range(1, 6), 'B': range(10, 0, -2)}) + >>> df + A B + 0 1 10 + 1 2 8 + 2 3 6 + 3 4 4 + 4 5 2 + >>> df.query('A > B') + A B + 4 5 2 + + The previous expression is equivalent to + + >>> df[df.A > df.B] + A B + 4 5 2 """ inplace = validate_bool_kwarg(inplace, 'inplace') if not isinstance(expr, compat.string_types): @@ -5142,8 +5158,7 @@ def _combine_const(self, other, func): def combine(self, other, func, fill_value=None, overwrite=True): """ - Perform column-wise combine with another DataFrame based on a - passed function. + Perform column-wise combine with another DataFrame. Combines a DataFrame with `other` DataFrame using `func` to element-wise combine columns. The row and column indexes of the @@ -5159,13 +5174,14 @@ def combine(self, other, func, fill_value=None, overwrite=True): fill_value : scalar value, default None The value to fill NaNs with prior to passing any column to the merge func. - overwrite : boolean, default True + overwrite : bool, default True If True, columns in `self` that do not exist in `other` will be overwritten with NaNs. Returns ------- - result : DataFrame + DataFrame + Combination of the provided DataFrames. See Also -------- @@ -5209,15 +5225,15 @@ def combine(self, other, func, fill_value=None, overwrite=True): >>> df1 = pd.DataFrame({'A': [0, 0], 'B': [None, 4]}) >>> df2 = pd.DataFrame({'A': [1, 1], 'B': [None, 3]}) >>> df1.combine(df2, take_smaller, fill_value=-5) - A B - 0 0 NaN + A B + 0 0 -5.0 1 0 3.0 Example that demonstrates the use of `overwrite` and behavior when the axis differ between the dataframes. >>> df1 = pd.DataFrame({'A': [0, 0], 'B': [4, 4]}) - >>> df2 = pd.DataFrame({'B': [3, 3], 'C': [-10, 1],}, index=[1, 2]) + >>> df2 = pd.DataFrame({'B': [3, 3], 'C': [-10, 1], }, index=[1, 2]) >>> df1.combine(df2, take_smaller) A B C 0 NaN NaN NaN @@ -5232,7 +5248,7 @@ def combine(self, other, func, fill_value=None, overwrite=True): Demonstrating the preference of the passed in dataframe. - >>> df2 = pd.DataFrame({'B': [3, 3], 'C': [1, 1],}, index=[1, 2]) + >>> df2 = pd.DataFrame({'B': [3, 3], 'C': [1, 1], }, index=[1, 2]) >>> df2.combine(df1, take_smaller) A B C 0 0.0 NaN NaN @@ -5716,19 +5732,19 @@ def pivot(self, index=None, columns=None, values=None): This first example aggregates values by taking the sum. - >>> table = pivot_table(df, values='D', index=['A', 'B'], + >>> table = pd.pivot_table(df, values='D', index=['A', 'B'], ... columns=['C'], aggfunc=np.sum) >>> table C large small A B - bar one 4 5 - two 7 6 - foo one 4 1 - two NaN 6 + bar one 4.0 5.0 + two 7.0 6.0 + foo one 4.0 1.0 + two NaN 6.0 We can also fill missing values using the `fill_value` parameter. - >>> table = pivot_table(df, values='D', index=['A', 'B'], + >>> table = pd.pivot_table(df, values='D', index=['A', 'B'], ... columns=['C'], aggfunc=np.sum, fill_value=0) >>> table C large small @@ -5740,12 +5756,11 @@ def pivot(self, index=None, columns=None, values=None): The next example aggregates by taking the mean across multiple columns. - >>> table = pivot_table(df, values=['D', 'E'], index=['A', 'C'], + >>> table = pd.pivot_table(df, values=['D', 'E'], index=['A', 'C'], ... aggfunc={'D': np.mean, ... 'E': np.mean}) >>> table - D E - mean mean + D E A C bar large 5.500000 7.500000 small 5.500000 8.500000 @@ -5755,17 +5770,17 @@ def pivot(self, index=None, columns=None, values=None): We can also calculate multiple types of aggregations for any given value column. - >>> table = pivot_table(df, values=['D', 'E'], index=['A', 'C'], + >>> table = pd.pivot_table(df, values=['D', 'E'], index=['A', 'C'], ... aggfunc={'D': np.mean, ... 'E': [min, max, np.mean]}) >>> table - D E - mean max mean min + D E + mean max mean min A C - bar large 5.500000 9 7.500000 6 - small 5.500000 9 8.500000 8 - foo large 2.000000 5 4.500000 4 - small 2.333333 6 4.333333 2 + bar large 5.500000 9.0 7.500000 6.0 + small 5.500000 9.0 8.500000 8.0 + foo large 2.000000 5.0 4.500000 4.0 + small 2.333333 6.0 4.333333 2.0 """ @Substitution('') @@ -6903,41 +6918,67 @@ def round(self, decimals=0, *args, **kwargs): columns not included in `decimals` will be left as is. Elements of `decimals` which are not columns of the input will be ignored. + *args + Additional keywords have no effect but might be accepted for + compatibility with numpy. + **kwargs + Additional keywords have no effect but might be accepted for + compatibility with numpy. Returns ------- - DataFrame + DataFrame : + A DataFrame with the affected columns rounded to the specified + number of decimal places. See Also -------- - numpy.around - Series.round + numpy.around : Round a numpy array to the given number of decimals. + Series.round : Round a Series to the given number of decimals. Examples -------- - >>> df = pd.DataFrame(np.random.random([3, 3]), - ... columns=['A', 'B', 'C'], index=['first', 'second', 'third']) + >>> df = pd.DataFrame([(.21, .32), (.01, .67), (.66, .03), (.21, .18)], + ... columns=['dogs', 'cats']) >>> df - A B C - first 0.028208 0.992815 0.173891 - second 0.038683 0.645646 0.577595 - third 0.877076 0.149370 0.491027 - >>> df.round(2) - A B C - first 0.03 0.99 0.17 - second 0.04 0.65 0.58 - third 0.88 0.15 0.49 - >>> df.round({'A': 1, 'C': 2}) - A B C - first 0.0 0.992815 0.17 - second 0.0 0.645646 0.58 - third 0.9 0.149370 0.49 - >>> decimals = pd.Series([1, 0, 2], index=['A', 'B', 'C']) + dogs cats + 0 0.21 0.32 + 1 0.01 0.67 + 2 0.66 0.03 + 3 0.21 0.18 + + By providing an integer each column is rounded to the same number + of decimal places + + >>> df.round(1) + dogs cats + 0 0.2 0.3 + 1 0.0 0.7 + 2 0.7 0.0 + 3 0.2 0.2 + + With a dict, the number of places for specific columns can be + specfified with the column names as key and the number of decimal + places as value + + >>> df.round({'dogs': 1, 'cats': 0}) + dogs cats + 0 0.2 0.0 + 1 0.0 1.0 + 2 0.7 0.0 + 3 0.2 0.0 + + Using a Series, the number of places for specific columns can be + specfified with the column names as index and the number of + decimal places as value + + >>> decimals = pd.Series([0, 1], index=['cats', 'dogs']) >>> df.round(decimals) - A B C - first 0.0 1 0.17 - second 0.0 1 0.58 - third 0.9 0 0.49 + dogs cats + 0 0.2 0.0 + 1 0.0 1.0 + 2 0.7 0.0 + 3 0.2 0.0 """ from pandas.core.reshape.concat import concat diff --git a/pandas/core/window.py b/pandas/core/window.py index 5a9157b43ecd6..5556a01307a7e 100644 --- a/pandas/core/window.py +++ b/pandas/core/window.py @@ -2117,7 +2117,7 @@ def _constructor(self): class EWM(_Rolling): r""" - Provides exponential weighted functions. + Provide exponential weighted functions. .. versionadded:: 0.18.0 @@ -2125,16 +2125,17 @@ class EWM(_Rolling): ---------- com : float, optional Specify decay in terms of center of mass, - :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0` + :math:`\alpha = 1 / (1 + com),\text{ for } com \geq 0`. span : float, optional Specify decay in terms of span, - :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1` + :math:`\alpha = 2 / (span + 1),\text{ for } span \geq 1`. halflife : float, optional Specify decay in terms of half-life, - :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } halflife > 0` + :math:`\alpha = 1 - exp(log(0.5) / halflife),\text{ for } + halflife > 0`. alpha : float, optional Specify smoothing factor :math:`\alpha` directly, - :math:`0 < \alpha \leq 1` + :math:`0 < \alpha \leq 1`. .. versionadded:: 0.18.0 @@ -2143,14 +2144,19 @@ class EWM(_Rolling): (otherwise result is NA). adjust : bool, default True Divide by decaying adjustment factor in beginning periods to account - for imbalance in relative weightings (viewing EWMA as a moving average) + for imbalance in relative weightings + (viewing EWMA as a moving average). ignore_na : bool, default False Ignore missing values when calculating weights; - specify True to reproduce pre-0.15.0 behavior + specify True to reproduce pre-0.15.0 behavior. + axis : {0 or 'index', 1 or 'columns'}, default 0 + The axis to use. The value 0 identifies the rows, and 1 + identifies the columns. Returns ------- - a Window sub-classed for the particular operation + DataFrame + A Window sub-classed for the particular operation. See Also -------- @@ -2188,6 +2194,7 @@ class EWM(_Rolling): -------- >>> df = pd.DataFrame({'B': [0, 1, 2, np.nan, 4]}) + >>> df B 0 0.0 1 1.0 From 51e6cb1881d8e63b870043f179c19084cf596885 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Sun, 3 Feb 2019 13:32:58 -0600 Subject: [PATCH 052/215] DOC: 0.24.1 release (#25125) [ci skip] --- doc/source/whatsnew/v0.24.1.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index 2e4d96dcf9f11..c66a990cd168c 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -2,8 +2,8 @@ .. _whatsnew_0241: -Whats New in 0.24.1 (February XX, 2019) ---------------------------------------- +Whats New in 0.24.1 (February 3, 2019) +-------------------------------------- .. warning:: From 1fc38f6d78b922cbc0106c8ac0c045cba5f39ab7 Mon Sep 17 00:00:00 2001 From: h-vetinari <33685575+h-vetinari@users.noreply.github.com> Date: Sun, 3 Feb 2019 21:14:21 +0100 Subject: [PATCH 053/215] Revert set_index inspection/error handling for 0.24.1 (#25085) * DOC: Minor what's new fix (#24933) * Backport PR #24916: BUG-24212 fix regression in #24897 (#24951) * Revert "Backport PR #24916: BUG-24212 fix regression in #24897 (#24951)" This reverts commit 84056c52e3f20ab44921b86a8e0f05275bf8ddb4. --- doc/source/whatsnew/v0.24.1.rst | 1 + pandas/core/frame.py | 55 ++++-------- pandas/tests/frame/test_alter_axes.py | 116 ++++++++++++++++++++++++-- 3 files changed, 130 insertions(+), 42 deletions(-) diff --git a/doc/source/whatsnew/v0.24.1.rst b/doc/source/whatsnew/v0.24.1.rst index c66a990cd168c..be0a2eb682e87 100644 --- a/doc/source/whatsnew/v0.24.1.rst +++ b/doc/source/whatsnew/v0.24.1.rst @@ -58,6 +58,7 @@ Fixed Regressions - Fixed regression in :func:`merge` when merging an empty ``DataFrame`` with multiple timezone-aware columns on one of the timezone-aware columns (:issue:`25014`). - Fixed regression in :meth:`Series.rename_axis` and :meth:`DataFrame.rename_axis` where passing ``None`` failed to remove the axis name (:issue:`25034`) - Fixed regression in :func:`to_timedelta` with `box=False` incorrectly returning a ``datetime64`` object instead of a ``timedelta64`` object (:issue:`24961`) +- Fixed regression where custom hashable types could not be used as column keys in :meth:`DataFrame.set_index` (:issue:`24969`) .. _whatsnew_0241.bug_fixes: diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 11a5c2c065f93..152d2741e1788 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -71,7 +71,7 @@ is_iterator, is_sequence, is_named_tuple) -from pandas.core.dtypes.generic import ABCSeries, ABCIndexClass, ABCMultiIndex +from pandas.core.dtypes.generic import ABCSeries, ABCIndexClass from pandas.core.dtypes.missing import isna, notna from pandas.core import algorithms @@ -4154,33 +4154,8 @@ def set_index(self, keys, drop=True, append=False, inplace=False, 4 16 10 2014 31 """ inplace = validate_bool_kwarg(inplace, 'inplace') - - err_msg = ('The parameter "keys" may be a column key, one-dimensional ' - 'array, or a list containing only valid column keys and ' - 'one-dimensional arrays.') - - if (is_scalar(keys) or isinstance(keys, tuple) - or isinstance(keys, (ABCIndexClass, ABCSeries, np.ndarray))): - # make sure we have a container of keys/arrays we can iterate over - # tuples can appear as valid column keys! + if not isinstance(keys, list): keys = [keys] - elif not isinstance(keys, list): - raise ValueError(err_msg) - - missing = [] - for col in keys: - if (is_scalar(col) or isinstance(col, tuple)): - # if col is a valid column key, everything is fine - # tuples are always considered keys, never as list-likes - if col not in self: - missing.append(col) - elif (not isinstance(col, (ABCIndexClass, ABCSeries, - np.ndarray, list)) - or getattr(col, 'ndim', 1) > 1): - raise ValueError(err_msg) - - if missing: - raise KeyError('{}'.format(missing)) if inplace: frame = self @@ -4191,7 +4166,7 @@ def set_index(self, keys, drop=True, append=False, inplace=False, names = [] if append: names = [x for x in self.index.names] - if isinstance(self.index, ABCMultiIndex): + if isinstance(self.index, MultiIndex): for i in range(self.index.nlevels): arrays.append(self.index._get_level_values(i)) else: @@ -4199,23 +4174,29 @@ def set_index(self, keys, drop=True, append=False, inplace=False, to_remove = [] for col in keys: - if isinstance(col, ABCMultiIndex): - for n in range(col.nlevels): + if isinstance(col, MultiIndex): + # append all but the last column so we don't have to modify + # the end of this loop + for n in range(col.nlevels - 1): arrays.append(col._get_level_values(n)) + + level = col._get_level_values(col.nlevels - 1) names.extend(col.names) - elif isinstance(col, (ABCIndexClass, ABCSeries)): - # if Index then not MultiIndex (treated above) - arrays.append(col) + elif isinstance(col, Series): + level = col._values + names.append(col.name) + elif isinstance(col, Index): + level = col names.append(col.name) - elif isinstance(col, (list, np.ndarray)): - arrays.append(col) + elif isinstance(col, (list, np.ndarray, Index)): + level = col names.append(None) - # from here, col can only be a column label else: - arrays.append(frame[col]._values) + level = frame[col]._values names.append(col) if drop: to_remove.append(col) + arrays.append(level) index = ensure_index_from_sequences(arrays, names) diff --git a/pandas/tests/frame/test_alter_axes.py b/pandas/tests/frame/test_alter_axes.py index 2d1afa2281d44..cc3687f856b4e 100644 --- a/pandas/tests/frame/test_alter_axes.py +++ b/pandas/tests/frame/test_alter_axes.py @@ -253,23 +253,129 @@ def test_set_index_raise_keys(self, frame_of_index_cols, drop, append): df.set_index(['A', df['A'], tuple(df['A'])], drop=drop, append=append) + @pytest.mark.xfail(reason='broken due to revert, see GH 25085') @pytest.mark.parametrize('append', [True, False]) @pytest.mark.parametrize('drop', [True, False]) - @pytest.mark.parametrize('box', [set, iter]) + @pytest.mark.parametrize('box', [set, iter, lambda x: (y for y in x)], + ids=['set', 'iter', 'generator']) def test_set_index_raise_on_type(self, frame_of_index_cols, box, drop, append): df = frame_of_index_cols msg = 'The parameter "keys" may be a column key, .*' - # forbidden type, e.g. set/tuple/iter - with pytest.raises(ValueError, match=msg): + # forbidden type, e.g. set/iter/generator + with pytest.raises(TypeError, match=msg): df.set_index(box(df['A']), drop=drop, append=append) - # forbidden type in list, e.g. set/tuple/iter - with pytest.raises(ValueError, match=msg): + # forbidden type in list, e.g. set/iter/generator + with pytest.raises(TypeError, match=msg): df.set_index(['A', df['A'], box(df['A'])], drop=drop, append=append) + def test_set_index_custom_label_type(self): + # GH 24969 + + class Thing(object): + def __init__(self, name, color): + self.name = name + self.color = color + + def __str__(self): + return "" % (self.name,) + + # necessary for pretty KeyError + __repr__ = __str__ + + thing1 = Thing('One', 'red') + thing2 = Thing('Two', 'blue') + df = DataFrame({thing1: [0, 1], thing2: [2, 3]}) + expected = DataFrame({thing1: [0, 1]}, + index=Index([2, 3], name=thing2)) + + # use custom label directly + result = df.set_index(thing2) + tm.assert_frame_equal(result, expected) + + # custom label wrapped in list + result = df.set_index([thing2]) + tm.assert_frame_equal(result, expected) + + # missing key + thing3 = Thing('Three', 'pink') + msg = "" + with pytest.raises(KeyError, match=msg): + # missing label directly + df.set_index(thing3) + + with pytest.raises(KeyError, match=msg): + # missing label in list + df.set_index([thing3]) + + def test_set_index_custom_label_hashable_iterable(self): + # GH 24969 + + # actual example discussed in GH 24984 was e.g. for shapely.geometry + # objects (e.g. a collection of Points) that can be both hashable and + # iterable; using frozenset as a stand-in for testing here + + class Thing(frozenset): + # need to stabilize repr for KeyError (due to random order in sets) + def __repr__(self): + tmp = sorted(list(self)) + # double curly brace prints one brace in format string + return "frozenset({{{}}})".format(', '.join(map(repr, tmp))) + + thing1 = Thing(['One', 'red']) + thing2 = Thing(['Two', 'blue']) + df = DataFrame({thing1: [0, 1], thing2: [2, 3]}) + expected = DataFrame({thing1: [0, 1]}, + index=Index([2, 3], name=thing2)) + + # use custom label directly + result = df.set_index(thing2) + tm.assert_frame_equal(result, expected) + + # custom label wrapped in list + result = df.set_index([thing2]) + tm.assert_frame_equal(result, expected) + + # missing key + thing3 = Thing(['Three', 'pink']) + msg = '.*' # due to revert, see GH 25085 + with pytest.raises(KeyError, match=msg): + # missing label directly + df.set_index(thing3) + + with pytest.raises(KeyError, match=msg): + # missing label in list + df.set_index([thing3]) + + def test_set_index_custom_label_type_raises(self): + # GH 24969 + + # purposefully inherit from something unhashable + class Thing(set): + def __init__(self, name, color): + self.name = name + self.color = color + + def __str__(self): + return "" % (self.name,) + + thing1 = Thing('One', 'red') + thing2 = Thing('Two', 'blue') + df = DataFrame([[0, 2], [1, 3]], columns=[thing1, thing2]) + + msg = 'unhashable type.*' + + with pytest.raises(TypeError, match=msg): + # use custom label directly + df.set_index(thing2) + + with pytest.raises(TypeError, match=msg): + # custom label wrapped in list + df.set_index([thing2]) + def test_construction_with_categorical_index(self): ci = tm.makeCategoricalIndex(10) ci.name = 'B' From d3c9d6e67491caf9046b0f8efd2d8d153b52b8e3 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Sun, 3 Feb 2019 12:34:54 -0800 Subject: [PATCH 054/215] DOC/CLN: Timezone section in timeseries.rst (#24825) * DOC: Improve timezone documentation in timeseries.rst * edit some of the examples * Address review --- doc/source/user_guide/timeseries.rst | 204 +++++++++++---------------- 1 file changed, 83 insertions(+), 121 deletions(-) diff --git a/doc/source/user_guide/timeseries.rst b/doc/source/user_guide/timeseries.rst index f56ad710973dd..5841125817d03 100644 --- a/doc/source/user_guide/timeseries.rst +++ b/doc/source/user_guide/timeseries.rst @@ -2129,11 +2129,13 @@ These can easily be converted to a ``PeriodIndex``: Time Zone Handling ------------------ -Pandas provides rich support for working with timestamps in different time -zones using ``pytz`` and ``dateutil`` libraries. ``dateutil`` currently is only -supported for fixed offset and tzfile zones. The default library is ``pytz``. -Support for ``dateutil`` is provided for compatibility with other -applications e.g. if you use ``dateutil`` in other Python packages. +pandas provides rich support for working with timestamps in different time +zones using the ``pytz`` and ``dateutil`` libraries. + +.. note:: + + pandas does not yet support ``datetime.timezone`` objects from the standard + library. Working with Time Zones ~~~~~~~~~~~~~~~~~~~~~~~ @@ -2145,13 +2147,16 @@ By default, pandas objects are time zone unaware: rng = pd.date_range('3/6/2012 00:00', periods=15, freq='D') rng.tz is None -To supply the time zone, you can use the ``tz`` keyword to ``date_range`` and -other functions. Dateutil time zone strings are distinguished from ``pytz`` -time zones by starting with ``dateutil/``. +To localize these dates to a time zone (assign a particular time zone to a naive date), +you can use the ``tz_localize`` method or the ``tz`` keyword argument in +:func:`date_range`, :class:`Timestamp`, or :class:`DatetimeIndex`. +You can either pass ``pytz`` or ``dateutil`` time zone objects or Olson time zone database strings. +Olson time zone strings will return ``pytz`` time zone objects by default. +To return ``dateutil`` time zone objects, append ``dateutil/`` before the string. * In ``pytz`` you can find a list of common (and less common) time zones using ``from pytz import common_timezones, all_timezones``. -* ``dateutil`` uses the OS timezones so there isn't a fixed list available. For +* ``dateutil`` uses the OS time zones so there isn't a fixed list available. For common zones, the names are the same as ``pytz``. .. ipython:: python @@ -2159,23 +2164,23 @@ time zones by starting with ``dateutil/``. import dateutil # pytz - rng_pytz = pd.date_range('3/6/2012 00:00', periods=10, freq='D', + rng_pytz = pd.date_range('3/6/2012 00:00', periods=3, freq='D', tz='Europe/London') rng_pytz.tz # dateutil - rng_dateutil = pd.date_range('3/6/2012 00:00', periods=10, freq='D', - tz='dateutil/Europe/London') + rng_dateutil = pd.date_range('3/6/2012 00:00', periods=3, freq='D') + rng_dateutil = rng_dateutil.tz_localize('dateutil/Europe/London') rng_dateutil.tz # dateutil - utc special case - rng_utc = pd.date_range('3/6/2012 00:00', periods=10, freq='D', + rng_utc = pd.date_range('3/6/2012 00:00', periods=3, freq='D', tz=dateutil.tz.tzutc()) rng_utc.tz -Note that the ``UTC`` timezone is a special case in ``dateutil`` and should be constructed explicitly -as an instance of ``dateutil.tz.tzutc``. You can also construct other timezones explicitly first, -which gives you more control over which time zone is used: +Note that the ``UTC`` time zone is a special case in ``dateutil`` and should be constructed explicitly +as an instance of ``dateutil.tz.tzutc``. You can also construct other time +zones objects explicitly first. .. ipython:: python @@ -2183,56 +2188,46 @@ which gives you more control over which time zone is used: # pytz tz_pytz = pytz.timezone('Europe/London') - rng_pytz = pd.date_range('3/6/2012 00:00', periods=10, freq='D', - tz=tz_pytz) + rng_pytz = pd.date_range('3/6/2012 00:00', periods=3, freq='D') + rng_pytz = rng_pytz.tz_localize(tz_pytz) rng_pytz.tz == tz_pytz # dateutil tz_dateutil = dateutil.tz.gettz('Europe/London') - rng_dateutil = pd.date_range('3/6/2012 00:00', periods=10, freq='D', + rng_dateutil = pd.date_range('3/6/2012 00:00', periods=3, freq='D', tz=tz_dateutil) rng_dateutil.tz == tz_dateutil -Timestamps, like Python's ``datetime.datetime`` object can be either time zone -naive or time zone aware. Naive time series and ``DatetimeIndex`` objects can be -*localized* using ``tz_localize``: - -.. ipython:: python - - ts = pd.Series(np.random.randn(len(rng)), rng) - - ts_utc = ts.tz_localize('UTC') - ts_utc - -Again, you can explicitly construct the timezone object first. -You can use the ``tz_convert`` method to convert pandas objects to convert -tz-aware data to another time zone: +To convert a time zone aware pandas object from one time zone to another, +you can use the ``tz_convert`` method. .. ipython:: python - ts_utc.tz_convert('US/Eastern') + rng_pytz.tz_convert('US/Eastern') .. warning:: - Be wary of conversions between libraries. For some zones ``pytz`` and ``dateutil`` have different - definitions of the zone. This is more of a problem for unusual timezones than for + Be wary of conversions between libraries. For some time zones, ``pytz`` and ``dateutil`` have different + definitions of the zone. This is more of a problem for unusual time zones than for 'standard' zones like ``US/Eastern``. .. warning:: - Be aware that a timezone definition across versions of timezone libraries may not - be considered equal. This may cause problems when working with stored data that - is localized using one version and operated on with a different version. - See :ref:`here` for how to handle such a situation. + Be aware that a time zone definition across versions of time zone libraries may not + be considered equal. This may cause problems when working with stored data that + is localized using one version and operated on with a different version. + See :ref:`here` for how to handle such a situation. .. warning:: - It is incorrect to pass a timezone directly into the ``datetime.datetime`` constructor (e.g., - ``datetime.datetime(2011, 1, 1, tz=timezone('US/Eastern'))``. Instead, the datetime - needs to be localized using the localize method on the timezone. + For ``pytz`` time zones, it is incorrect to pass a time zone object directly into + the ``datetime.datetime`` constructor + (e.g., ``datetime.datetime(2011, 1, 1, tz=pytz.timezone('US/Eastern'))``. + Instead, the datetime needs to be localized using the ``localize`` method + on the ``pytz`` time zone object. -Under the hood, all timestamps are stored in UTC. Scalar values from a -``DatetimeIndex`` with a time zone will have their fields (day, hour, minute) +Under the hood, all timestamps are stored in UTC. Values from a time zone aware +:class:`DatetimeIndex` or :class:`Timestamp` will have their fields (day, hour, minute, etc.) localized to the time zone. However, timestamps with the same UTC value are still considered to be equal even if they are in different time zones: @@ -2241,51 +2236,35 @@ still considered to be equal even if they are in different time zones: rng_eastern = rng_utc.tz_convert('US/Eastern') rng_berlin = rng_utc.tz_convert('Europe/Berlin') - rng_eastern[5] - rng_berlin[5] - rng_eastern[5] == rng_berlin[5] - -Like ``Series``, ``DataFrame``, and ``DatetimeIndex``; ``Timestamp`` objects -can be converted to other time zones using ``tz_convert``: - -.. ipython:: python - - rng_eastern[5] - rng_berlin[5] - rng_eastern[5].tz_convert('Europe/Berlin') - -Localization of ``Timestamp`` functions just like ``DatetimeIndex`` and ``Series``: - -.. ipython:: python - - rng[5] - rng[5].tz_localize('Asia/Shanghai') - + rng_eastern[2] + rng_berlin[2] + rng_eastern[2] == rng_berlin[2] -Operations between ``Series`` in different time zones will yield UTC -``Series``, aligning the data on the UTC timestamps: +Operations between :class:`Series` in different time zones will yield UTC +:class:`Series`, aligning the data on the UTC timestamps: .. ipython:: python + ts_utc = pd.Series(range(3), pd.date_range('20130101', periods=3, tz='UTC')) eastern = ts_utc.tz_convert('US/Eastern') berlin = ts_utc.tz_convert('Europe/Berlin') result = eastern + berlin result result.index -To remove timezone from tz-aware ``DatetimeIndex``, use ``tz_localize(None)`` or ``tz_convert(None)``. -``tz_localize(None)`` will remove timezone holding local time representations. -``tz_convert(None)`` will remove timezone after converting to UTC time. +To remove time zone information, use ``tz_localize(None)`` or ``tz_convert(None)``. +``tz_localize(None)`` will remove the time zone yielding the local time representation. +``tz_convert(None)`` will remove the time zone after converting to UTC time. .. ipython:: python didx = pd.date_range(start='2014-08-01 09:00', freq='H', - periods=10, tz='US/Eastern') + periods=3, tz='US/Eastern') didx didx.tz_localize(None) didx.tz_convert(None) - # tz_convert(None) is identical with tz_convert('UTC').tz_localize(None) + # tz_convert(None) is identical to tz_convert('UTC').tz_localize(None) didx.tz_convert('UTC').tz_localize(None) .. _timeseries.timezone_ambiguous: @@ -2293,54 +2272,34 @@ To remove timezone from tz-aware ``DatetimeIndex``, use ``tz_localize(None)`` or Ambiguous Times when Localizing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In some cases, localize cannot determine the DST and non-DST hours when there are -duplicates. This often happens when reading files or database records that simply -duplicate the hours. Passing ``ambiguous='infer'`` into ``tz_localize`` will -attempt to determine the right offset. Below the top example will fail as it -contains ambiguous times and the bottom will infer the right offset. +``tz_localize`` may not be able to determine the UTC offset of a timestamp +because daylight savings time (DST) in a local time zone causes some times to occur +twice within one day ("clocks fall back"). The following options are available: + +* ``'raise'``: Raises a ``pytz.AmbiguousTimeError`` (the default behavior) +* ``'infer'``: Attempt to determine the correct offset base on the monotonicity of the timestamps +* ``'NaT'``: Replaces ambiguous times with ``NaT`` +* ``bool``: ``True`` represents a DST time, ``False`` represents non-DST time. An array-like of ``bool`` values is supported for a sequence of times. .. ipython:: python rng_hourly = pd.DatetimeIndex(['11/06/2011 00:00', '11/06/2011 01:00', - '11/06/2011 01:00', '11/06/2011 02:00', - '11/06/2011 03:00']) + '11/06/2011 01:00', '11/06/2011 02:00']) -This will fail as there are ambiguous times +This will fail as there are ambiguous times (``'11/06/2011 01:00'``) .. code-block:: ipython In [2]: rng_hourly.tz_localize('US/Eastern') AmbiguousTimeError: Cannot infer dst time from Timestamp('2011-11-06 01:00:00'), try using the 'ambiguous' argument -Infer the ambiguous times - -.. ipython:: python - - rng_hourly_eastern = rng_hourly.tz_localize('US/Eastern', ambiguous='infer') - rng_hourly_eastern.to_list() - -In addition to 'infer', there are several other arguments supported. Passing -an array-like of bools or 0s/1s where True represents a DST hour and False a -non-DST hour, allows for distinguishing more than one DST -transition (e.g., if you have multiple records in a database each with their -own DST transition). Or passing 'NaT' will fill in transition times -with not-a-time values. These methods are available in the ``DatetimeIndex`` -constructor as well as ``tz_localize``. +Handle these ambiguous times by specifying the following. .. ipython:: python - rng_hourly_dst = np.array([1, 1, 0, 0, 0]) - rng_hourly.tz_localize('US/Eastern', ambiguous=rng_hourly_dst).to_list() - rng_hourly.tz_localize('US/Eastern', ambiguous='NaT').to_list() - - didx = pd.date_range(start='2014-08-01 09:00', freq='H', - periods=10, tz='US/Eastern') - didx - didx.tz_localize(None) - didx.tz_convert(None) - - # tz_convert(None) is identical with tz_convert('UTC').tz_localize(None) - didx.tz_convert('UCT').tz_localize(None) + rng_hourly.tz_localize('US/Eastern', ambiguous='infer') + rng_hourly.tz_localize('US/Eastern', ambiguous='NaT') + rng_hourly.tz_localize('US/Eastern', ambiguous=[True, True, False, False]) .. _timeseries.timezone_nonexistent: @@ -2348,7 +2307,7 @@ Nonexistent Times when Localizing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A DST transition may also shift the local time ahead by 1 hour creating nonexistent -local times. The behavior of localizing a timeseries with nonexistent times +local times ("clocks spring forward"). The behavior of localizing a timeseries with nonexistent times can be controlled by the ``nonexistent`` argument. The following options are available: * ``'raise'``: Raises a ``pytz.NonExistentTimeError`` (the default behavior) @@ -2382,58 +2341,61 @@ Transform nonexistent times to ``NaT`` or shift the times. .. _timeseries.timezone_series: -TZ Aware Dtypes -~~~~~~~~~~~~~~~ +Time Zone Series Operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``Series/DatetimeIndex`` with a timezone **naive** value are represented with a dtype of ``datetime64[ns]``. +A :class:`Series` with time zone **naive** values is +represented with a dtype of ``datetime64[ns]``. .. ipython:: python s_naive = pd.Series(pd.date_range('20130101', periods=3)) s_naive -``Series/DatetimeIndex`` with a timezone **aware** value are represented with a dtype of ``datetime64[ns, tz]``. +A :class:`Series` with a time zone **aware** values is +represented with a dtype of ``datetime64[ns, tz]`` where ``tz`` is the time zone .. ipython:: python s_aware = pd.Series(pd.date_range('20130101', periods=3, tz='US/Eastern')) s_aware -Both of these ``Series`` can be manipulated via the ``.dt`` accessor, see :ref:`here `. +Both of these :class:`Series` time zone information +can be manipulated via the ``.dt`` accessor, see :ref:`the dt accessor section `. -For example, to localize and convert a naive stamp to timezone aware. +For example, to localize and convert a naive stamp to time zone aware. .. ipython:: python s_naive.dt.tz_localize('UTC').dt.tz_convert('US/Eastern') - -Further more you can ``.astype(...)`` timezone aware (and naive). This operation is effectively a localize AND convert on a naive stamp, and -a convert on an aware stamp. +Time zone information can also be manipulated using the ``astype`` method. +This method can localize and convert time zone naive timestamps or +convert time zone aware timestamps. .. ipython:: python - # localize and convert a naive timezone + # localize and convert a naive time zone s_naive.astype('datetime64[ns, US/Eastern]') # make an aware tz naive s_aware.astype('datetime64[ns]') - # convert to a new timezone + # convert to a new time zone s_aware.astype('datetime64[ns, CET]') .. note:: Using :meth:`Series.to_numpy` on a ``Series``, returns a NumPy array of the data. - NumPy does not currently support timezones (even though it is *printing* in the local timezone!), - therefore an object array of Timestamps is returned for timezone aware data: + NumPy does not currently support time zones (even though it is *printing* in the local time zone!), + therefore an object array of Timestamps is returned for time zone aware data: .. ipython:: python s_naive.to_numpy() s_aware.to_numpy() - By converting to an object array of Timestamps, it preserves the timezone + By converting to an object array of Timestamps, it preserves the time zone information. For example, when converting back to a Series: .. ipython:: python From 88318e3066b5394a007d2ffaab33d866c2f066a7 Mon Sep 17 00:00:00 2001 From: Kara de la Marck Date: Mon, 4 Feb 2019 12:56:02 +0000 Subject: [PATCH 055/215] DOC: Fix validation type error RT04 (#25107) (#25129) --- ci/code_checks.sh | 4 +- pandas/core/algorithms.py | 6 +- pandas/core/dtypes/common.py | 138 ++++++++++++++++++------------- pandas/core/frame.py | 4 +- pandas/core/generic.py | 14 ++-- pandas/core/indexes/accessors.py | 4 +- pandas/core/indexes/base.py | 2 +- pandas/core/indexes/multi.py | 2 +- pandas/core/panel.py | 4 +- pandas/core/series.py | 2 +- pandas/io/formats/style.py | 4 +- 11 files changed, 105 insertions(+), 79 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index f71d2ce68f800..35358306468c5 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -240,8 +240,8 @@ fi ### DOCSTRINGS ### if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then - MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, EX04)' ; echo $MSG - $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR05,EX04 + MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, EX04, RT04)' ; echo $MSG + $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR05,EX04,RT04 RET=$(($RET + $?)) ; echo $MSG "DONE" fi diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index b473a7aef929e..02f33c49c79ae 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -289,9 +289,9 @@ def unique(values): Returns ------- unique values. - - If the input is an Index, the return is an Index - - If the input is a Categorical dtype, the return is a Categorical - - If the input is a Series/ndarray, the return will be an ndarray + If the input is an Index, the return is an Index + If the input is a Categorical dtype, the return is a Categorical + If the input is a Series/ndarray, the return will be an ndarray See Also -------- diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index e9bf0f87088db..0379a3d3eb3cb 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -139,7 +139,8 @@ def is_object_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array-like or dtype is of the object dtype. + boolean + Whether or not the array-like or dtype is of the object dtype. Examples -------- @@ -230,8 +231,8 @@ def is_scipy_sparse(arr): Returns ------- - boolean : Whether or not the array-like is a - scipy.sparse.spmatrix instance. + boolean + Whether or not the array-like is a scipy.sparse.spmatrix instance. Notes ----- @@ -270,7 +271,8 @@ def is_categorical(arr): Returns ------- - boolean : Whether or not the array-like is of a Categorical instance. + boolean + Whether or not the array-like is of a Categorical instance. Examples -------- @@ -305,8 +307,9 @@ def is_datetimetz(arr): Returns ------- - boolean : Whether or not the array-like is a datetime array-like with - a timezone component in its dtype. + boolean + Whether or not the array-like is a datetime array-like with a + timezone component in its dtype. Examples -------- @@ -347,7 +350,8 @@ def is_offsetlike(arr_or_obj): Returns ------- - boolean : Whether the object is a DateOffset or listlike of DatetOffsets + boolean + Whether the object is a DateOffset or listlike of DatetOffsets Examples -------- @@ -381,7 +385,8 @@ def is_period(arr): Returns ------- - boolean : Whether or not the array-like is a periodical index. + boolean + Whether or not the array-like is a periodical index. Examples -------- @@ -411,8 +416,8 @@ def is_datetime64_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array-like or dtype is of - the datetime64 dtype. + boolean + Whether or not the array-like or dtype is of the datetime64 dtype. Examples -------- @@ -442,8 +447,8 @@ def is_datetime64tz_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array-like or dtype is of - a DatetimeTZDtype dtype. + boolean + Whether or not the array-like or dtype is of a DatetimeTZDtype dtype. Examples -------- @@ -480,8 +485,8 @@ def is_timedelta64_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array-like or dtype is - of the timedelta64 dtype. + boolean + Whether or not the array-like or dtype is of the timedelta64 dtype. Examples -------- @@ -511,7 +516,8 @@ def is_period_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array-like or dtype is of the Period dtype. + boolean + Whether or not the array-like or dtype is of the Period dtype. Examples -------- @@ -544,8 +550,8 @@ def is_interval_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array-like or dtype is - of the Interval dtype. + boolean + Whether or not the array-like or dtype is of the Interval dtype. Examples -------- @@ -580,8 +586,8 @@ def is_categorical_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array-like or dtype is - of the Categorical dtype. + boolean + Whether or not the array-like or dtype is of the Categorical dtype. Examples -------- @@ -613,7 +619,8 @@ def is_string_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of the string dtype. + boolean + Whether or not the array or dtype is of the string dtype. Examples -------- @@ -647,8 +654,9 @@ def is_period_arraylike(arr): Returns ------- - boolean : Whether or not the array-like is a periodical - array-like or PeriodIndex instance. + boolean + Whether or not the array-like is a periodical array-like or + PeriodIndex instance. Examples -------- @@ -678,8 +686,9 @@ def is_datetime_arraylike(arr): Returns ------- - boolean : Whether or not the array-like is a datetime - array-like or DatetimeIndex. + boolean + Whether or not the array-like is a datetime array-like or + DatetimeIndex. Examples -------- @@ -713,7 +722,8 @@ def is_datetimelike(arr): Returns ------- - boolean : Whether or not the array-like is a datetime-like array-like. + boolean + Whether or not the array-like is a datetime-like array-like. Examples -------- @@ -754,7 +764,8 @@ def is_dtype_equal(source, target): Returns ---------- - boolean : Whether or not the two dtypes are equal. + boolean + Whether or not the two dtypes are equal. Examples -------- @@ -794,7 +805,8 @@ def is_dtype_union_equal(source, target): Returns ---------- - boolean : Whether or not the two dtypes are equal. + boolean + Whether or not the two dtypes are equal. >>> is_dtype_equal("int", int) True @@ -835,7 +847,8 @@ def is_any_int_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of an integer dtype. + boolean + Whether or not the array or dtype is of an integer dtype. Examples -------- @@ -883,8 +896,9 @@ def is_integer_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of an integer dtype - and not an instance of timedelta64. + boolean + Whether or not the array or dtype is of an integer dtype and + not an instance of timedelta64. Examples -------- @@ -938,8 +952,9 @@ def is_signed_integer_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of a signed integer dtype - and not an instance of timedelta64. + boolean + Whether or not the array or dtype is of a signed integer dtype + and not an instance of timedelta64. Examples -------- @@ -993,8 +1008,8 @@ def is_unsigned_integer_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of an - unsigned integer dtype. + boolean + Whether or not the array or dtype is of an unsigned integer dtype. Examples -------- @@ -1036,7 +1051,8 @@ def is_int64_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of the int64 dtype. + boolean + Whether or not the array or dtype is of the int64 dtype. Notes ----- @@ -1086,7 +1102,8 @@ def is_datetime64_any_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of the datetime64 dtype. + boolean + Whether or not the array or dtype is of the datetime64 dtype. Examples -------- @@ -1126,7 +1143,8 @@ def is_datetime64_ns_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of the datetime64[ns] dtype. + boolean + Whether or not the array or dtype is of the datetime64[ns] dtype. Examples -------- @@ -1178,8 +1196,8 @@ def is_timedelta64_ns_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of the - timedelta64[ns] dtype. + boolean + Whether or not the array or dtype is of the timedelta64[ns] dtype. Examples -------- @@ -1207,8 +1225,9 @@ def is_datetime_or_timedelta_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of a - timedelta64, or datetime64 dtype. + boolean + Whether or not the array or dtype is of a timedelta64, + or datetime64 dtype. Examples -------- @@ -1248,7 +1267,8 @@ def _is_unorderable_exception(e): Returns ------- - boolean : Whether or not the exception raised is an unorderable exception. + boolean + Whether or not the exception raised is an unorderable exception. """ if PY36: @@ -1275,8 +1295,8 @@ def is_numeric_v_string_like(a, b): Returns ------- - boolean : Whether we return a comparing a string-like - object to a numeric array. + boolean + Whether we return a comparing a string-like object to a numeric array. Examples -------- @@ -1332,8 +1352,8 @@ def is_datetimelike_v_numeric(a, b): Returns ------- - boolean : Whether we return a comparing a datetime-like - to a numeric object. + boolean + Whether we return a comparing a datetime-like to a numeric object. Examples -------- @@ -1388,8 +1408,8 @@ def is_datetimelike_v_object(a, b): Returns ------- - boolean : Whether we return a comparing a datetime-like - to an object instance. + boolean + Whether we return a comparing a datetime-like to an object instance. Examples -------- @@ -1442,7 +1462,8 @@ def needs_i8_conversion(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype should be converted to int64. + boolean + Whether or not the array or dtype should be converted to int64. Examples -------- @@ -1480,7 +1501,8 @@ def is_numeric_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of a numeric dtype. + boolean + Whether or not the array or dtype is of a numeric dtype. Examples -------- @@ -1524,7 +1546,8 @@ def is_string_like_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of the string dtype. + boolean + Whether or not the array or dtype is of the string dtype. Examples -------- @@ -1555,7 +1578,8 @@ def is_float_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of a float dtype. + boolean + Whether or not the array or dtype is of a float dtype. Examples -------- @@ -1586,7 +1610,8 @@ def is_bool_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of a boolean dtype. + boolean + Whether or not the array or dtype is of a boolean dtype. Notes ----- @@ -1655,8 +1680,8 @@ def is_extension_type(arr): Returns ------- - boolean : Whether or not the array-like is of a pandas - extension class instance. + boolean + Whether or not the array-like is of a pandas extension class instance. Examples -------- @@ -1760,7 +1785,8 @@ def is_complex_dtype(arr_or_dtype): Returns ------- - boolean : Whether or not the array or dtype is of a compex dtype. + boolean + Whether or not the array or dtype is of a compex dtype. Examples -------- diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 152d2741e1788..be125983ce293 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -7767,10 +7767,10 @@ def quantile(self, q=0.5, axis=0, numeric_only=True, ------- quantiles : Series or DataFrame - - If ``q`` is an array, a DataFrame will be returned where the + If ``q`` is an array, a DataFrame will be returned where the index is ``q``, the columns are the columns of self, and the values are the quantiles. - - If ``q`` is a float, a Series will be returned where the + If ``q`` is a float, a Series will be returned where the index is the columns of self and the values are the quantiles. See Also diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 0312ed6ecf3bf..60079e06d8e79 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -4951,10 +4951,10 @@ def pipe(self, func, *args, **kwargs): Returns ------- DataFrame, Series or scalar - if DataFrame.agg is called with a single function, returns a Series - if DataFrame.agg is called with several functions, returns a DataFrame - if Series.agg is called with single function, returns a scalar - if Series.agg is called with several functions, returns a Series + If DataFrame.agg is called with a single function, returns a Series + If DataFrame.agg is called with several functions, returns a DataFrame + If Series.agg is called with single function, returns a scalar + If Series.agg is called with several functions, returns a Series %(see_also)s @@ -6878,10 +6878,10 @@ def asof(self, where, subset=None): ------- scalar, Series, or DataFrame - * scalar : when `self` is a Series and `where` is a scalar - * Series: when `self` is a Series and `where` is an array-like, + Scalar : when `self` is a Series and `where` is a scalar + Series: when `self` is a Series and `where` is an array-like, or when `self` is a DataFrame and `where` is a scalar - * DataFrame : when `self` is a DataFrame and `where` is an + DataFrame : when `self` is a DataFrame and `where` is an array-like See Also diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index c43469d3c3a81..602e11a08b4ed 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -140,7 +140,7 @@ def to_pydatetime(self): Returns ------- numpy.ndarray - object dtype array containing native Python datetime objects. + Object dtype array containing native Python datetime objects. See Also -------- @@ -208,7 +208,7 @@ def to_pytimedelta(self): Returns ------- a : numpy.ndarray - 1D array containing data with `datetime.timedelta` type. + Array of 1D containing data with `datetime.timedelta` type. See Also -------- diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 2fa034670e885..d0b44db840a29 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -4259,7 +4259,7 @@ def shift(self, periods=1, freq=None): Returns ------- pandas.Index - shifted index + Shifted index See Also -------- diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 14975dbbefa63..e2237afbcac0f 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1391,7 +1391,7 @@ def get_level_values(self, level): Returns ------- values : Index - ``values`` is a level of this MultiIndex converted to + Values is a level of this MultiIndex converted to a single :class:`Index` (or subclass thereof). Examples diff --git a/pandas/core/panel.py b/pandas/core/panel.py index 540192d1a592c..8dd604fb580b4 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -802,7 +802,7 @@ def major_xs(self, key): Returns ------- y : DataFrame - index -> minor axis, columns -> items + Index -> minor axis, columns -> items Notes ----- @@ -826,7 +826,7 @@ def minor_xs(self, key): Returns ------- y : DataFrame - index -> major axis, columns -> items + Index -> major axis, columns -> items Notes ----- diff --git a/pandas/core/series.py b/pandas/core/series.py index e8c6cf9096948..427da96c5e1c4 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2025,7 +2025,7 @@ def quantile(self, q=0.5, interpolation='linear'): Returns ------- quantile : float or Series - if ``q`` is an array, a Series will be returned where the + If ``q`` is an array, a Series will be returned where the index is ``q`` and the values are the quantiles. See Also diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 61862ee0028d3..790cff95827fc 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -433,7 +433,7 @@ def render(self, **kwargs): Returns ------- rendered : str - the rendered HTML + The rendered HTML Notes ----- @@ -1223,7 +1223,7 @@ def from_custom_template(cls, searchpath, name): Returns ------- MyStyler : subclass of Styler - has the correct ``env`` and ``template`` class attributes set. + Has the correct ``env`` and ``template`` class attributes set. """ loader = ChoiceLoader([ FileSystemLoader(searchpath), From 3eccea38bf2add6916d8905e5063ce0dfc58b67f Mon Sep 17 00:00:00 2001 From: rbenes Date: Mon, 4 Feb 2019 14:00:44 +0100 Subject: [PATCH 056/215] Reading a HDF5 created in py2 (#25058) --- doc/source/whatsnew/v0.24.2.rst | 2 +- pandas/io/pytables.py | 2 +- .../io/data/legacy_hdf/legacy_table_py2.h5 | Bin 0 -> 72279 bytes pandas/tests/io/test_pytables.py | 17 ++++++++++++++++- 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 pandas/tests/io/data/legacy_hdf/legacy_table_py2.h5 diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index cba21ce7ee1e6..316fc21c126ac 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -51,7 +51,7 @@ Bug Fixes **I/O** -- +- Bug in reading a HDF5 table-format ``DataFrame`` created in Python 2, in Python 3 (:issue:`24925`) - - diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index 4e103482f48a2..2ab6ddb5b25c7 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -3288,7 +3288,7 @@ def get_attrs(self): self.nan_rep = getattr(self.attrs, 'nan_rep', None) self.encoding = _ensure_encoding( getattr(self.attrs, 'encoding', None)) - self.errors = getattr(self.attrs, 'errors', 'strict') + self.errors = _ensure_decoded(getattr(self.attrs, 'errors', 'strict')) self.levels = getattr( self.attrs, 'levels', None) or [] self.index_axes = [ diff --git a/pandas/tests/io/data/legacy_hdf/legacy_table_py2.h5 b/pandas/tests/io/data/legacy_hdf/legacy_table_py2.h5 new file mode 100644 index 0000000000000000000000000000000000000000..3863d714a315b79de100e8446db766ec7dc2e82c GIT binary patch literal 72279 zcmeI#%}yIv6u|K@5O5q*2$iaeZdlb`Axyqlh89yS8G|rwQmN8(j3>yd8C$l4(p{JB zTXdNR=^~HPw`k9~cMcu~oF-KlRq`M3jOWh%IP;r(?_B?Ou(!WlS}z5<6^p?_uA=CK^3k@feV0jg`TSouciU}A*Zg1Ir>&_bedmlF9zS$Puh*6 zmdme0$MV-Ux8zLo@3><@Mw;e7{{D3Lt7g3&?jM~T?w*FT%5QJh(sFCA&ipuCO$XfS zg3A{C(>UE(_W9Cc{)GKUBQR^64%7MDApYxd+HM`~6{H7SzbmHw)yBzIdVZXX!{XuS z1#G^{drjw~|J!_5cJtjZO1jZFocui0+27Br_vC2L|HUY}h(qgjI$!#Evk zDm)wX$1d5G|H57O=hgnOR;o6(O6`ivS}6^;OOxS)QncwZl!K@4Sdcr?dH*u%zl_Jz z3ibW6CLfE()z_{>l~UBMbY$U26AKWyq zWfxaBnnW7a%ZtR_>AS~Ol~y%={~k=DBpk)V8}C6$cloBz^{y)sCuf6hFFAJ?WvA`x zlEFM$SNCu2EctSy(O@KzYW{3L7*9sMvq`r9EIIjpd>QLA)c2+Q%l6dmtsOh&#62X04muYFEll^ZWT? z_PY0pHY52r+|jS&7jCTMWv>je2u0+zMN}hq~-{<1o99xBh>7x!SdI zdguP`gCNq%WV^lG_RMlN7N)-gmZgqjU~%p@noX@YI~MfZ+STv6-QI33wDLCgZkDs( zAK7-dugbl9yV>{RPr~Tq(sq~RW9{~mNqioUGV%I(I|_Xn&96h!@@L+AZpNSX6Dy}P zv44*Gz^&ZP_k-_OQP_2BjeK1Cen`%DZp77jdt6s<;(T14@crWIWa7HsA199=Iuq;1 zixOioXq_BAYfJj9=i@rRT-V=yy1uq?IX%k7Tej!x*AsP8)lJ_GYzQEL00Iag@L>x? zwt%n&RVV$y_+j5RcZUE12q1s}0tg_000IagfB*srAb Date: Mon, 4 Feb 2019 05:05:38 -0800 Subject: [PATCH 057/215] BUG: Fixing regression in DataFrame.all and DataFrame.any with bool_only=True (#25102) --- doc/source/whatsnew/v0.24.2.rst | 4 +--- pandas/core/frame.py | 3 ++- pandas/tests/frame/test_analytics.py | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 316fc21c126ac..a047ad46e4887 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -20,9 +20,7 @@ including other versions of pandas. Fixed Regressions ^^^^^^^^^^^^^^^^^ -- -- -- +- Fixed regression in :meth:`DataFrame.all` and :meth:`DataFrame.any` where ``bool_only=True`` was ignored (:issue:`25101`) .. _whatsnew_0242.enhancements: diff --git a/pandas/core/frame.py b/pandas/core/frame.py index be125983ce293..b11d143775250 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -7498,7 +7498,8 @@ def f(x): if filter_type is None or filter_type == 'numeric': data = self._get_numeric_data() elif filter_type == 'bool': - data = self + # GH 25101, # GH 24434 + data = self._get_bool_data() if axis == 0 else self else: # pragma: no cover msg = ("Generating numeric_only data with filter_type {f}" "not supported.".format(f=filter_type)) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index 386e5f57617cf..456af34e74956 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -1442,6 +1442,26 @@ def test_any_datetime(self): expected = Series([True, True, True, False]) tm.assert_series_equal(result, expected) + def test_any_all_bool_only(self): + + # GH 25101 + df = DataFrame({"col1": [1, 2, 3], + "col2": [4, 5, 6], + "col3": [None, None, None]}) + + result = df.all(bool_only=True) + expected = Series(dtype=np.bool) + tm.assert_series_equal(result, expected) + + df = DataFrame({"col1": [1, 2, 3], + "col2": [4, 5, 6], + "col3": [None, None, None], + "col4": [False, False, True]}) + + result = df.all(bool_only=True) + expected = Series({"col4": False}) + tm.assert_series_equal(result, expected) + @pytest.mark.parametrize('func, data, expected', [ (np.any, {}, False), (np.all, {}, True), From f3f1edda40acbb6313e3c8d9531371dd8c88ac9e Mon Sep 17 00:00:00 2001 From: hannah-c Date: Mon, 4 Feb 2019 13:12:56 +0000 Subject: [PATCH 058/215] Removal of return variable names (#25123) --- pandas/core/generic.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 60079e06d8e79..10d866d67edea 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9994,8 +9994,7 @@ def _add_numeric_operations(cls): cls, 'all', name, name2, axis_descr, _all_desc, nanops.nanall, _all_see_also, _all_examples, empty_value=True) - @Substitution(outname='mad', - desc="Return the mean absolute deviation of the values " + @Substitution(desc="Return the mean absolute deviation of the values " "for the requested axis.", name1=name, name2=name2, axis_descr=axis_descr, min_count='', see_also='', examples='') @@ -10036,8 +10035,7 @@ def mad(self, axis=None, skipna=None, level=None): "ddof argument", nanops.nanstd) - @Substitution(outname='compounded', - desc="Return the compound percentage of the values for " + @Substitution(desc="Return the compound percentage of the values for " "the requested axis.", name1=name, name2=name2, axis_descr=axis_descr, min_count='', see_also='', examples='') @@ -10274,7 +10272,7 @@ def _doc_parms(cls): Returns ------- -%(outname)s : %(name1)s or %(name2)s (if level specified) +%(name1)s or %(name2)s (if level specified) %(see_also)s %(examples)s\ """ @@ -10300,7 +10298,7 @@ def _doc_parms(cls): Returns ------- -%(outname)s : %(name1)s or %(name2)s (if level specified)\n""" +%(name1)s or %(name2)s (if level specified)\n""" _bool_doc = """ %(desc)s @@ -10419,7 +10417,7 @@ def _doc_parms(cls): Returns ------- -%(outname)s : %(name1)s or %(name2)s\n +%(name1)s or %(name2)s\n See Also -------- core.window.Expanding.%(accum_func_name)s : Similar functionality @@ -10912,7 +10910,7 @@ def _doc_parms(cls): def _make_min_count_stat_function(cls, name, name1, name2, axis_descr, desc, f, see_also='', examples=''): - @Substitution(outname=name, desc=desc, name1=name1, name2=name2, + @Substitution(desc=desc, name1=name1, name2=name2, axis_descr=axis_descr, min_count=_min_count_stub, see_also=see_also, examples=examples) @Appender(_num_doc) @@ -10940,7 +10938,7 @@ def stat_func(self, axis=None, skipna=None, level=None, numeric_only=None, def _make_stat_function(cls, name, name1, name2, axis_descr, desc, f, see_also='', examples=''): - @Substitution(outname=name, desc=desc, name1=name1, name2=name2, + @Substitution(desc=desc, name1=name1, name2=name2, axis_descr=axis_descr, min_count='', see_also=see_also, examples=examples) @Appender(_num_doc) @@ -10964,7 +10962,7 @@ def stat_func(self, axis=None, skipna=None, level=None, numeric_only=None, def _make_stat_function_ddof(cls, name, name1, name2, axis_descr, desc, f): - @Substitution(outname=name, desc=desc, name1=name1, name2=name2, + @Substitution(desc=desc, name1=name1, name2=name2, axis_descr=axis_descr) @Appender(_num_ddof_doc) def stat_func(self, axis=None, skipna=None, level=None, ddof=1, @@ -10985,7 +10983,7 @@ def stat_func(self, axis=None, skipna=None, level=None, ddof=1, def _make_cum_function(cls, name, name1, name2, axis_descr, desc, accum_func, accum_func_name, mask_a, mask_b, examples): - @Substitution(outname=name, desc=desc, name1=name1, name2=name2, + @Substitution(desc=desc, name1=name1, name2=name2, axis_descr=axis_descr, accum_func_name=accum_func_name, examples=examples) @Appender(_cnum_doc) @@ -11020,7 +11018,7 @@ def cum_func(self, axis=None, skipna=True, *args, **kwargs): def _make_logical_function(cls, name, name1, name2, axis_descr, desc, f, see_also, examples, empty_value): - @Substitution(outname=name, desc=desc, name1=name1, name2=name2, + @Substitution(desc=desc, name1=name1, name2=name2, axis_descr=axis_descr, see_also=see_also, examples=examples, empty_value=empty_value) @Appender(_bool_doc) From 2199f34f82aa5b951a4f84a1c7f0cbe8439394bc Mon Sep 17 00:00:00 2001 From: Josh Friedlander <16547083+lordgrenville@users.noreply.github.com> Date: Mon, 4 Feb 2019 15:26:07 +0200 Subject: [PATCH 059/215] DOC: Improve docstring of Series.mul (#25136) --- pandas/core/ops.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 10cebc6f94b92..b3b3b9dd2c20b 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -459,14 +459,15 @@ def _get_op_name(op, special): Fill existing missing (NaN) values, and any new element needed for successful Series alignment, with this value before computation. If data in both corresponding Series locations is missing - the result will be missing + the result will be missing. level : int or name Broadcast across a level, matching Index values on the - passed MultiIndex level + passed MultiIndex level. Returns ------- -result : Series +Series + The result of the operation. See Also -------- @@ -495,6 +496,27 @@ def _get_op_name(op, special): d 1.0 e NaN dtype: float64 +>>> a.subtract(b, fill_value=0) +a 0.0 +b 1.0 +c 1.0 +d -1.0 +e NaN +dtype: float64 +>>> a.multiply(b) +a 1.0 +b NaN +c NaN +d NaN +e NaN +dtype: float64 +>>> a.divide(b, fill_value=0) +a 1.0 +b inf +c inf +d 0.0 +e NaN +dtype: float64 """ _arith_doc_FRAME = """ From 2e38d5552a5c7b2c0091cecddd483f4f08ad1d2c Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 4 Feb 2019 05:35:35 -0800 Subject: [PATCH 060/215] TST/REF: collect DataFrame reduction tests (#24914) --- pandas/tests/frame/test_analytics.py | 581 ++++++++++++++------------- 1 file changed, 291 insertions(+), 290 deletions(-) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index 456af34e74956..2e690ebbfa121 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -231,9 +231,9 @@ def assert_bool_op_api(opname, bool_frame_with_na, float_string_frame, getattr(bool_frame_with_na, opname)(axis=1, bool_only=False) -class TestDataFrameAnalytics(): +class TestDataFrameAnalytics(object): - # ---------------------------------------------------------------------= + # --------------------------------------------------------------------- # Correlation and covariance @td.skip_if_no_scipy @@ -502,6 +502,9 @@ def test_corrwith_kendall(self): expected = Series(np.ones(len(result))) tm.assert_series_equal(result, expected) + # --------------------------------------------------------------------- + # Describe + def test_bool_describe_in_mixed_frame(self): df = DataFrame({ 'string_data': ['a', 'b', 'c', 'd', 'e'], @@ -693,82 +696,113 @@ def test_describe_tz_values(self, tz_naive_fixture): result = df.describe(include='all') tm.assert_frame_equal(result, expected) - def test_reduce_mixed_frame(self): - # GH 6806 - df = DataFrame({ - 'bool_data': [True, True, False, False, False], - 'int_data': [10, 20, 30, 40, 50], - 'string_data': ['a', 'b', 'c', 'd', 'e'], - }) - df.reindex(columns=['bool_data', 'int_data', 'string_data']) - test = df.sum(axis=0) - tm.assert_numpy_array_equal(test.values, - np.array([2, 150, 'abcde'], dtype=object)) - tm.assert_series_equal(test, df.T.sum(axis=1)) + # --------------------------------------------------------------------- + # Reductions - def test_count(self, float_frame_with_na, float_frame, float_string_frame): - f = lambda s: notna(s).sum() - assert_stat_op_calc('count', f, float_frame_with_na, has_skipna=False, - check_dtype=False, check_dates=True) + def test_stat_op_api(self, float_frame, float_string_frame): assert_stat_op_api('count', float_frame, float_string_frame, has_numeric_only=True) + assert_stat_op_api('sum', float_frame, float_string_frame, + has_numeric_only=True) - # corner case - frame = DataFrame() - ct1 = frame.count(1) - assert isinstance(ct1, Series) + assert_stat_op_api('nunique', float_frame, float_string_frame) + assert_stat_op_api('mean', float_frame, float_string_frame) + assert_stat_op_api('product', float_frame, float_string_frame) + assert_stat_op_api('median', float_frame, float_string_frame) + assert_stat_op_api('min', float_frame, float_string_frame) + assert_stat_op_api('max', float_frame, float_string_frame) + assert_stat_op_api('mad', float_frame, float_string_frame) + assert_stat_op_api('var', float_frame, float_string_frame) + assert_stat_op_api('std', float_frame, float_string_frame) + assert_stat_op_api('sem', float_frame, float_string_frame) + assert_stat_op_api('median', float_frame, float_string_frame) - ct2 = frame.count(0) - assert isinstance(ct2, Series) + try: + from scipy.stats import skew, kurtosis # noqa:F401 + assert_stat_op_api('skew', float_frame, float_string_frame) + assert_stat_op_api('kurt', float_frame, float_string_frame) + except ImportError: + pass - # GH 423 - df = DataFrame(index=lrange(10)) - result = df.count(1) - expected = Series(0, index=df.index) - tm.assert_series_equal(result, expected) + def test_stat_op_calc(self, float_frame_with_na, mixed_float_frame): - df = DataFrame(columns=lrange(10)) - result = df.count(0) - expected = Series(0, index=df.columns) - tm.assert_series_equal(result, expected) + def count(s): + return notna(s).sum() - df = DataFrame() - result = df.count() - expected = Series(0, index=[]) - tm.assert_series_equal(result, expected) + def nunique(s): + return len(algorithms.unique1d(s.dropna())) - def test_nunique(self, float_frame_with_na, float_frame, - float_string_frame): - f = lambda s: len(algorithms.unique1d(s.dropna())) - assert_stat_op_calc('nunique', f, float_frame_with_na, + def mad(x): + return np.abs(x - x.mean()).mean() + + def var(x): + return np.var(x, ddof=1) + + def std(x): + return np.std(x, ddof=1) + + def sem(x): + return np.std(x, ddof=1) / np.sqrt(len(x)) + + def skewness(x): + from scipy.stats import skew # noqa:F811 + if len(x) < 3: + return np.nan + return skew(x, bias=False) + + def kurt(x): + from scipy.stats import kurtosis # noqa:F811 + if len(x) < 4: + return np.nan + return kurtosis(x, bias=False) + + assert_stat_op_calc('nunique', nunique, float_frame_with_na, has_skipna=False, check_dtype=False, check_dates=True) - assert_stat_op_api('nunique', float_frame, float_string_frame) - df = DataFrame({'A': [1, 1, 1], - 'B': [1, 2, 3], - 'C': [1, np.nan, 3]}) - tm.assert_series_equal(df.nunique(), Series({'A': 1, 'B': 3, 'C': 2})) - tm.assert_series_equal(df.nunique(dropna=False), - Series({'A': 1, 'B': 3, 'C': 3})) - tm.assert_series_equal(df.nunique(axis=1), Series({0: 1, 1: 2, 2: 2})) - tm.assert_series_equal(df.nunique(axis=1, dropna=False), - Series({0: 1, 1: 3, 2: 2})) - - def test_sum(self, float_frame_with_na, mixed_float_frame, - float_frame, float_string_frame): - assert_stat_op_api('sum', float_frame, float_string_frame, - has_numeric_only=True) - assert_stat_op_calc('sum', np.sum, float_frame_with_na, - skipna_alternative=np.nansum) # mixed types (with upcasting happening) assert_stat_op_calc('sum', np.sum, mixed_float_frame.astype('float32'), check_dtype=False, check_less_precise=True) + assert_stat_op_calc('sum', np.sum, float_frame_with_na, + skipna_alternative=np.nansum) + assert_stat_op_calc('mean', np.mean, float_frame_with_na, + check_dates=True) + assert_stat_op_calc('product', np.prod, float_frame_with_na) + + assert_stat_op_calc('mad', mad, float_frame_with_na) + assert_stat_op_calc('var', var, float_frame_with_na) + assert_stat_op_calc('std', std, float_frame_with_na) + assert_stat_op_calc('sem', sem, float_frame_with_na) + + assert_stat_op_calc('count', count, float_frame_with_na, + has_skipna=False, check_dtype=False, + check_dates=True) + + try: + from scipy import skew, kurtosis # noqa:F401 + assert_stat_op_calc('skew', skewness, float_frame_with_na) + assert_stat_op_calc('kurt', kurt, float_frame_with_na) + except ImportError: + pass + + # TODO: Ensure warning isn't emitted in the first place + @pytest.mark.filterwarnings("ignore:All-NaN:RuntimeWarning") + def test_median(self, float_frame_with_na, int_frame): + def wrapper(x): + if isna(x).any(): + return np.nan + return np.median(x) + + assert_stat_op_calc('median', wrapper, float_frame_with_na, + check_dates=True) + assert_stat_op_calc('median', wrapper, int_frame, check_dtype=False, + check_dates=True) + @pytest.mark.parametrize('method', ['sum', 'mean', 'prod', 'var', 'std', 'skew', 'min', 'max']) def test_stat_operators_attempt_obj_array(self, method): - # GH 676 + # GH#676 data = { 'a': [-0.00049987540199591344, -0.0016467257772919831, 0.00067695870775883013], @@ -789,10 +823,44 @@ def test_stat_operators_attempt_obj_array(self, method): if method in ['sum', 'prod']: tm.assert_series_equal(result, expected) - def test_mean(self, float_frame_with_na, float_frame, float_string_frame): - assert_stat_op_calc('mean', np.mean, float_frame_with_na, - check_dates=True) - assert_stat_op_api('mean', float_frame, float_string_frame) + @pytest.mark.parametrize('op', ['mean', 'std', 'var', + 'skew', 'kurt', 'sem']) + def test_mixed_ops(self, op): + # GH#16116 + df = DataFrame({'int': [1, 2, 3, 4], + 'float': [1., 2., 3., 4.], + 'str': ['a', 'b', 'c', 'd']}) + + result = getattr(df, op)() + assert len(result) == 2 + + with pd.option_context('use_bottleneck', False): + result = getattr(df, op)() + assert len(result) == 2 + + def test_reduce_mixed_frame(self): + # GH 6806 + df = DataFrame({ + 'bool_data': [True, True, False, False, False], + 'int_data': [10, 20, 30, 40, 50], + 'string_data': ['a', 'b', 'c', 'd', 'e'], + }) + df.reindex(columns=['bool_data', 'int_data', 'string_data']) + test = df.sum(axis=0) + tm.assert_numpy_array_equal(test.values, + np.array([2, 150, 'abcde'], dtype=object)) + tm.assert_series_equal(test, df.T.sum(axis=1)) + + def test_nunique(self): + df = DataFrame({'A': [1, 1, 1], + 'B': [1, 2, 3], + 'C': [1, np.nan, 3]}) + tm.assert_series_equal(df.nunique(), Series({'A': 1, 'B': 3, 'C': 2})) + tm.assert_series_equal(df.nunique(dropna=False), + Series({'A': 1, 'B': 3, 'C': 3})) + tm.assert_series_equal(df.nunique(axis=1), Series({0: 1, 1: 2, 2: 2})) + tm.assert_series_equal(df.nunique(axis=1, dropna=False), + Series({0: 1, 1: 3, 2: 2})) @pytest.mark.parametrize('tz', [None, 'UTC']) def test_mean_mixed_datetime_numeric(self, tz): @@ -813,103 +881,7 @@ def test_mean_excludeds_datetimes(self, tz): expected = pd.Series() tm.assert_series_equal(result, expected) - def test_product(self, float_frame_with_na, float_frame, - float_string_frame): - assert_stat_op_calc('product', np.prod, float_frame_with_na) - assert_stat_op_api('product', float_frame, float_string_frame) - - # TODO: Ensure warning isn't emitted in the first place - @pytest.mark.filterwarnings("ignore:All-NaN:RuntimeWarning") - def test_median(self, float_frame_with_na, float_frame, - float_string_frame): - def wrapper(x): - if isna(x).any(): - return np.nan - return np.median(x) - - assert_stat_op_calc('median', wrapper, float_frame_with_na, - check_dates=True) - assert_stat_op_api('median', float_frame, float_string_frame) - - def test_min(self, float_frame_with_na, int_frame, - float_frame, float_string_frame): - with warnings.catch_warnings(record=True): - warnings.simplefilter("ignore", RuntimeWarning) - assert_stat_op_calc('min', np.min, float_frame_with_na, - check_dates=True) - assert_stat_op_calc('min', np.min, int_frame) - assert_stat_op_api('min', float_frame, float_string_frame) - - def test_cummin(self, datetime_frame): - datetime_frame.loc[5:10, 0] = np.nan - datetime_frame.loc[10:15, 1] = np.nan - datetime_frame.loc[15:, 2] = np.nan - - # axis = 0 - cummin = datetime_frame.cummin() - expected = datetime_frame.apply(Series.cummin) - tm.assert_frame_equal(cummin, expected) - - # axis = 1 - cummin = datetime_frame.cummin(axis=1) - expected = datetime_frame.apply(Series.cummin, axis=1) - tm.assert_frame_equal(cummin, expected) - - # it works - df = DataFrame({'A': np.arange(20)}, index=np.arange(20)) - result = df.cummin() # noqa - - # fix issue - cummin_xs = datetime_frame.cummin(axis=1) - assert np.shape(cummin_xs) == np.shape(datetime_frame) - - def test_cummax(self, datetime_frame): - datetime_frame.loc[5:10, 0] = np.nan - datetime_frame.loc[10:15, 1] = np.nan - datetime_frame.loc[15:, 2] = np.nan - - # axis = 0 - cummax = datetime_frame.cummax() - expected = datetime_frame.apply(Series.cummax) - tm.assert_frame_equal(cummax, expected) - - # axis = 1 - cummax = datetime_frame.cummax(axis=1) - expected = datetime_frame.apply(Series.cummax, axis=1) - tm.assert_frame_equal(cummax, expected) - - # it works - df = DataFrame({'A': np.arange(20)}, index=np.arange(20)) - result = df.cummax() # noqa - - # fix issue - cummax_xs = datetime_frame.cummax(axis=1) - assert np.shape(cummax_xs) == np.shape(datetime_frame) - - def test_max(self, float_frame_with_na, int_frame, - float_frame, float_string_frame): - with warnings.catch_warnings(record=True): - warnings.simplefilter("ignore", RuntimeWarning) - assert_stat_op_calc('max', np.max, float_frame_with_na, - check_dates=True) - assert_stat_op_calc('max', np.max, int_frame) - assert_stat_op_api('max', float_frame, float_string_frame) - - def test_mad(self, float_frame_with_na, float_frame, float_string_frame): - f = lambda x: np.abs(x - x.mean()).mean() - assert_stat_op_calc('mad', f, float_frame_with_na) - assert_stat_op_api('mad', float_frame, float_string_frame) - - def test_var_std(self, float_frame_with_na, datetime_frame, float_frame, - float_string_frame): - alt = lambda x: np.var(x, ddof=1) - assert_stat_op_calc('var', alt, float_frame_with_na) - assert_stat_op_api('var', float_frame, float_string_frame) - - alt = lambda x: np.std(x, ddof=1) - assert_stat_op_calc('std', alt, float_frame_with_na) - assert_stat_op_api('std', float_frame, float_string_frame) - + def test_var_std(self, datetime_frame): result = datetime_frame.std(ddof=4) expected = datetime_frame.apply(lambda x: x.std(ddof=4)) tm.assert_almost_equal(result, expected) @@ -952,79 +924,7 @@ def test_numeric_only_flag(self, meth): pytest.raises(TypeError, lambda: getattr(df2, meth)( axis=1, numeric_only=False)) - @pytest.mark.parametrize('op', ['mean', 'std', 'var', - 'skew', 'kurt', 'sem']) - def test_mixed_ops(self, op): - # GH 16116 - df = DataFrame({'int': [1, 2, 3, 4], - 'float': [1., 2., 3., 4.], - 'str': ['a', 'b', 'c', 'd']}) - - result = getattr(df, op)() - assert len(result) == 2 - - with pd.option_context('use_bottleneck', False): - result = getattr(df, op)() - assert len(result) == 2 - - def test_cumsum(self, datetime_frame): - datetime_frame.loc[5:10, 0] = np.nan - datetime_frame.loc[10:15, 1] = np.nan - datetime_frame.loc[15:, 2] = np.nan - - # axis = 0 - cumsum = datetime_frame.cumsum() - expected = datetime_frame.apply(Series.cumsum) - tm.assert_frame_equal(cumsum, expected) - - # axis = 1 - cumsum = datetime_frame.cumsum(axis=1) - expected = datetime_frame.apply(Series.cumsum, axis=1) - tm.assert_frame_equal(cumsum, expected) - - # works - df = DataFrame({'A': np.arange(20)}, index=np.arange(20)) - result = df.cumsum() # noqa - - # fix issue - cumsum_xs = datetime_frame.cumsum(axis=1) - assert np.shape(cumsum_xs) == np.shape(datetime_frame) - - def test_cumprod(self, datetime_frame): - datetime_frame.loc[5:10, 0] = np.nan - datetime_frame.loc[10:15, 1] = np.nan - datetime_frame.loc[15:, 2] = np.nan - - # axis = 0 - cumprod = datetime_frame.cumprod() - expected = datetime_frame.apply(Series.cumprod) - tm.assert_frame_equal(cumprod, expected) - - # axis = 1 - cumprod = datetime_frame.cumprod(axis=1) - expected = datetime_frame.apply(Series.cumprod, axis=1) - tm.assert_frame_equal(cumprod, expected) - - # fix issue - cumprod_xs = datetime_frame.cumprod(axis=1) - assert np.shape(cumprod_xs) == np.shape(datetime_frame) - - # ints - df = datetime_frame.fillna(0).astype(int) - df.cumprod(0) - df.cumprod(1) - - # ints32 - df = datetime_frame.fillna(0).astype(np.int32) - df.cumprod(0) - df.cumprod(1) - - def test_sem(self, float_frame_with_na, datetime_frame, - float_frame, float_string_frame): - alt = lambda x: np.std(x, ddof=1) / np.sqrt(len(x)) - assert_stat_op_calc('sem', alt, float_frame_with_na) - assert_stat_op_api('sem', float_frame, float_string_frame) - + def test_sem(self, datetime_frame): result = datetime_frame.sem(ddof=4) expected = datetime_frame.apply( lambda x: x.std(ddof=4) / np.sqrt(len(x))) @@ -1039,29 +939,7 @@ def test_sem(self, float_frame_with_na, datetime_frame, assert not (result < 0).any() @td.skip_if_no_scipy - def test_skew(self, float_frame_with_na, float_frame, float_string_frame): - from scipy.stats import skew - - def alt(x): - if len(x) < 3: - return np.nan - return skew(x, bias=False) - - assert_stat_op_calc('skew', alt, float_frame_with_na) - assert_stat_op_api('skew', float_frame, float_string_frame) - - @td.skip_if_no_scipy - def test_kurt(self, float_frame_with_na, float_frame, float_string_frame): - from scipy.stats import kurtosis - - def alt(x): - if len(x) < 4: - return np.nan - return kurtosis(x, bias=False) - - assert_stat_op_calc('kurt', alt, float_frame_with_na) - assert_stat_op_api('kurt', float_frame, float_string_frame) - + def test_kurt(self): index = MultiIndex(levels=[['bar'], ['one', 'two', 'three'], [0, 1]], codes=[[0, 0, 0, 0, 0, 0], [0, 1, 2, 0, 1, 2], @@ -1323,20 +1201,146 @@ def test_stats_mixed_type(self, float_string_frame): float_string_frame.mean(1) float_string_frame.skew(1) - # TODO: Ensure warning isn't emitted in the first place - @pytest.mark.filterwarnings("ignore:All-NaN:RuntimeWarning") - def test_median_corner(self, int_frame, float_frame, float_string_frame): - def wrapper(x): - if isna(x).any(): - return np.nan - return np.median(x) + def test_sum_bools(self): + df = DataFrame(index=lrange(1), columns=lrange(10)) + bools = isna(df) + assert bools.sum(axis=1)[0] == 10 - assert_stat_op_calc('median', wrapper, int_frame, check_dtype=False, - check_dates=True) - assert_stat_op_api('median', float_frame, float_string_frame) + # --------------------------------------------------------------------- + # Cumulative Reductions - cumsum, cummax, ... + + def test_cumsum_corner(self): + dm = DataFrame(np.arange(20).reshape(4, 5), + index=lrange(4), columns=lrange(5)) + # ?(wesm) + result = dm.cumsum() # noqa + + def test_cumsum(self, datetime_frame): + datetime_frame.loc[5:10, 0] = np.nan + datetime_frame.loc[10:15, 1] = np.nan + datetime_frame.loc[15:, 2] = np.nan + + # axis = 0 + cumsum = datetime_frame.cumsum() + expected = datetime_frame.apply(Series.cumsum) + tm.assert_frame_equal(cumsum, expected) + + # axis = 1 + cumsum = datetime_frame.cumsum(axis=1) + expected = datetime_frame.apply(Series.cumsum, axis=1) + tm.assert_frame_equal(cumsum, expected) + + # works + df = DataFrame({'A': np.arange(20)}, index=np.arange(20)) + result = df.cumsum() # noqa + + # fix issue + cumsum_xs = datetime_frame.cumsum(axis=1) + assert np.shape(cumsum_xs) == np.shape(datetime_frame) + + def test_cumprod(self, datetime_frame): + datetime_frame.loc[5:10, 0] = np.nan + datetime_frame.loc[10:15, 1] = np.nan + datetime_frame.loc[15:, 2] = np.nan + + # axis = 0 + cumprod = datetime_frame.cumprod() + expected = datetime_frame.apply(Series.cumprod) + tm.assert_frame_equal(cumprod, expected) + + # axis = 1 + cumprod = datetime_frame.cumprod(axis=1) + expected = datetime_frame.apply(Series.cumprod, axis=1) + tm.assert_frame_equal(cumprod, expected) + + # fix issue + cumprod_xs = datetime_frame.cumprod(axis=1) + assert np.shape(cumprod_xs) == np.shape(datetime_frame) + # ints + df = datetime_frame.fillna(0).astype(int) + df.cumprod(0) + df.cumprod(1) + + # ints32 + df = datetime_frame.fillna(0).astype(np.int32) + df.cumprod(0) + df.cumprod(1) + + def test_cummin(self, datetime_frame): + datetime_frame.loc[5:10, 0] = np.nan + datetime_frame.loc[10:15, 1] = np.nan + datetime_frame.loc[15:, 2] = np.nan + + # axis = 0 + cummin = datetime_frame.cummin() + expected = datetime_frame.apply(Series.cummin) + tm.assert_frame_equal(cummin, expected) + + # axis = 1 + cummin = datetime_frame.cummin(axis=1) + expected = datetime_frame.apply(Series.cummin, axis=1) + tm.assert_frame_equal(cummin, expected) + + # it works + df = DataFrame({'A': np.arange(20)}, index=np.arange(20)) + result = df.cummin() # noqa + + # fix issue + cummin_xs = datetime_frame.cummin(axis=1) + assert np.shape(cummin_xs) == np.shape(datetime_frame) + + def test_cummax(self, datetime_frame): + datetime_frame.loc[5:10, 0] = np.nan + datetime_frame.loc[10:15, 1] = np.nan + datetime_frame.loc[15:, 2] = np.nan + + # axis = 0 + cummax = datetime_frame.cummax() + expected = datetime_frame.apply(Series.cummax) + tm.assert_frame_equal(cummax, expected) + + # axis = 1 + cummax = datetime_frame.cummax(axis=1) + expected = datetime_frame.apply(Series.cummax, axis=1) + tm.assert_frame_equal(cummax, expected) + + # it works + df = DataFrame({'A': np.arange(20)}, index=np.arange(20)) + result = df.cummax() # noqa + + # fix issue + cummax_xs = datetime_frame.cummax(axis=1) + assert np.shape(cummax_xs) == np.shape(datetime_frame) + + # --------------------------------------------------------------------- # Miscellanea + def test_count(self): + # corner case + frame = DataFrame() + ct1 = frame.count(1) + assert isinstance(ct1, Series) + + ct2 = frame.count(0) + assert isinstance(ct2, Series) + + # GH#423 + df = DataFrame(index=lrange(10)) + result = df.count(1) + expected = Series(0, index=df.index) + tm.assert_series_equal(result, expected) + + df = DataFrame(columns=lrange(10)) + result = df.count(0) + expected = Series(0, index=df.columns) + tm.assert_series_equal(result, expected) + + df = DataFrame() + result = df.count() + expected = Series(0, index=[]) + tm.assert_series_equal(result, expected) + def test_count_objects(self, float_string_frame): dm = DataFrame(float_string_frame._series) df = DataFrame(float_string_frame._series) @@ -1344,17 +1348,23 @@ def test_count_objects(self, float_string_frame): tm.assert_series_equal(dm.count(), df.count()) tm.assert_series_equal(dm.count(1), df.count(1)) - def test_cumsum_corner(self): - dm = DataFrame(np.arange(20).reshape(4, 5), - index=lrange(4), columns=lrange(5)) - # ?(wesm) - result = dm.cumsum() # noqa + def test_pct_change(self): + # GH#11150 + pnl = DataFrame([np.arange(0, 40, 10), + np.arange(0, 40, 10), + np.arange(0, 40, 10)]).astype(np.float64) + pnl.iat[1, 0] = np.nan + pnl.iat[1, 1] = np.nan + pnl.iat[2, 3] = 60 - def test_sum_bools(self): - df = DataFrame(index=lrange(1), columns=lrange(10)) - bools = isna(df) - assert bools.sum(axis=1)[0] == 10 + for axis in range(2): + expected = pnl.ffill(axis=axis) / pnl.ffill(axis=axis).shift( + axis=axis) - 1 + result = pnl.pct_change(axis=axis, fill_method='pad') + tm.assert_frame_equal(result, expected) + + # ---------------------------------------------------------------------- # Index of max / min def test_idxmin(self, float_frame, int_frame): @@ -1700,7 +1710,9 @@ def test_isin_empty_datetimelike(self): result = df1_td.isin(df3) tm.assert_frame_equal(result, expected) + # --------------------------------------------------------------------- # Rounding + def test_round(self): # GH 2665 @@ -1888,22 +1900,9 @@ def test_round_nonunique_categorical(self): tm.assert_frame_equal(result, expected) - def test_pct_change(self): - # GH 11150 - pnl = DataFrame([np.arange(0, 40, 10), np.arange(0, 40, 10), np.arange( - 0, 40, 10)]).astype(np.float64) - pnl.iat[1, 0] = np.nan - pnl.iat[1, 1] = np.nan - pnl.iat[2, 3] = 60 - - for axis in range(2): - expected = pnl.ffill(axis=axis) / pnl.ffill(axis=axis).shift( - axis=axis) - 1 - result = pnl.pct_change(axis=axis, fill_method='pad') - - tm.assert_frame_equal(result, expected) - + # --------------------------------------------------------------------- # Clip + def test_clip(self, float_frame): median = float_frame.median().median() original = float_frame.copy() @@ -2076,7 +2075,9 @@ def test_clip_with_na_args(self, float_frame): 'col_2': [np.nan, np.nan, np.nan]}) tm.assert_frame_equal(result, expected) + # --------------------------------------------------------------------- # Matrix-like + def test_dot(self): a = DataFrame(np.random.randn(3, 4), index=['a', 'b', 'c'], columns=['p', 'q', 'r', 's']) From e3b095074c5c3b29ec25a994cd204349b872d961 Mon Sep 17 00:00:00 2001 From: Thein Oo Date: Mon, 4 Feb 2019 18:50:01 -0500 Subject: [PATCH 061/215] Fix validation error type `SS05` and check in CI (#25133) --- ci/code_checks.sh | 4 ++-- pandas/_libs/tslibs/nattype.pyx | 6 ++++-- pandas/_libs/tslibs/timestamps.pyx | 6 ++++-- pandas/core/arrays/categorical.py | 18 +++++++++--------- pandas/core/dtypes/common.py | 2 +- pandas/core/dtypes/dtypes.py | 3 ++- pandas/core/frame.py | 2 +- pandas/core/generic.py | 2 +- pandas/core/groupby/generic.py | 4 +++- pandas/core/groupby/groupby.py | 12 ++++++------ pandas/core/indexes/base.py | 4 ++-- pandas/core/indexes/category.py | 2 +- pandas/core/indexes/datetimes.py | 2 +- pandas/core/ops.py | 8 ++++---- pandas/core/panel.py | 2 +- pandas/core/series.py | 6 +++--- pandas/core/strings.py | 2 +- pandas/core/window.py | 8 ++++---- pandas/io/stata.py | 26 +++++++++++++++----------- pandas/plotting/_misc.py | 5 +++-- pandas/util/_decorators.py | 3 ++- 21 files changed, 70 insertions(+), 57 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index 35358306468c5..d16249724127f 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -240,8 +240,8 @@ fi ### DOCSTRINGS ### if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then - MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, EX04, RT04)' ; echo $MSG - $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR05,EX04,RT04 + MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, EX04, RT04, SS05)' ; echo $MSG + $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR05,EX04,RT04,SS05 RET=$(($RET + $?)) ; echo $MSG "DONE" fi diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index f2de3fda3be15..b64c3479f23fe 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -183,7 +183,9 @@ cdef class _NaT(datetime): return np.datetime64(NPY_NAT, 'ns') def to_datetime64(self): - """ Returns a numpy.datetime64 object with 'ns' precision """ + """ + Return a numpy.datetime64 object with 'ns' precision. + """ return np.datetime64('NaT', 'ns') def __repr__(self): @@ -448,7 +450,7 @@ class NaTType(_NaT): """ Timestamp.now(tz=None) - Returns new Timestamp object representing current time local to + Return new Timestamp object representing current time local to tz. Parameters diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index a9be60214080a..cad63b4323015 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -340,7 +340,9 @@ cdef class _Timestamp(datetime): self.microsecond, self.tzinfo) cpdef to_datetime64(self): - """ Returns a numpy.datetime64 object with 'ns' precision """ + """ + Return a numpy.datetime64 object with 'ns' precision. + """ return np.datetime64(self.value, 'ns') def __add__(self, other): @@ -614,7 +616,7 @@ class Timestamp(_Timestamp): """ Timestamp.now(tz=None) - Returns new Timestamp object representing current time local to + Return new Timestamp object representing current time local to tz. Parameters diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 6259ead5ed93e..e26f6cb03a768 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -214,7 +214,7 @@ def contains(cat, key, container): class Categorical(ExtensionArray, PandasObject): """ - Represents a categorical variable in classic R / S-plus fashion + Represent a categorical variable in classic R / S-plus fashion `Categoricals` can only take on only a limited, and usually fixed, number of possible values (`categories`). In contrast to statistical categorical @@ -747,7 +747,7 @@ def _set_dtype(self, dtype): def set_ordered(self, value, inplace=False): """ - Sets the ordered attribute to the boolean value + Set the ordered attribute to the boolean value Parameters ---------- @@ -793,7 +793,7 @@ def as_unordered(self, inplace=False): def set_categories(self, new_categories, ordered=None, rename=False, inplace=False): """ - Sets the categories to the specified new_categories. + Set the categories to the specified new_categories. `new_categories` can include new categories (which will result in unused categories) or remove old categories (which results in values @@ -864,7 +864,7 @@ def set_categories(self, new_categories, ordered=None, rename=False, def rename_categories(self, new_categories, inplace=False): """ - Renames categories. + Rename categories. Parameters ---------- @@ -958,7 +958,7 @@ def rename_categories(self, new_categories, inplace=False): def reorder_categories(self, new_categories, ordered=None, inplace=False): """ - Reorders categories as specified in new_categories. + Reorder categories as specified in new_categories. `new_categories` need to include all old categories and no new category items. @@ -1051,7 +1051,7 @@ def add_categories(self, new_categories, inplace=False): def remove_categories(self, removals, inplace=False): """ - Removes the specified categories. + Remove the specified categories. `removals` must be included in the old categories. Values which were in the removed categories will be set to NaN @@ -1104,7 +1104,7 @@ def remove_categories(self, removals, inplace=False): def remove_unused_categories(self, inplace=False): """ - Removes categories which are not used. + Remove categories which are not used. Parameters ---------- @@ -1454,7 +1454,7 @@ def dropna(self): def value_counts(self, dropna=True): """ - Returns a Series containing counts of each category. + Return a Series containing counts of each category. Every category will have an entry, even those with a count of 0. @@ -1570,7 +1570,7 @@ def argsort(self, *args, **kwargs): def sort_values(self, inplace=False, ascending=True, na_position='last'): """ - Sorts the Categorical by category value returning a new + Sort the Categorical by category value returning a new Categorical by default. While an ordering is applied to the category values, sorting in this diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index 0379a3d3eb3cb..4be7eb8ddb890 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -2006,7 +2006,7 @@ def _validate_date_like_dtype(dtype): def pandas_dtype(dtype): """ - Converts input into a pandas only dtype object or a numpy dtype object. + Convert input into a pandas only dtype object or a numpy dtype object. Parameters ---------- diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index f84471c3b04e8..ac38b2fbbe957 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -17,7 +17,8 @@ def register_extension_dtype(cls): - """Class decorator to register an ExtensionType with pandas. + """ + Register an ExtensionType with pandas as class decorator. .. versionadded:: 0.24.0 diff --git a/pandas/core/frame.py b/pandas/core/frame.py index b11d143775250..afc4194e71eb1 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -6009,7 +6009,7 @@ def unstack(self, level=-1, fill_value=None): return unstack(self, level, fill_value) _shared_docs['melt'] = (""" - Unpivots a DataFrame from wide format to long format, optionally + Unpivot a DataFrame from wide format to long format, optionally leaving identifier variables set. This function is useful to massage a DataFrame into a format where one diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 10d866d67edea..d03d78bd9f18c 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -10125,7 +10125,7 @@ def nanptp(values, axis=0, skipna=True): cls.ptp = _make_stat_function( cls, 'ptp', name, name2, axis_descr, - """Returns the difference between the maximum value and the + """Return the difference between the maximum value and the minimum value in the object. This is the equivalent of the ``numpy.ndarray`` method ``ptp``.\n\n.. deprecated:: 0.24.0 Use numpy.ptp instead""", diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index c5142a4ee98cc..78aa6d13a9e02 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -1021,7 +1021,9 @@ def true_and_notna(x, *args, **kwargs): return filtered def nunique(self, dropna=True): - """ Returns number of unique elements in the group """ + """ + Return number of unique elements in the group. + """ ids, _, _ = self.grouper.group_info val = self.obj.get_values() diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index 8766fdbc29755..bee806df824df 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -625,7 +625,7 @@ def curried(x): def get_group(self, name, obj=None): """ - Constructs NDFrame from group with provided name. + Construct NDFrame from group with provided name. Parameters ---------- @@ -1047,7 +1047,7 @@ def result_to_bool(result): @Appender(_common_see_also) def any(self, skipna=True): """ - Returns True if any value in the group is truthful, else False. + Return True if any value in the group is truthful, else False. Parameters ---------- @@ -1060,7 +1060,7 @@ def any(self, skipna=True): @Appender(_common_see_also) def all(self, skipna=True): """ - Returns True if all values in the group are truthful, else False. + Return True if all values in the group are truthful, else False. Parameters ---------- @@ -1813,7 +1813,7 @@ def cumcount(self, ascending=True): def rank(self, method='average', ascending=True, na_option='keep', pct=False, axis=0): """ - Provides the rank of values within each group. + Provide the rank of values within each group. Parameters ---------- @@ -2039,7 +2039,7 @@ def pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, @Substitution(name='groupby', see_also=_common_see_also) def head(self, n=5): """ - Returns first n rows of each group. + Return first n rows of each group. Essentially equivalent to ``.apply(lambda x: x.head(n))``, except ignores as_index flag. @@ -2067,7 +2067,7 @@ def head(self, n=5): @Substitution(name='groupby', see_also=_common_see_also) def tail(self, n=5): """ - Returns last n rows of each group. + Return last n rows of each group. Essentially equivalent to ``.apply(lambda x: x.tail(n))``, except ignores as_index flag. diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index d0b44db840a29..664bf3a4040d8 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -4049,7 +4049,7 @@ def putmask(self, mask, value): def equals(self, other): """ - Determines if two Index objects contain the same elements. + Determine if two Index objects contain the same elements. """ if self.is_(other): return True @@ -4144,7 +4144,7 @@ def asof(self, label): def asof_locs(self, where, mask): """ - Finds the locations (indices) of the labels from the index for + Find the locations (indices) of the labels from the index for every entry in the `where` argument. As in the `asof` function, if the label (a particular entry in diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index e43b64827d02a..0a5536854ebd4 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -232,7 +232,7 @@ def _is_dtype_compat(self, other): def equals(self, other): """ - Determines if two CategorialIndex objects contain the same elements. + Determine if two CategorialIndex objects contain the same elements. """ if self.is_(other): return True diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 9c46860eb49d6..f34eb75d352a1 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1284,7 +1284,7 @@ def delete(self, loc): def indexer_at_time(self, time, asof=False): """ - Returns index locations of index values at particular time of day + Return index locations of index values at particular time of day (e.g. 9:30AM). Parameters diff --git a/pandas/core/ops.py b/pandas/core/ops.py index b3b3b9dd2c20b..dbdabecafae3a 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -447,7 +447,7 @@ def _get_op_name(op, special): _op_descriptions[reverse_op]['reverse'] = key _flex_doc_SERIES = """ -{desc} of series and other, element-wise (binary operator `{op_name}`). +Return {desc} of series and other, element-wise (binary operator `{op_name}`). Equivalent to ``{equiv}``, but with support to substitute a fill_value for missing data in one of the inputs. @@ -547,7 +547,7 @@ def _get_op_name(op, special): """ _flex_doc_FRAME = """ -{desc} of dataframe and other, element-wise (binary operator `{op_name}`). +Get {desc} of dataframe and other, element-wise (binary operator `{op_name}`). Equivalent to ``{equiv}``, but with support to substitute a fill_value for missing data in one of the inputs. With reverse version, `{reverse}`. @@ -701,7 +701,7 @@ def _get_op_name(op, special): """ _flex_comp_doc_FRAME = """ -{desc} of dataframe and other, element-wise (binary operator `{op_name}`). +Get {desc} of dataframe and other, element-wise (binary operator `{op_name}`). Among flexible wrappers (`eq`, `ne`, `le`, `lt`, `ge`, `gt`) to comparison operators. @@ -847,7 +847,7 @@ def _get_op_name(op, special): """ _flex_doc_PANEL = """ -{desc} of series and other, element-wise (binary operator `{op_name}`). +Return {desc} of series and other, element-wise (binary operator `{op_name}`). Equivalent to ``{equiv}``. Parameters diff --git a/pandas/core/panel.py b/pandas/core/panel.py index 8dd604fb580b4..c8afafde48ac2 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -999,7 +999,7 @@ def construct_index_parts(idx, major=True): def apply(self, func, axis='major', **kwargs): """ - Applies function along axis (or axes) of the Panel. + Apply function along axis (or axes) of the Panel. Parameters ---------- diff --git a/pandas/core/series.py b/pandas/core/series.py index 427da96c5e1c4..fb84a36d215e5 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -580,7 +580,7 @@ def nonzero(self): def put(self, *args, **kwargs): """ - Applies the `put` method to its `values` attribute if it has one. + Apply the `put` method to its `values` attribute if it has one. See Also -------- @@ -1456,7 +1456,7 @@ def iteritems(self): def keys(self): """ - Alias for index. + Return alias for index. """ return self.index @@ -2987,7 +2987,7 @@ def sort_index(self, axis=0, level=None, ascending=True, inplace=False, def argsort(self, axis=0, kind='quicksort', order=None): """ - Overrides ndarray.argsort. Argsorts the value, omitting NA/null values, + Override ndarray.argsort. Argsorts the value, omitting NA/null values, and places the result in the same locations as the non-NA values. Parameters diff --git a/pandas/core/strings.py b/pandas/core/strings.py index ca79dcd9408d8..bfa36cb4bfd86 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -2869,7 +2869,7 @@ def rindex(self, sub, start=0, end=None): return self._wrap_result(result) _shared_docs['len'] = (""" - Computes the length of each element in the Series/Index. The element may be + Compute the length of each element in the Series/Index. The element may be a sequence (such as a string, tuple or list) or a collection (such as a dictionary). diff --git a/pandas/core/window.py b/pandas/core/window.py index 5556a01307a7e..060226d94cca0 100644 --- a/pandas/core/window.py +++ b/pandas/core/window.py @@ -438,7 +438,7 @@ def aggregate(self, arg, *args, **kwargs): class Window(_Window): """ - Provides rolling window calculations. + Provide rolling window calculations. .. versionadded:: 0.18.0 @@ -1803,7 +1803,7 @@ def corr(self, other=None, pairwise=None, **kwargs): class RollingGroupby(_GroupByMixin, Rolling): """ - Provides a rolling groupby implementation. + Provide a rolling groupby implementation. .. versionadded:: 0.18.1 @@ -1834,7 +1834,7 @@ def _validate_monotonic(self): class Expanding(_Rolling_and_Expanding): """ - Provides expanding transformations. + Provide expanding transformations. .. versionadded:: 0.18.0 @@ -2076,7 +2076,7 @@ def corr(self, other=None, pairwise=None, **kwargs): class ExpandingGroupby(_GroupByMixin, Expanding): """ - Provides a expanding groupby implementation. + Provide a expanding groupby implementation. .. versionadded:: 0.18.1 diff --git a/pandas/io/stata.py b/pandas/io/stata.py index 0bd084f4e5df7..9ba3290c0b51a 100644 --- a/pandas/io/stata.py +++ b/pandas/io/stata.py @@ -119,7 +119,7 @@ _iterator_params) _data_method_doc = """\ -Reads observations from Stata file, converting them into a dataframe +Read observations from Stata file, converting them into a dataframe .. deprecated:: This is a legacy method. Use `read` in new code. @@ -1726,18 +1726,22 @@ def _do_convert_categoricals(self, data, value_label_dict, lbllist, return data def data_label(self): - """Returns data label of Stata file""" + """ + Return data label of Stata file. + """ return self.data_label def variable_labels(self): - """Returns variable labels as a dict, associating each variable name - with corresponding label + """ + Return variable labels as a dict, associating each variable name + with corresponding label. """ return dict(zip(self.varlist, self._variable_labels)) def value_labels(self): - """Returns a dict, associating each variable name a dict, associating - each value its corresponding label + """ + Return a dict, associating each variable name a dict, associating + each value its corresponding label. """ if not self._value_labels_read: self._read_value_labels() @@ -1747,7 +1751,7 @@ def value_labels(self): def _open_file_binary_write(fname): """ - Open a binary file or no-op if file-like + Open a binary file or no-op if file-like. Parameters ---------- @@ -1778,14 +1782,14 @@ def _set_endianness(endianness): def _pad_bytes(name, length): """ - Takes a char string and pads it with null bytes until it's length chars + Take a char string and pads it with null bytes until it's length chars. """ return name + "\x00" * (length - len(name)) def _convert_datetime_to_stata_type(fmt): """ - Converts from one of the stata date formats to a type in TYPE_MAP + Convert from one of the stata date formats to a type in TYPE_MAP. """ if fmt in ["tc", "%tc", "td", "%td", "tw", "%tw", "tm", "%tm", "tq", "%tq", "th", "%th", "ty", "%ty"]: @@ -1812,7 +1816,7 @@ def _maybe_convert_to_int_keys(convert_dates, varlist): def _dtype_to_stata_type(dtype, column): """ - Converts dtype types to stata types. Returns the byte of the given ordinal. + Convert dtype types to stata types. Returns the byte of the given ordinal. See TYPE_MAP and comments for an explanation. This is also explained in the dta spec. 1 - 244 are strings of this length @@ -1850,7 +1854,7 @@ def _dtype_to_stata_type(dtype, column): def _dtype_to_default_stata_fmt(dtype, column, dta_version=114, force_strl=False): """ - Maps numpy dtype to stata's default format for this type. Not terribly + Map numpy dtype to stata's default format for this type. Not terribly important since users can change this in Stata. Semantics are object -> "%DDs" where DD is the length of the string. If not a string, diff --git a/pandas/plotting/_misc.py b/pandas/plotting/_misc.py index 1c69c03025e00..01cc8ecc6f957 100644 --- a/pandas/plotting/_misc.py +++ b/pandas/plotting/_misc.py @@ -273,7 +273,7 @@ def normalize(series): def andrews_curves(frame, class_column, ax=None, samples=200, color=None, colormap=None, **kwds): """ - Generates a matplotlib plot of Andrews curves, for visualising clusters of + Generate a matplotlib plot of Andrews curves, for visualising clusters of multivariate data. Andrews curves have the functional form: @@ -598,7 +598,8 @@ def lag_plot(series, lag=1, ax=None, **kwds): def autocorrelation_plot(series, ax=None, **kwds): - """Autocorrelation plot for time series. + """ + Autocorrelation plot for time series. Parameters: ----------- diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index 86cd8b1e698c6..2f7816e3a6d00 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -9,7 +9,8 @@ def deprecate(name, alternative, version, alt_name=None, klass=None, stacklevel=2, msg=None): - """Return a new function that emits a deprecation warning on use. + """ + Return a new function that emits a deprecation warning on use. To use this method for a deprecated function, another function `alternative` with the same signature must exist. The deprecated From f04bb2fc9c3d0d23e92bbdf09fe6b94e59589cbe Mon Sep 17 00:00:00 2001 From: Vibhu Agarwal Date: Wed, 6 Feb 2019 07:47:00 +0530 Subject: [PATCH 062/215] Fixed tuple to List Conversion in Dataframe class (#25089) --- doc/source/whatsnew/v0.24.2.rst | 4 +++- pandas/_libs/lib.pyx | 2 +- pandas/tests/frame/test_constructors.py | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index a047ad46e4887..6a9a316da1ec6 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -22,6 +22,8 @@ Fixed Regressions - Fixed regression in :meth:`DataFrame.all` and :meth:`DataFrame.any` where ``bool_only=True`` was ignored (:issue:`25101`) +- Fixed issue in ``DataFrame`` construction with passing a mixed list of mixed types could segfault. (:issue:`25075`) + .. _whatsnew_0242.enhancements: Enhancements @@ -94,4 +96,4 @@ Bug Fixes Contributors ~~~~~~~~~~~~ -.. contributors:: v0.24.1..v0.24.2 \ No newline at end of file +.. contributors:: v0.24.1..v0.24.2 diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx index f8875d60049b1..1f0f0a408aee8 100644 --- a/pandas/_libs/lib.pyx +++ b/pandas/_libs/lib.pyx @@ -2277,7 +2277,7 @@ def to_object_array(rows: object, int min_width=0): result = np.empty((n, k), dtype=object) for i in range(n): - row = input_rows[i] + row = list(input_rows[i]) for j in range(len(row)): result[i, j] = row[j] diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index 90ad48cac3a5f..b97f5e0b6edf9 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -1183,6 +1183,13 @@ def test_constructor_mixed_dict_and_Series(self): index=['a', 'b']) tm.assert_frame_equal(result, expected) + def test_constructor_mixed_type_rows(self): + # Issue 25075 + data = [[1, 2], (3, 4)] + result = DataFrame(data) + expected = DataFrame([[1, 2], [3, 4]]) + tm.assert_frame_equal(result, expected) + def test_constructor_tuples(self): result = DataFrame({'A': [(1, 2), (3, 4)]}) expected = DataFrame({'A': Series([(1, 2), (3, 4)])}) From 5db3455838e620069d448e13328bd7ef633b4f11 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 6 Feb 2019 02:25:46 +0000 Subject: [PATCH 063/215] STY: use pytest.raises context manager (indexes/multi) (#25175) --- pandas/compat/numpy/__init__.py | 4 +- pandas/tests/indexes/multi/test_analytics.py | 99 ++++++++----------- pandas/tests/indexes/multi/test_compat.py | 6 +- .../tests/indexes/multi/test_constructor.py | 67 ++++++------- pandas/tests/indexes/multi/test_contains.py | 23 +++-- pandas/tests/indexes/multi/test_drop.py | 24 +++-- pandas/tests/indexes/multi/test_get_set.py | 15 +-- pandas/tests/indexes/multi/test_indexing.py | 56 +++++++---- pandas/tests/indexes/multi/test_integrity.py | 15 +-- 9 files changed, 167 insertions(+), 142 deletions(-) diff --git a/pandas/compat/numpy/__init__.py b/pandas/compat/numpy/__init__.py index 5e67cf2ee2837..bc9af01a97467 100644 --- a/pandas/compat/numpy/__init__.py +++ b/pandas/compat/numpy/__init__.py @@ -12,6 +12,7 @@ _np_version_under1p13 = _nlv < LooseVersion('1.13') _np_version_under1p14 = _nlv < LooseVersion('1.14') _np_version_under1p15 = _nlv < LooseVersion('1.15') +_np_version_under1p16 = _nlv < LooseVersion('1.16') if _nlv < '1.12': @@ -64,5 +65,6 @@ def np_array_datetime64_compat(arr, *args, **kwargs): __all__ = ['np', '_np_version_under1p13', '_np_version_under1p14', - '_np_version_under1p15' + '_np_version_under1p15', + '_np_version_under1p16' ] diff --git a/pandas/tests/indexes/multi/test_analytics.py b/pandas/tests/indexes/multi/test_analytics.py index dca6180f39664..beec8e544f94b 100644 --- a/pandas/tests/indexes/multi/test_analytics.py +++ b/pandas/tests/indexes/multi/test_analytics.py @@ -4,6 +4,7 @@ import pytest from pandas.compat import lrange +from pandas.compat.numpy import _np_version_under1p16 import pandas as pd from pandas import Index, MultiIndex, date_range, period_range @@ -13,8 +14,11 @@ def test_shift(idx): # GH8083 test the base class for shift - pytest.raises(NotImplementedError, idx.shift, 1) - pytest.raises(NotImplementedError, idx.shift, 1, 2) + msg = "Not supported for type MultiIndex" + with pytest.raises(NotImplementedError, match=msg): + idx.shift(1) + with pytest.raises(NotImplementedError, match=msg): + idx.shift(1, 2) def test_groupby(idx): @@ -50,25 +54,26 @@ def test_truncate(): result = index.truncate(before=1, after=2) assert len(result.levels[0]) == 2 - # after < before - pytest.raises(ValueError, index.truncate, 3, 1) + msg = "after < before" + with pytest.raises(ValueError, match=msg): + index.truncate(3, 1) def test_where(): i = MultiIndex.from_tuples([('A', 1), ('A', 2)]) - with pytest.raises(NotImplementedError): + msg = r"\.where is not supported for MultiIndex operations" + with pytest.raises(NotImplementedError, match=msg): i.where(True) -def test_where_array_like(): +@pytest.mark.parametrize('klass', [list, tuple, np.array, pd.Series]) +def test_where_array_like(klass): i = MultiIndex.from_tuples([('A', 1), ('A', 2)]) - klasses = [list, tuple, np.array, pd.Series] cond = [False, True] - - for klass in klasses: - with pytest.raises(NotImplementedError): - i.where(klass(cond)) + msg = r"\.where is not supported for MultiIndex operations" + with pytest.raises(NotImplementedError, match=msg): + i.where(klass(cond)) # TODO: reshape @@ -141,7 +146,8 @@ def test_take(idx): # if not isinstance(idx, # (DatetimeIndex, PeriodIndex, TimedeltaIndex)): # GH 10791 - with pytest.raises(AttributeError): + msg = "'MultiIndex' object has no attribute 'freq'" + with pytest.raises(AttributeError, match=msg): idx.freq @@ -199,7 +205,8 @@ def test_take_fill_value(): with pytest.raises(ValueError, match=msg): idx.take(np.array([1, 0, -5]), fill_value=True) - with pytest.raises(IndexError): + msg = "index -5 is out of bounds for size 4" + with pytest.raises(IndexError, match=msg): idx.take(np.array([1, -5])) @@ -215,13 +222,15 @@ def test_sub(idx): first = idx # - now raises (previously was set op difference) - with pytest.raises(TypeError): + msg = "cannot perform __sub__ with this index type: MultiIndex" + with pytest.raises(TypeError, match=msg): first - idx[-3:] - with pytest.raises(TypeError): + with pytest.raises(TypeError, match=msg): idx[-3:] - first - with pytest.raises(TypeError): + with pytest.raises(TypeError, match=msg): idx[-3:] - first.tolist() - with pytest.raises(TypeError): + msg = "cannot perform __rsub__ with this index type: MultiIndex" + with pytest.raises(TypeError, match=msg): first.tolist() - idx[-3:] @@ -272,50 +281,28 @@ def test_map_dictlike(idx, mapper): np.arccos, np.arctan, np.sinh, np.cosh, np.tanh, np.arcsinh, np.arccosh, np.arctanh, np.deg2rad, np.rad2deg -]) -def test_numpy_ufuncs(func): +], ids=lambda func: func.__name__) +def test_numpy_ufuncs(idx, func): # test ufuncs of numpy. see: # http://docs.scipy.org/doc/numpy/reference/ufuncs.html - # copy and paste from idx fixture as pytest doesn't support - # parameters and fixtures at the same time. - major_axis = Index(['foo', 'bar', 'baz', 'qux']) - minor_axis = Index(['one', 'two']) - major_codes = np.array([0, 0, 1, 2, 3, 3]) - minor_codes = np.array([0, 1, 0, 1, 0, 1]) - index_names = ['first', 'second'] - - idx = MultiIndex( - levels=[major_axis, minor_axis], - codes=[major_codes, minor_codes], - names=index_names, - verify_integrity=False - ) - - with pytest.raises(Exception): - with np.errstate(all='ignore'): - func(idx) + if _np_version_under1p16: + expected_exception = AttributeError + msg = "'tuple' object has no attribute '{}'".format(func.__name__) + else: + expected_exception = TypeError + msg = ("loop of ufunc does not support argument 0 of type tuple which" + " has no callable {} method").format(func.__name__) + with pytest.raises(expected_exception, match=msg): + func(idx) @pytest.mark.parametrize('func', [ np.isfinite, np.isinf, np.isnan, np.signbit -]) -def test_numpy_type_funcs(func): - # for func in [np.isfinite, np.isinf, np.isnan, np.signbit]: - # copy and paste from idx fixture as pytest doesn't support - # parameters and fixtures at the same time. - major_axis = Index(['foo', 'bar', 'baz', 'qux']) - minor_axis = Index(['one', 'two']) - major_codes = np.array([0, 0, 1, 2, 3, 3]) - minor_codes = np.array([0, 1, 0, 1, 0, 1]) - index_names = ['first', 'second'] - - idx = MultiIndex( - levels=[major_axis, minor_axis], - codes=[major_codes, minor_codes], - names=index_names, - verify_integrity=False - ) - - with pytest.raises(Exception): +], ids=lambda func: func.__name__) +def test_numpy_type_funcs(idx, func): + msg = ("ufunc '{}' not supported for the input types, and the inputs" + " could not be safely coerced to any supported types according to" + " the casting rule ''safe''").format(func.__name__) + with pytest.raises(TypeError, match=msg): func(idx) diff --git a/pandas/tests/indexes/multi/test_compat.py b/pandas/tests/indexes/multi/test_compat.py index f405fc659c709..89685b9feec27 100644 --- a/pandas/tests/indexes/multi/test_compat.py +++ b/pandas/tests/indexes/multi/test_compat.py @@ -124,8 +124,6 @@ def test_compat(indices): def test_pickle_compat_construction(holder): # this is testing for pickle compat - if holder is None: - return - # need an object to create with - pytest.raises(TypeError, holder) + with pytest.raises(TypeError, match="Must pass both levels and codes"): + holder() diff --git a/pandas/tests/indexes/multi/test_constructor.py b/pandas/tests/indexes/multi/test_constructor.py index e6678baf8a996..055d54c613260 100644 --- a/pandas/tests/indexes/multi/test_constructor.py +++ b/pandas/tests/indexes/multi/test_constructor.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from collections import OrderedDict -import re import numpy as np import pytest @@ -30,10 +29,10 @@ def test_constructor_no_levels(): with pytest.raises(ValueError, match=msg): MultiIndex(levels=[], codes=[]) - both_re = re.compile('Must pass both levels and codes') - with pytest.raises(TypeError, match=both_re): + msg = "Must pass both levels and codes" + with pytest.raises(TypeError, match=msg): MultiIndex(levels=[]) - with pytest.raises(TypeError, match=both_re): + with pytest.raises(TypeError, match=msg): MultiIndex(codes=[]) @@ -42,8 +41,8 @@ def test_constructor_nonhashable_names(): levels = [[1, 2], [u'one', u'two']] codes = [[0, 0, 1, 1], [0, 1, 0, 1]] names = (['foo'], ['bar']) - message = "MultiIndex.name must be a hashable type" - with pytest.raises(TypeError, match=message): + msg = r"MultiIndex\.name must be a hashable type" + with pytest.raises(TypeError, match=msg): MultiIndex(levels=levels, codes=codes, names=names) # With .rename() @@ -51,11 +50,11 @@ def test_constructor_nonhashable_names(): codes=[[0, 0, 1, 1], [0, 1, 0, 1]], names=('foo', 'bar')) renamed = [['foor'], ['barr']] - with pytest.raises(TypeError, match=message): + with pytest.raises(TypeError, match=msg): mi.rename(names=renamed) # With .set_names() - with pytest.raises(TypeError, match=message): + with pytest.raises(TypeError, match=msg): mi.set_names(names=renamed) @@ -67,8 +66,9 @@ def test_constructor_mismatched_codes_levels(idx): with pytest.raises(ValueError, match=msg): MultiIndex(levels=levels, codes=codes) - length_error = re.compile('>= length of level') - label_error = re.compile(r'Unequal code lengths: \[4, 2\]') + length_error = (r"On level 0, code max \(3\) >= length of level \(1\)\." + " NOTE: this index is in an inconsistent state") + label_error = r"Unequal code lengths: \[4, 2\]" # important to check that it's looking at the right thing. with pytest.raises(ValueError, match=length_error): @@ -253,21 +253,14 @@ def test_from_arrays_empty(): tm.assert_index_equal(result, expected) -@pytest.mark.parametrize('invalid_array', [ - (1), - ([1]), - ([1, 2]), - ([[1], 2]), - ('a'), - (['a']), - (['a', 'b']), - ([['a'], 'b']), -]) -def test_from_arrays_invalid_input(invalid_array): - invalid_inputs = [1, [1], [1, 2], [[1], 2], - 'a', ['a'], ['a', 'b'], [['a'], 'b']] - for i in invalid_inputs: - pytest.raises(TypeError, MultiIndex.from_arrays, arrays=i) +@pytest.mark.parametrize('invalid_sequence_of_arrays', [ + 1, [1], [1, 2], [[1], 2], 'a', ['a'], ['a', 'b'], [['a'], 'b']]) +def test_from_arrays_invalid_input(invalid_sequence_of_arrays): + msg = (r"Input must be a list / sequence of array-likes|" + r"Input must be list-like|" + r"object of type 'int' has no len\(\)") + with pytest.raises(TypeError, match=msg): + MultiIndex.from_arrays(arrays=invalid_sequence_of_arrays) @pytest.mark.parametrize('idx1, idx2', [ @@ -332,9 +325,10 @@ def test_tuples_with_name_string(): # GH 15110 and GH 14848 li = [(0, 0, 1), (0, 1, 0), (1, 0, 0)] - with pytest.raises(ValueError): + msg = "Names should be list-like for a MultiIndex" + with pytest.raises(ValueError, match=msg): pd.Index(li, name='abc') - with pytest.raises(ValueError): + with pytest.raises(ValueError, match=msg): pd.Index(li, name='a') @@ -398,7 +392,10 @@ def test_from_product_empty_three_levels(N): [['a'], 'b'], ]) def test_from_product_invalid_input(invalid_input): - pytest.raises(TypeError, MultiIndex.from_product, iterables=invalid_input) + msg = (r"Input must be a list / sequence of iterables|" + "Input must be list-like") + with pytest.raises(TypeError, match=msg): + MultiIndex.from_product(iterables=invalid_input) def test_from_product_datetimeindex(): @@ -563,15 +560,15 @@ def test_from_frame_valid_names(names_in, names_out): assert mi.names == names_out -@pytest.mark.parametrize('names_in,names_out', [ - ('bad_input', ValueError("Names should be list-like for a MultiIndex")), - (['a', 'b', 'c'], ValueError("Length of names must match number of " - "levels in MultiIndex.")) +@pytest.mark.parametrize('names,expected_error_msg', [ + ('bad_input', "Names should be list-like for a MultiIndex"), + (['a', 'b', 'c'], + "Length of names must match number of levels in MultiIndex") ]) -def test_from_frame_invalid_names(names_in, names_out): +def test_from_frame_invalid_names(names, expected_error_msg): # GH 22420 df = pd.DataFrame([['a', 'a'], ['a', 'b'], ['b', 'a'], ['b', 'b']], columns=pd.MultiIndex.from_tuples([('L1', 'x'), ('L2', 'y')])) - with pytest.raises(type(names_out), match=names_out.args[0]): - pd.MultiIndex.from_frame(df, names=names_in) + with pytest.raises(ValueError, match=expected_error_msg): + pd.MultiIndex.from_frame(df, names=names) diff --git a/pandas/tests/indexes/multi/test_contains.py b/pandas/tests/indexes/multi/test_contains.py index b73ff11a4dd4e..56836b94a6b03 100644 --- a/pandas/tests/indexes/multi/test_contains.py +++ b/pandas/tests/indexes/multi/test_contains.py @@ -83,15 +83,24 @@ def test_isin_level_kwarg(): tm.assert_numpy_array_equal(expected, idx.isin(vals_1, level=1)) tm.assert_numpy_array_equal(expected, idx.isin(vals_1, level=-1)) - pytest.raises(IndexError, idx.isin, vals_0, level=5) - pytest.raises(IndexError, idx.isin, vals_0, level=-5) - - pytest.raises(KeyError, idx.isin, vals_0, level=1.0) - pytest.raises(KeyError, idx.isin, vals_1, level=-1.0) - pytest.raises(KeyError, idx.isin, vals_1, level='A') + msg = "Too many levels: Index has only 2 levels, not 6" + with pytest.raises(IndexError, match=msg): + idx.isin(vals_0, level=5) + msg = ("Too many levels: Index has only 2 levels, -5 is not a valid level" + " number") + with pytest.raises(IndexError, match=msg): + idx.isin(vals_0, level=-5) + + with pytest.raises(KeyError, match=r"'Level 1\.0 not found'"): + idx.isin(vals_0, level=1.0) + with pytest.raises(KeyError, match=r"'Level -1\.0 not found'"): + idx.isin(vals_1, level=-1.0) + with pytest.raises(KeyError, match="'Level A not found'"): + idx.isin(vals_1, level='A') idx.names = ['A', 'B'] tm.assert_numpy_array_equal(expected, idx.isin(vals_0, level='A')) tm.assert_numpy_array_equal(expected, idx.isin(vals_1, level='B')) - pytest.raises(KeyError, idx.isin, vals_1, level='C') + with pytest.raises(KeyError, match="'Level C not found'"): + idx.isin(vals_1, level='C') diff --git a/pandas/tests/indexes/multi/test_drop.py b/pandas/tests/indexes/multi/test_drop.py index 0cf73d3d752ad..ac167c126fd13 100644 --- a/pandas/tests/indexes/multi/test_drop.py +++ b/pandas/tests/indexes/multi/test_drop.py @@ -4,7 +4,7 @@ import numpy as np import pytest -from pandas.compat import lrange +from pandas.compat import PY2, lrange from pandas.errors import PerformanceWarning import pandas as pd @@ -12,6 +12,7 @@ import pandas.util.testing as tm +@pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_drop(idx): dropped = idx.drop([('foo', 'two'), ('qux', 'one')]) @@ -31,13 +32,17 @@ def test_drop(idx): tm.assert_index_equal(dropped, expected) index = MultiIndex.from_tuples([('bar', 'two')]) - pytest.raises(KeyError, idx.drop, [('bar', 'two')]) - pytest.raises(KeyError, idx.drop, index) - pytest.raises(KeyError, idx.drop, ['foo', 'two']) + with pytest.raises(KeyError, match=r"^10$"): + idx.drop([('bar', 'two')]) + with pytest.raises(KeyError, match=r"^10$"): + idx.drop(index) + with pytest.raises(KeyError, match=r"^'two'$"): + idx.drop(['foo', 'two']) # partially correct argument mixed_index = MultiIndex.from_tuples([('qux', 'one'), ('bar', 'two')]) - pytest.raises(KeyError, idx.drop, mixed_index) + with pytest.raises(KeyError, match=r"^10$"): + idx.drop(mixed_index) # error='ignore' dropped = idx.drop(index, errors='ignore') @@ -59,7 +64,8 @@ def test_drop(idx): # mixed partial / full drop / error='ignore' mixed_index = ['foo', ('qux', 'one'), 'two'] - pytest.raises(KeyError, idx.drop, mixed_index) + with pytest.raises(KeyError, match=r"^'two'$"): + idx.drop(mixed_index) dropped = idx.drop(mixed_index, errors='ignore') expected = idx[[2, 3, 5]] tm.assert_index_equal(dropped, expected) @@ -98,10 +104,12 @@ def test_droplevel_list(): expected = index[:2] assert dropped.equals(expected) - with pytest.raises(ValueError): + msg = ("Cannot remove 3 levels from an index with 3 levels: at least one" + " level must be left") + with pytest.raises(ValueError, match=msg): index[:2].droplevel(['one', 'two', 'three']) - with pytest.raises(KeyError): + with pytest.raises(KeyError, match="'Level four not found'"): index[:2].droplevel(['one', 'four']) diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index d201cb2eb178b..62911c7032aca 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -25,7 +25,9 @@ def test_get_level_number_integer(idx): idx.names = [1, 0] assert idx._get_level_number(1) == 0 assert idx._get_level_number(0) == 1 - pytest.raises(IndexError, idx._get_level_number, 2) + msg = "Too many levels: Index has only 2 levels, not 3" + with pytest.raises(IndexError, match=msg): + idx._get_level_number(2) with pytest.raises(KeyError, match='Level fourth not found'): idx._get_level_number('fourth') @@ -62,7 +64,7 @@ def test_get_value_duplicates(): names=['tag', 'day']) assert index.get_loc('D') == slice(0, 3) - with pytest.raises(KeyError): + with pytest.raises(KeyError, match=r"^'D'$"): index._engine.get_value(np.array([]), 'D') @@ -125,7 +127,8 @@ def test_set_name_methods(idx, index_names): ind = idx.set_names(new_names) assert idx.names == index_names assert ind.names == new_names - with pytest.raises(ValueError, match="^Length"): + msg = "Length of names must match number of levels in MultiIndex" + with pytest.raises(ValueError, match=msg): ind.set_names(new_names + new_names) new_names2 = [name + "SUFFIX2" for name in new_names] res = ind.set_names(new_names2, inplace=True) @@ -163,10 +166,10 @@ def test_set_levels_codes_directly(idx): minor_codes = [(x + 1) % 1 for x in minor_codes] new_codes = [major_codes, minor_codes] - with pytest.raises(AttributeError): + msg = "can't set attribute" + with pytest.raises(AttributeError, match=msg): idx.levels = new_levels - - with pytest.raises(AttributeError): + with pytest.raises(AttributeError, match=msg): idx.codes = new_codes diff --git a/pandas/tests/indexes/multi/test_indexing.py b/pandas/tests/indexes/multi/test_indexing.py index c40ecd9e82a07..c2af3b2050d8d 100644 --- a/pandas/tests/indexes/multi/test_indexing.py +++ b/pandas/tests/indexes/multi/test_indexing.py @@ -6,7 +6,7 @@ import numpy as np import pytest -from pandas.compat import lrange +from pandas.compat import PY2, lrange import pandas as pd from pandas import ( @@ -112,13 +112,14 @@ def test_slice_locs_not_contained(): def test_putmask_with_wrong_mask(idx): # GH18368 - with pytest.raises(ValueError): + msg = "putmask: mask and data must be the same size" + with pytest.raises(ValueError, match=msg): idx.putmask(np.ones(len(idx) + 1, np.bool), 1) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match=msg): idx.putmask(np.ones(len(idx) - 1, np.bool), 1) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match=msg): idx.putmask('foo', 1) @@ -176,9 +177,12 @@ def test_get_indexer(): def test_get_indexer_nearest(): midx = MultiIndex.from_tuples([('a', 1), ('b', 2)]) - with pytest.raises(NotImplementedError): + msg = ("method='nearest' not implemented yet for MultiIndex; see GitHub" + " issue 9365") + with pytest.raises(NotImplementedError, match=msg): midx.get_indexer(['a'], method='nearest') - with pytest.raises(NotImplementedError): + msg = "tolerance not implemented yet for MultiIndex" + with pytest.raises(NotImplementedError, match=msg): midx.get_indexer(['a'], method='pad', tolerance=2) @@ -251,20 +255,26 @@ def test_getitem_bool_index_single(ind1, ind2): tm.assert_index_equal(idx[ind2], expected) +@pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_get_loc(idx): assert idx.get_loc(('foo', 'two')) == 1 assert idx.get_loc(('baz', 'two')) == 3 - pytest.raises(KeyError, idx.get_loc, ('bar', 'two')) - pytest.raises(KeyError, idx.get_loc, 'quux') + with pytest.raises(KeyError, match=r"^10$"): + idx.get_loc(('bar', 'two')) + with pytest.raises(KeyError, match=r"^'quux'$"): + idx.get_loc('quux') - pytest.raises(NotImplementedError, idx.get_loc, 'foo', - method='nearest') + msg = ("only the default get_loc method is currently supported for" + " MultiIndex") + with pytest.raises(NotImplementedError, match=msg): + idx.get_loc('foo', method='nearest') # 3 levels index = MultiIndex(levels=[Index(lrange(4)), Index(lrange(4)), Index( lrange(4))], codes=[np.array([0, 0, 1, 2, 2, 2, 3, 3]), np.array( [0, 1, 0, 0, 0, 1, 0, 1]), np.array([1, 0, 1, 1, 0, 0, 1, 0])]) - pytest.raises(KeyError, index.get_loc, (1, 1)) + with pytest.raises(KeyError, match=r"^\(1, 1\)$"): + index.get_loc((1, 1)) assert index.get_loc((2, 0)) == slice(3, 5) @@ -297,11 +307,14 @@ def test_get_loc_level(): assert loc == expected assert new_index is None - pytest.raises(KeyError, index.get_loc_level, (2, 2)) + with pytest.raises(KeyError, match=r"^\(2, 2\)$"): + index.get_loc_level((2, 2)) # GH 22221: unused label - pytest.raises(KeyError, index.drop(2).get_loc_level, 2) + with pytest.raises(KeyError, match=r"^2$"): + index.drop(2).get_loc_level(2) # Unused label on unsorted level: - pytest.raises(KeyError, index.drop(1, level=2).get_loc_level, 2, 2) + with pytest.raises(KeyError, match=r"^2$"): + index.drop(1, level=2).get_loc_level(2, level=2) index = MultiIndex(levels=[[2000], lrange(4)], codes=[np.array( [0, 0, 0, 0]), np.array([0, 1, 2, 3])]) @@ -342,8 +355,10 @@ def test_get_loc_cast_bool(): assert idx.get_loc((0, 1)) == 1 assert idx.get_loc((1, 0)) == 2 - pytest.raises(KeyError, idx.get_loc, (False, True)) - pytest.raises(KeyError, idx.get_loc, (True, False)) + with pytest.raises(KeyError, match=r"^\(False, True\)$"): + idx.get_loc((False, True)) + with pytest.raises(KeyError, match=r"^\(True, False\)$"): + idx.get_loc((True, False)) @pytest.mark.parametrize('level', [0, 1]) @@ -361,9 +376,12 @@ def test_get_loc_missing_nan(): # GH 8569 idx = MultiIndex.from_arrays([[1.0, 2.0], [3.0, 4.0]]) assert isinstance(idx.get_loc(1), slice) - pytest.raises(KeyError, idx.get_loc, 3) - pytest.raises(KeyError, idx.get_loc, np.nan) - pytest.raises(KeyError, idx.get_loc, [np.nan]) + with pytest.raises(KeyError, match=r"^3\.0$"): + idx.get_loc(3) + with pytest.raises(KeyError, match=r"^nan$"): + idx.get_loc(np.nan) + with pytest.raises(KeyError, match=r"^\[nan\]$"): + idx.get_loc([np.nan]) def test_get_indexer_categorical_time(): diff --git a/pandas/tests/indexes/multi/test_integrity.py b/pandas/tests/indexes/multi/test_integrity.py index c1638a9cde660..a7dc093147725 100644 --- a/pandas/tests/indexes/multi/test_integrity.py +++ b/pandas/tests/indexes/multi/test_integrity.py @@ -159,7 +159,8 @@ def test_isna_behavior(idx): # should not segfault GH5123 # NOTE: if MI representation changes, may make sense to allow # isna(MI) - with pytest.raises(NotImplementedError): + msg = "isna is not defined for MultiIndex" + with pytest.raises(NotImplementedError, match=msg): pd.isna(idx) @@ -168,16 +169,16 @@ def test_large_multiindex_error(): df_below_1000000 = pd.DataFrame( 1, index=pd.MultiIndex.from_product([[1, 2], range(499999)]), columns=['dest']) - with pytest.raises(KeyError): + with pytest.raises(KeyError, match=r"^\(-1, 0\)$"): df_below_1000000.loc[(-1, 0), 'dest'] - with pytest.raises(KeyError): + with pytest.raises(KeyError, match=r"^\(3, 0\)$"): df_below_1000000.loc[(3, 0), 'dest'] df_above_1000000 = pd.DataFrame( 1, index=pd.MultiIndex.from_product([[1, 2], range(500001)]), columns=['dest']) - with pytest.raises(KeyError): + with pytest.raises(KeyError, match=r"^\(-1, 0\)$"): df_above_1000000.loc[(-1, 0), 'dest'] - with pytest.raises(KeyError): + with pytest.raises(KeyError, match=r"^\(3, 0\)$"): df_above_1000000.loc[(3, 0), 'dest'] @@ -260,7 +261,9 @@ def test_hash_error(indices): def test_mutability(indices): if not len(indices): return - pytest.raises(TypeError, indices.__setitem__, 0, indices[0]) + msg = "Index does not support mutable operations" + with pytest.raises(TypeError, match=msg): + indices[0] = indices[0] def test_wrong_number_names(indices): From bd58782a06ce9105d09f6ce2429d6863abd0c6c7 Mon Sep 17 00:00:00 2001 From: Saurav Chakravorty Date: Wed, 6 Feb 2019 08:08:46 +0530 Subject: [PATCH 064/215] DOC: Updates to Timestamp document (#25163) --- pandas/_libs/tslibs/timestamps.pyx | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index cad63b4323015..25b0b4069cf7c 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -504,6 +504,9 @@ cdef class _Timestamp(datetime): @property def asm8(self): + """ + Return numpy datetime64 format in nanoseconds. + """ return np.datetime64(self.value, 'ns') @property @@ -570,15 +573,18 @@ class Timestamp(_Timestamp): Using the primary calling convention: This converts a datetime-like string + >>> pd.Timestamp('2017-01-01T12') Timestamp('2017-01-01 12:00:00') This converts a float representing a Unix epoch in units of seconds + >>> pd.Timestamp(1513393355.5, unit='s') Timestamp('2017-12-16 03:02:35.500000') This converts an int representing a Unix-epoch in units of seconds and for a particular timezone + >>> pd.Timestamp(1513393355, unit='s', tz='US/Pacific') Timestamp('2017-12-15 19:02:35-0800', tz='US/Pacific') @@ -934,6 +940,9 @@ class Timestamp(_Timestamp): @property def dayofweek(self): + """ + Return day of whe week. + """ return self.weekday() def day_name(self, locale=None): @@ -983,30 +992,48 @@ class Timestamp(_Timestamp): @property def dayofyear(self): + """ + Return the day of the year. + """ return ccalendar.get_day_of_year(self.year, self.month, self.day) @property def week(self): + """ + Return the week number of the year. + """ return ccalendar.get_week_of_year(self.year, self.month, self.day) weekofyear = week @property def quarter(self): + """ + Return the quarter of the year. + """ return ((self.month - 1) // 3) + 1 @property def days_in_month(self): + """ + Return the number of days in the month. + """ return ccalendar.get_days_in_month(self.year, self.month) daysinmonth = days_in_month @property def freqstr(self): + """ + Return the total number of days in the month. + """ return getattr(self.freq, 'freqstr', self.freq) @property def is_month_start(self): + """ + Return True if date is first day of month. + """ if self.freq is None: # fast-path for non-business frequencies return self.day == 1 @@ -1014,6 +1041,9 @@ class Timestamp(_Timestamp): @property def is_month_end(self): + """ + Return True if date is last day of month. + """ if self.freq is None: # fast-path for non-business frequencies return self.day == self.days_in_month @@ -1021,6 +1051,9 @@ class Timestamp(_Timestamp): @property def is_quarter_start(self): + """ + Return True if date is first day of the quarter. + """ if self.freq is None: # fast-path for non-business frequencies return self.day == 1 and self.month % 3 == 1 @@ -1028,6 +1061,9 @@ class Timestamp(_Timestamp): @property def is_quarter_end(self): + """ + Return True if date is last day of the quarter. + """ if self.freq is None: # fast-path for non-business frequencies return (self.month % 3) == 0 and self.day == self.days_in_month @@ -1035,6 +1071,9 @@ class Timestamp(_Timestamp): @property def is_year_start(self): + """ + Return True if date is first day of the year. + """ if self.freq is None: # fast-path for non-business frequencies return self.day == self.month == 1 @@ -1042,6 +1081,9 @@ class Timestamp(_Timestamp): @property def is_year_end(self): + """ + Return True if date is last day of the year. + """ if self.freq is None: # fast-path for non-business frequencies return self.month == 12 and self.day == 31 @@ -1049,6 +1091,9 @@ class Timestamp(_Timestamp): @property def is_leap_year(self): + """ + Return True if year is a leap year. + """ return bool(ccalendar.is_leapyear(self.year)) def tz_localize(self, tz, ambiguous='raise', nonexistent='raise', From 2d8f0ab1a127d594cd9e24f5d74a2e66c10beabb Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 5 Feb 2019 21:43:25 -0500 Subject: [PATCH 065/215] BLD: pin cython language level to '2' (#25145) Not explicitly pinning the language level has been producing future warnings from cython. The next release of cython is going to change the default level to '3str' under which the pandas cython extensions do not compile. The long term solution is to update the cython files to the next language level, but this is a stop-gap to keep pandas building. --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4bf040b8c8e20..c8d29a2e4be5a 100755 --- a/setup.py +++ b/setup.py @@ -450,7 +450,8 @@ def run(self): # Note: if not using `cythonize`, coverage can be enabled by # pinning `ext.cython_directives = directives` to each ext in extensions. # github.com/cython/cython/wiki/enhancements-compilerdirectives#in-setuppy -directives = {'linetrace': False} +directives = {'linetrace': False, + 'language_level': 2} macros = [] if linetrace: # https://pypkg.com/pypi/pytest-cython/f/tests/example-project/setup.py From 9cac5a9abdded0fcd0f1c698ce1df9cf70138b22 Mon Sep 17 00:00:00 2001 From: h-vetinari <33685575+h-vetinari@users.noreply.github.com> Date: Wed, 6 Feb 2019 03:56:31 +0100 Subject: [PATCH 066/215] CLN: Use ABCs in set_index (#25128) --- pandas/core/frame.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index afc4194e71eb1..098e65718de5c 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -71,7 +71,7 @@ is_iterator, is_sequence, is_named_tuple) -from pandas.core.dtypes.generic import ABCSeries, ABCIndexClass +from pandas.core.dtypes.generic import ABCSeries, ABCIndexClass, ABCMultiIndex from pandas.core.dtypes.missing import isna, notna from pandas.core import algorithms @@ -4166,7 +4166,7 @@ def set_index(self, keys, drop=True, append=False, inplace=False, names = [] if append: names = [x for x in self.index.names] - if isinstance(self.index, MultiIndex): + if isinstance(self.index, ABCMultiIndex): for i in range(self.index.nlevels): arrays.append(self.index._get_level_values(i)) else: @@ -4174,29 +4174,23 @@ def set_index(self, keys, drop=True, append=False, inplace=False, to_remove = [] for col in keys: - if isinstance(col, MultiIndex): - # append all but the last column so we don't have to modify - # the end of this loop - for n in range(col.nlevels - 1): + if isinstance(col, ABCMultiIndex): + for n in range(col.nlevels): arrays.append(col._get_level_values(n)) - - level = col._get_level_values(col.nlevels - 1) names.extend(col.names) - elif isinstance(col, Series): - level = col._values - names.append(col.name) - elif isinstance(col, Index): - level = col + elif isinstance(col, (ABCIndexClass, ABCSeries)): + # if Index then not MultiIndex (treated above) + arrays.append(col) names.append(col.name) - elif isinstance(col, (list, np.ndarray, Index)): - level = col + elif isinstance(col, (list, np.ndarray)): + arrays.append(col) names.append(None) + # from here, col can only be a column label else: - level = frame[col]._values + arrays.append(frame[col]._values) names.append(col) if drop: to_remove.append(col) - arrays.append(level) index = ensure_index_from_sequences(arrays, names) From efa23ca3a0e5a5ec8a46f148eb0c2505b347d4c7 Mon Sep 17 00:00:00 2001 From: Cecilia Date: Wed, 6 Feb 2019 03:12:23 +0000 Subject: [PATCH 067/215] DOC: update docstring for series.nunique (#25116) --- pandas/core/base.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index 7b3152595e4b2..24695dd4cfd9c 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -1323,12 +1323,31 @@ def nunique(self, dropna=True): Parameters ---------- - dropna : boolean, default True + dropna : bool, default True Don't include NaN in the count. Returns ------- - nunique : int + int + + See Also + -------- + DataFrame.nunique: Method nunique for DataFrame. + Series.count: Count non-NA/null observations in the Series. + + Examples + -------- + >>> s = pd.Series([1, 3, 5, 7, 7]) + >>> s + 0 1 + 1 3 + 2 5 + 3 7 + 4 7 + dtype: int64 + + >>> s.nunique() + 4 """ uniqs = self.unique() n = len(uniqs) From 776530cc62d3d0f58c20a95b79776d9c69a2dbf2 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 5 Feb 2019 19:47:25 -0800 Subject: [PATCH 068/215] DEPR: remove PanelGroupBy, disable DataFrame.to_panel (#25047) --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/frame.py | 40 +--- pandas/core/groupby/__init__.py | 2 +- pandas/core/groupby/generic.py | 90 +-------- pandas/core/panel.py | 4 +- pandas/core/resample.py | 9 +- pandas/io/pytables.py | 39 +--- pandas/tests/dtypes/test_generic.py | 5 +- pandas/tests/frame/test_subclass.py | 25 +-- pandas/tests/groupby/test_groupby.py | 25 --- pandas/tests/groupby/test_grouping.py | 26 +-- pandas/tests/io/test_pytables.py | 23 --- pandas/tests/resample/test_datetime_index.py | 53 +----- pandas/tests/resample/test_time_grouper.py | 23 +-- pandas/tests/test_panel.py | 189 ------------------- 15 files changed, 14 insertions(+), 541 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 09626be713c4f..a3fb1c575e7f1 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -51,7 +51,7 @@ Deprecations Removal of prior version deprecations/changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - +- Removed (parts of) :class:`Panel` (:issue:`25047`) - - - diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 098e65718de5c..0c160d1a2bb54 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -1974,45 +1974,7 @@ def to_panel(self): ------- panel : Panel """ - # only support this kind for now - if (not isinstance(self.index, MultiIndex) or # pragma: no cover - len(self.index.levels) != 2): - raise NotImplementedError('Only 2-level MultiIndex are supported.') - - if not self.index.is_unique: - raise ValueError("Can't convert non-uniquely indexed " - "DataFrame to Panel") - - self._consolidate_inplace() - - # minor axis must be sorted - if self.index.lexsort_depth < 2: - selfsorted = self.sort_index(level=0) - else: - selfsorted = self - - major_axis, minor_axis = selfsorted.index.levels - major_codes, minor_codes = selfsorted.index.codes - shape = len(major_axis), len(minor_axis) - - # preserve names, if any - major_axis = major_axis.copy() - major_axis.name = self.index.names[0] - - minor_axis = minor_axis.copy() - minor_axis.name = self.index.names[1] - - # create new axes - new_axes = [selfsorted.columns, major_axis, minor_axis] - - # create new manager - new_mgr = selfsorted._data.reshape_nd(axes=new_axes, - labels=[major_codes, - minor_codes], - shape=shape, - ref_items=selfsorted.columns) - - return self._constructor_expanddim(new_mgr) + raise NotImplementedError("Panel is being removed in pandas 0.25.0.") @deprecate_kwarg(old_arg_name='encoding', new_arg_name=None) def to_stata(self, fname, convert_dates=None, write_index=True, diff --git a/pandas/core/groupby/__init__.py b/pandas/core/groupby/__init__.py index 9c15a5ebfe0f2..ac35f3825e5e8 100644 --- a/pandas/core/groupby/__init__.py +++ b/pandas/core/groupby/__init__.py @@ -1,4 +1,4 @@ from pandas.core.groupby.groupby import GroupBy # noqa: F401 from pandas.core.groupby.generic import ( # noqa: F401 - SeriesGroupBy, DataFrameGroupBy, PanelGroupBy) + SeriesGroupBy, DataFrameGroupBy) from pandas.core.groupby.grouper import Grouper # noqa: F401 diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index 78aa6d13a9e02..c8ea9ce689871 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -1,5 +1,5 @@ """ -Define the SeriesGroupBy, DataFrameGroupBy, and PanelGroupBy +Define the SeriesGroupBy and DataFrameGroupBy classes that hold the groupby interfaces (and some implementations). These are user facing as the result of the ``df.groupby(...)`` operations, @@ -39,7 +39,6 @@ from pandas.core.index import CategoricalIndex, Index, MultiIndex import pandas.core.indexes.base as ibase from pandas.core.internals import BlockManager, make_block -from pandas.core.panel import Panel from pandas.core.series import Series from pandas.plotting._core import boxplot_frame_groupby @@ -1586,90 +1585,3 @@ def groupby_series(obj, col=None): return results boxplot = boxplot_frame_groupby - - -class PanelGroupBy(NDFrameGroupBy): - - def aggregate(self, arg, *args, **kwargs): - return super(PanelGroupBy, self).aggregate(arg, *args, **kwargs) - - agg = aggregate - - def _iterate_slices(self): - if self.axis == 0: - # kludge - if self._selection is None: - slice_axis = self._selected_obj.items - else: - slice_axis = self._selection_list - slicer = lambda x: self._selected_obj[x] - else: - raise NotImplementedError("axis other than 0 is not supported") - - for val in slice_axis: - if val in self.exclusions: - continue - - yield val, slicer(val) - - def aggregate(self, arg, *args, **kwargs): - """ - Aggregate using input function or dict of {column -> function} - - Parameters - ---------- - arg : function or dict - Function to use for aggregating groups. If a function, must either - work when passed a Panel or when passed to Panel.apply. If - pass a dict, the keys must be DataFrame column names - - Returns - ------- - aggregated : Panel - """ - if isinstance(arg, compat.string_types): - return getattr(self, arg)(*args, **kwargs) - - return self._aggregate_generic(arg, *args, **kwargs) - - def _wrap_generic_output(self, result, obj): - if self.axis == 0: - new_axes = list(obj.axes) - new_axes[0] = self.grouper.result_index - elif self.axis == 1: - x, y, z = obj.axes - new_axes = [self.grouper.result_index, z, x] - else: - x, y, z = obj.axes - new_axes = [self.grouper.result_index, y, x] - - result = Panel._from_axes(result, new_axes) - - if self.axis == 1: - result = result.swapaxes(0, 1).swapaxes(0, 2) - elif self.axis == 2: - result = result.swapaxes(0, 2) - - return result - - def _aggregate_item_by_item(self, func, *args, **kwargs): - obj = self._obj_with_exclusions - result = {} - - if self.axis > 0: - for item in obj: - try: - itemg = DataFrameGroupBy(obj[item], - axis=self.axis - 1, - grouper=self.grouper) - result[item] = itemg.aggregate(func, *args, **kwargs) - except (ValueError, TypeError): - raise - new_axes = list(obj.axes) - new_axes[self.axis] = self.grouper.result_index - return Panel._from_axes(result, new_axes) - else: - raise ValueError("axis value must be greater than 0") - - def _wrap_aggregated_output(self, output, names=None): - raise AbstractMethodError(self) diff --git a/pandas/core/panel.py b/pandas/core/panel.py index c8afafde48ac2..de535eeea4b5e 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -917,9 +917,7 @@ def groupby(self, function, axis='major'): ------- grouped : PanelGroupBy """ - from pandas.core.groupby import PanelGroupBy - axis = self._get_axis_number(axis) - return PanelGroupBy(self, function, axis=axis) + raise NotImplementedError("Panel is removed in pandas 0.25.0") def to_frame(self, filter_observations=True): """ diff --git a/pandas/core/resample.py b/pandas/core/resample.py index a7204fcd9dd20..fbddc9ff29ce9 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -20,7 +20,7 @@ import pandas.core.algorithms as algos from pandas.core.generic import _shared_docs from pandas.core.groupby.base import GroupByMixin -from pandas.core.groupby.generic import PanelGroupBy, SeriesGroupBy +from pandas.core.groupby.generic import SeriesGroupBy from pandas.core.groupby.groupby import ( GroupBy, _GroupBy, _pipe_template, groupby) from pandas.core.groupby.grouper import Grouper @@ -340,12 +340,7 @@ def _groupby_and_aggregate(self, how, grouper=None, *args, **kwargs): obj = self._selected_obj - try: - grouped = groupby(obj, by=None, grouper=grouper, axis=self.axis) - except TypeError: - - # panel grouper - grouped = PanelGroupBy(obj, grouper=grouper, axis=self.axis) + grouped = groupby(obj, by=None, grouper=grouper, axis=self.axis) try: if isinstance(obj, ABCDataFrame) and compat.callable(how): diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index 2ab6ddb5b25c7..00fa01bb23c8c 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -31,7 +31,7 @@ PeriodIndex, Series, SparseDataFrame, SparseSeries, TimedeltaIndex, compat, concat, isna, to_datetime) from pandas.core import config -from pandas.core.algorithms import match, unique +from pandas.core.algorithms import unique from pandas.core.arrays.categorical import ( Categorical, _factorize_from_iterables) from pandas.core.arrays.sparse import BlockIndex, IntIndex @@ -3944,29 +3944,7 @@ def read(self, where=None, columns=None, **kwargs): objs.append(obj) else: - warnings.warn(duplicate_doc, DuplicateWarning, stacklevel=5) - - # reconstruct - long_index = MultiIndex.from_arrays( - [i.values for i in self.index_axes]) - - for c in self.values_axes: - lp = DataFrame(c.data, index=long_index, columns=c.values) - - # need a better algorithm - tuple_index = long_index.values - - unique_tuples = unique(tuple_index) - unique_tuples = com.asarray_tuplesafe(unique_tuples) - - indexer = match(unique_tuples, tuple_index) - indexer = ensure_platform_int(indexer) - - new_index = long_index.take(indexer) - new_values = lp.values.take(indexer, axis=0) - - lp = DataFrame(new_values, index=new_index, columns=lp.columns) - objs.append(lp.to_panel()) + raise NotImplementedError("Panel is removed in pandas 0.25.0") # create the composite object if len(objs) == 1: @@ -4875,16 +4853,3 @@ def select_coords(self): return self.coordinates return np.arange(start, stop) - -# utilities ### - - -def timeit(key, df, fn=None, remove=True, **kwargs): - if fn is None: - fn = 'timeit.h5' - store = HDFStore(fn, mode='w') - store.append(key, df, **kwargs) - store.close() - - if remove: - os.remove(fn) diff --git a/pandas/tests/dtypes/test_generic.py b/pandas/tests/dtypes/test_generic.py index 1622088d05f4d..2bb3559d56d61 100644 --- a/pandas/tests/dtypes/test_generic.py +++ b/pandas/tests/dtypes/test_generic.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from warnings import catch_warnings, simplefilter +from warnings import catch_warnings import numpy as np @@ -39,9 +39,6 @@ def test_abc_types(self): assert isinstance(pd.Int64Index([1, 2, 3]), gt.ABCIndexClass) assert isinstance(pd.Series([1, 2, 3]), gt.ABCSeries) assert isinstance(self.df, gt.ABCDataFrame) - with catch_warnings(record=True): - simplefilter('ignore', FutureWarning) - assert isinstance(self.df.to_panel(), gt.ABCPanel) assert isinstance(self.sparse_series, gt.ABCSparseSeries) assert isinstance(self.sparse_array, gt.ABCSparseArray) assert isinstance(self.sparse_frame, gt.ABCSparseDataFrame) diff --git a/pandas/tests/frame/test_subclass.py b/pandas/tests/frame/test_subclass.py index 4f0747c0d6945..2e3696e7e04cc 100644 --- a/pandas/tests/frame/test_subclass.py +++ b/pandas/tests/frame/test_subclass.py @@ -6,7 +6,7 @@ import pytest import pandas as pd -from pandas import DataFrame, Index, MultiIndex, Panel, Series +from pandas import DataFrame, Index, MultiIndex, Series from pandas.tests.frame.common import TestData import pandas.util.testing as tm @@ -125,29 +125,6 @@ def test_indexing_sliced(self): tm.assert_series_equal(res, exp) assert isinstance(res, tm.SubclassedSeries) - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") - def test_to_panel_expanddim(self): - # GH 9762 - - class SubclassedFrame(DataFrame): - - @property - def _constructor_expanddim(self): - return SubclassedPanel - - class SubclassedPanel(Panel): - pass - - index = MultiIndex.from_tuples([(0, 0), (0, 1), (0, 2)]) - df = SubclassedFrame({'X': [1, 2, 3], 'Y': [4, 5, 6]}, index=index) - result = df.to_panel() - assert isinstance(result, SubclassedPanel) - expected = SubclassedPanel([[[1, 2, 3]], [[4, 5, 6]]], - items=['X', 'Y'], major_axis=[0], - minor_axis=[0, 1, 2], - dtype='int64') - tm.assert_panel_equal(result, expected) - def test_subclass_attr_err_propagation(self): # GH 11808 class A(DataFrame): diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py index 98c917a6eca3c..0bfc7ababd18a 100644 --- a/pandas/tests/groupby/test_groupby.py +++ b/pandas/tests/groupby/test_groupby.py @@ -1239,31 +1239,6 @@ def _check_work(gp): # _check_work(panel.groupby(lambda x: x.month, axis=1)) -@pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") -def test_panel_groupby(): - panel = tm.makePanel() - tm.add_nans(panel) - grouped = panel.groupby({'ItemA': 0, 'ItemB': 0, 'ItemC': 1}, - axis='items') - agged = grouped.mean() - agged2 = grouped.agg(lambda x: x.mean('items')) - - tm.assert_panel_equal(agged, agged2) - - tm.assert_index_equal(agged.items, Index([0, 1])) - - grouped = panel.groupby(lambda x: x.month, axis='major') - agged = grouped.mean() - - exp = Index(sorted(list(set(panel.major_axis.month)))) - tm.assert_index_equal(agged.major_axis, exp) - - grouped = panel.groupby({'A': 0, 'B': 0, 'C': 1, 'D': 1}, - axis='minor') - agged = grouped.mean() - tm.assert_index_equal(agged.minor_axis, Index([0, 1])) - - def test_groupby_2d_malformed(): d = DataFrame(index=lrange(2)) d['group'] = ['g1', 'g2'] diff --git a/pandas/tests/groupby/test_grouping.py b/pandas/tests/groupby/test_grouping.py index a509a7cb57c97..44b5bd5f13992 100644 --- a/pandas/tests/groupby/test_grouping.py +++ b/pandas/tests/groupby/test_grouping.py @@ -14,8 +14,7 @@ from pandas.core.groupby.grouper import Grouping import pandas.util.testing as tm from pandas.util.testing import ( - assert_almost_equal, assert_frame_equal, assert_panel_equal, - assert_series_equal) + assert_almost_equal, assert_frame_equal, assert_series_equal) # selection # -------------------------------- @@ -563,17 +562,7 @@ def test_list_grouper_with_nat(self): # -------------------------------- class TestGetGroup(): - - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") def test_get_group(self): - wp = tm.makePanel() - grouped = wp.groupby(lambda x: x.month, axis='major') - - gp = grouped.get_group(1) - expected = wp.reindex( - major=[x for x in wp.major_axis if x.month == 1]) - assert_panel_equal(gp, expected) - # GH 5267 # be datelike friendly df = DataFrame({'DATE': pd.to_datetime( @@ -755,19 +744,6 @@ def test_multi_iter_frame(self, three_group): for key, group in grouped: pass - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") - def test_multi_iter_panel(self): - wp = tm.makePanel() - grouped = wp.groupby([lambda x: x.month, lambda x: x.weekday()], - axis=1) - - for (month, wd), group in grouped: - exp_axis = [x - for x in wp.major_axis - if x.month == month and x.weekday() == wd] - expected = wp.reindex(major=exp_axis) - assert_panel_equal(group, expected) - def test_dictify(self, df): dict(iter(df.groupby('A'))) dict(iter(df.groupby(['A', 'B']))) diff --git a/pandas/tests/io/test_pytables.py b/pandas/tests/io/test_pytables.py index 9430011288f27..c339c33751b5f 100644 --- a/pandas/tests/io/test_pytables.py +++ b/pandas/tests/io/test_pytables.py @@ -3050,29 +3050,6 @@ def test_select_with_dups(self): result = store.select('df', columns=['B', 'A']) assert_frame_equal(result, expected, by_blocks=True) - @pytest.mark.filterwarnings( - "ignore:\\nduplicate:pandas.io.pytables.DuplicateWarning" - ) - def test_wide_table_dups(self): - with ensure_clean_store(self.path) as store: - with catch_warnings(record=True): - - wp = tm.makePanel() - store.put('panel', wp, format='table') - store.put('panel', wp, format='table', append=True) - - recons = store['panel'] - - assert_panel_equal(recons, wp) - - def test_long(self): - def _check(left, right): - assert_panel_equal(left.to_panel(), right.to_panel()) - - with catch_warnings(record=True): - wp = tm.makePanel() - self._check_roundtrip(wp.to_frame(), _check) - def test_overwrite_node(self): with ensure_clean_store(self.path) as store: diff --git a/pandas/tests/resample/test_datetime_index.py b/pandas/tests/resample/test_datetime_index.py index 856c4df5380e5..ceccb48194f85 100644 --- a/pandas/tests/resample/test_datetime_index.py +++ b/pandas/tests/resample/test_datetime_index.py @@ -1,6 +1,5 @@ from datetime import datetime, timedelta from functools import partial -from warnings import catch_warnings, simplefilter import numpy as np import pytest @@ -10,7 +9,7 @@ from pandas.errors import UnsupportedFunctionCall import pandas as pd -from pandas import DataFrame, Panel, Series, Timedelta, Timestamp, isna, notna +from pandas import DataFrame, Series, Timedelta, Timestamp, isna, notna from pandas.core.indexes.datetimes import date_range from pandas.core.indexes.period import Period, period_range from pandas.core.resample import ( @@ -692,56 +691,6 @@ def test_resample_axis1(): tm.assert_frame_equal(result, expected) -def test_resample_panel(): - rng = date_range('1/1/2000', '6/30/2000') - n = len(rng) - - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - panel = Panel(np.random.randn(3, n, 5), - items=['one', 'two', 'three'], - major_axis=rng, - minor_axis=['a', 'b', 'c', 'd', 'e']) - - result = panel.resample('M', axis=1).mean() - - def p_apply(panel, f): - result = {} - for item in panel.items: - result[item] = f(panel[item]) - return Panel(result, items=panel.items) - - expected = p_apply(panel, lambda x: x.resample('M').mean()) - tm.assert_panel_equal(result, expected) - - panel2 = panel.swapaxes(1, 2) - result = panel2.resample('M', axis=2).mean() - expected = p_apply(panel2, - lambda x: x.resample('M', axis=1).mean()) - tm.assert_panel_equal(result, expected) - - -@pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") -def test_resample_panel_numpy(): - rng = date_range('1/1/2000', '6/30/2000') - n = len(rng) - - with catch_warnings(record=True): - panel = Panel(np.random.randn(3, n, 5), - items=['one', 'two', 'three'], - major_axis=rng, - minor_axis=['a', 'b', 'c', 'd', 'e']) - - result = panel.resample('M', axis=1).apply(lambda x: x.mean(1)) - expected = panel.resample('M', axis=1).mean() - tm.assert_panel_equal(result, expected) - - panel = panel.swapaxes(1, 2) - result = panel.resample('M', axis=2).apply(lambda x: x.mean(2)) - expected = panel.resample('M', axis=2).mean() - tm.assert_panel_equal(result, expected) - - def test_resample_anchored_ticks(): # If a fixed delta (5 minute, 4 hour) evenly divides a day, we should # "anchor" the origin at midnight so we get regular intervals rather diff --git a/pandas/tests/resample/test_time_grouper.py b/pandas/tests/resample/test_time_grouper.py index a4eb7933738c0..2f330d1f2484b 100644 --- a/pandas/tests/resample/test_time_grouper.py +++ b/pandas/tests/resample/test_time_grouper.py @@ -5,7 +5,7 @@ import pytest import pandas as pd -from pandas import DataFrame, Panel, Series +from pandas import DataFrame, Series from pandas.core.indexes.datetimes import date_range from pandas.core.resample import TimeGrouper import pandas.util.testing as tm @@ -79,27 +79,6 @@ def f(df): tm.assert_index_equal(result.index, df.index) -@pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") -def test_panel_aggregation(): - ind = pd.date_range('1/1/2000', periods=100) - data = np.random.randn(2, len(ind), 4) - - wp = Panel(data, items=['Item1', 'Item2'], major_axis=ind, - minor_axis=['A', 'B', 'C', 'D']) - - tg = TimeGrouper('M', axis=1) - _, grouper, _ = tg._get_grouper(wp) - bingrouped = wp.groupby(grouper) - binagg = bingrouped.mean() - - def f(x): - assert (isinstance(x, Panel)) - return x.mean(1) - - result = bingrouped.agg(f) - tm.assert_panel_equal(result, binagg) - - @pytest.mark.parametrize('name, func', [ ('Int64Index', tm.makeIntIndex), ('Index', tm.makeUnicodeIndex), diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index ba0ad72e624f7..6b20acc844829 100644 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -1653,61 +1653,6 @@ def test_transpose_copy(self): panel.values[0, 1, 1] = np.nan assert notna(result.values[1, 0, 1]) - def test_to_frame(self): - # filtered - filtered = self.panel.to_frame() - expected = self.panel.to_frame().dropna(how='any') - assert_frame_equal(filtered, expected) - - # unfiltered - unfiltered = self.panel.to_frame(filter_observations=False) - assert_panel_equal(unfiltered.to_panel(), self.panel) - - # names - assert unfiltered.index.names == ('major', 'minor') - - # unsorted, round trip - df = self.panel.to_frame(filter_observations=False) - unsorted = df.take(np.random.permutation(len(df))) - pan = unsorted.to_panel() - assert_panel_equal(pan, self.panel) - - # preserve original index names - df = DataFrame(np.random.randn(6, 2), - index=[['a', 'a', 'b', 'b', 'c', 'c'], - [0, 1, 0, 1, 0, 1]], - columns=['one', 'two']) - df.index.names = ['foo', 'bar'] - df.columns.name = 'baz' - - rdf = df.to_panel().to_frame() - assert rdf.index.names == df.index.names - assert rdf.columns.names == df.columns.names - - def test_to_frame_mixed(self): - panel = self.panel.fillna(0) - panel['str'] = 'foo' - panel['bool'] = panel['ItemA'] > 0 - - lp = panel.to_frame() - wp = lp.to_panel() - assert wp['bool'].values.dtype == np.bool_ - # Previously, this was mutating the underlying - # index and changing its name - assert_frame_equal(wp['bool'], panel['bool'], check_names=False) - - # GH 8704 - # with categorical - df = panel.to_frame() - df['category'] = df['str'].astype('category') - - # to_panel - # TODO: this converts back to object - p = df.to_panel() - expected = panel.copy() - expected['category'] = 'foo' - assert_panel_equal(p, expected) - def test_to_frame_multi_major(self): idx = MultiIndex.from_tuples( [(1, 'one'), (1, 'two'), (2, 'one'), (2, 'two')]) @@ -1808,22 +1753,6 @@ def test_to_frame_multi_drop_level(self): expected = DataFrame({'i1': [1., 2], 'i2': [1., 2]}, index=exp_idx) assert_frame_equal(result, expected) - def test_to_panel_na_handling(self): - df = DataFrame(np.random.randint(0, 10, size=20).reshape((10, 2)), - index=[[0, 0, 0, 0, 0, 0, 1, 1, 1, 1], - [0, 1, 2, 3, 4, 5, 2, 3, 4, 5]]) - - panel = df.to_panel() - assert isna(panel[0].loc[1, [0, 1]]).all() - - def test_to_panel_duplicates(self): - # #2441 - df = DataFrame({'a': [0, 0, 1], 'b': [1, 1, 1], 'c': [1, 2, 3]}) - idf = df.set_index(['a', 'b']) - - with pytest.raises(ValueError, match='non-uniquely indexed'): - idf.to_panel() - def test_panel_dups(self): # GH 4960 @@ -2121,14 +2050,6 @@ def test_get_attr(self): self.panel['i'] = self.panel['ItemA'] assert_frame_equal(self.panel['i'], self.panel.i) - def test_from_frame_level1_unsorted(self): - tuples = [('MSFT', 3), ('MSFT', 2), ('AAPL', 2), ('AAPL', 1), - ('MSFT', 1)] - midx = MultiIndex.from_tuples(tuples) - df = DataFrame(np.random.rand(5, 4), index=midx) - p = df.to_panel() - assert_frame_equal(p.minor_xs(2), df.xs(2, level=1).sort_index()) - def test_to_excel(self): try: import xlwt # noqa @@ -2404,40 +2325,11 @@ def setup_method(self, method): self.panel = panel.to_frame() self.unfiltered_panel = panel.to_frame(filter_observations=False) - def test_ops_differently_indexed(self): - # trying to set non-identically indexed panel - wp = self.panel.to_panel() - wp2 = wp.reindex(major=wp.major_axis[:-1]) - lp2 = wp2.to_frame() - - result = self.panel + lp2 - assert_frame_equal(result.reindex(lp2.index), lp2 * 2) - - # careful, mutation - self.panel['foo'] = lp2['ItemA'] - assert_series_equal(self.panel['foo'].reindex(lp2.index), - lp2['ItemA'], - check_names=False) - def test_ops_scalar(self): result = self.panel.mul(2) expected = DataFrame.__mul__(self.panel, 2) assert_frame_equal(result, expected) - def test_combineFrame(self): - wp = self.panel.to_panel() - result = self.panel.add(wp['ItemA'].stack(), axis=0) - assert_frame_equal(result.to_panel()['ItemA'], wp['ItemA'] * 2) - - def test_combinePanel(self): - wp = self.panel.to_panel() - result = self.panel.add(self.panel) - wide_result = result.to_panel() - assert_frame_equal(wp['ItemA'] * 2, wide_result['ItemA']) - - # one item - result = self.panel.add(self.panel.filter(['ItemA'])) - def test_combine_scalar(self): result = self.panel.mul(2) expected = DataFrame(self.panel._data) * 2 @@ -2454,34 +2346,6 @@ def test_combine_series(self): expected = DataFrame.add(self.panel, s, axis=1) assert_frame_equal(result, expected) - def test_operators(self): - wp = self.panel.to_panel() - result = (self.panel + 1).to_panel() - assert_frame_equal(wp['ItemA'] + 1, result['ItemA']) - - def test_arith_flex_panel(self): - ops = ['add', 'sub', 'mul', 'div', - 'truediv', 'pow', 'floordiv', 'mod'] - if not compat.PY3: - aliases = {} - else: - aliases = {'div': 'truediv'} - self.panel = self.panel.to_panel() - - for n in [np.random.randint(-50, -1), np.random.randint(1, 50), 0]: - for op in ops: - alias = aliases.get(op, op) - f = getattr(operator, alias) - exp = f(self.panel, n) - result = getattr(self.panel, op)(n) - assert_panel_equal(result, exp, check_panel_type=True) - - # rops - r_f = lambda x, y: f(y, x) - exp = r_f(self.panel, n) - result = getattr(self.panel, 'r' + op)(n) - assert_panel_equal(result, exp) - def test_sort(self): def is_sorted(arr): return (arr[1:] > arr[:-1]).any() @@ -2502,45 +2366,6 @@ def test_to_sparse(self): with pytest.raises(NotImplementedError, match=msg): self.panel.to_sparse - def test_truncate(self): - dates = self.panel.index.levels[0] - start, end = dates[1], dates[5] - - trunced = self.panel.truncate(start, end).to_panel() - expected = self.panel.to_panel()['ItemA'].truncate(start, end) - - # TODO truncate drops index.names - assert_frame_equal(trunced['ItemA'], expected, check_names=False) - - trunced = self.panel.truncate(before=start).to_panel() - expected = self.panel.to_panel()['ItemA'].truncate(before=start) - - # TODO truncate drops index.names - assert_frame_equal(trunced['ItemA'], expected, check_names=False) - - trunced = self.panel.truncate(after=end).to_panel() - expected = self.panel.to_panel()['ItemA'].truncate(after=end) - - # TODO truncate drops index.names - assert_frame_equal(trunced['ItemA'], expected, check_names=False) - - # truncate on dates that aren't in there - wp = self.panel.to_panel() - new_index = wp.major_axis[::5] - - wp2 = wp.reindex(major=new_index) - - lp2 = wp2.to_frame() - lp_trunc = lp2.truncate(wp.major_axis[2], wp.major_axis[-2]) - - wp_trunc = wp2.truncate(wp.major_axis[2], wp.major_axis[-2]) - - assert_panel_equal(wp_trunc, lp_trunc.to_panel()) - - # throw proper exception - pytest.raises(Exception, lp2.truncate, wp.major_axis[-2], - wp.major_axis[2]) - def test_axis_dummies(self): from pandas.core.reshape.reshape import make_axis_dummies @@ -2567,20 +2392,6 @@ def test_get_dummies(self): dummies = get_dummies(self.panel['Label']) tm.assert_numpy_array_equal(dummies.values, minor_dummies.values) - def test_mean(self): - means = self.panel.mean(level='minor') - - # test versus Panel version - wide_means = self.panel.to_panel().mean('major') - assert_frame_equal(means, wide_means) - - def test_sum(self): - sums = self.panel.sum(level='minor') - - # test versus Panel version - wide_sums = self.panel.to_panel().sum('major') - assert_frame_equal(sums, wide_sums) - def test_count(self): index = self.panel.index From 09633b868f2f999599e29d32a326e112fdbbf3ec Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Wed, 6 Feb 2019 04:51:06 +0100 Subject: [PATCH 069/215] BUG: DataFrame.merge(suffixes=) does not respect None (#24819) --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/core/internals/managers.py | 26 +++++++--- pandas/core/reshape/merge.py | 12 +++-- pandas/tests/reshape/merge/test_merge.py | 62 ++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 10 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index a3fb1c575e7f1..cbefae07b07f1 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -181,6 +181,7 @@ Groupby/Resample/Rolling Reshaping ^^^^^^^^^ +- Bug in :func:`pandas.merge` adds a string of ``None`` if ``None`` is assigned in suffixes instead of remain the column name as-is (:issue:`24782`). - Bug in :func:`merge` when merging by index name would sometimes result in an incorrectly numbered index (:issue:`24212`) - :func:`to_records` now accepts dtypes to its `column_dtypes` parameter (:issue:`24895`) - diff --git a/pandas/core/internals/managers.py b/pandas/core/internals/managers.py index 050c3d3e87fc6..5cae6e1a89170 100644 --- a/pandas/core/internals/managers.py +++ b/pandas/core/internals/managers.py @@ -1971,16 +1971,28 @@ def items_overlap_with_suffix(left, lsuffix, right, rsuffix): raise ValueError('columns overlap but no suffix specified: ' '{rename}'.format(rename=to_rename)) - def lrenamer(x): - if x in to_rename: - return '{x}{lsuffix}'.format(x=x, lsuffix=lsuffix) - return x + def renamer(x, suffix): + """Rename the left and right indices. + + If there is overlap, and suffix is not None, add + suffix, otherwise, leave it as-is. + + Parameters + ---------- + x : original column name + suffix : str or None - def rrenamer(x): - if x in to_rename: - return '{x}{rsuffix}'.format(x=x, rsuffix=rsuffix) + Returns + ------- + x : renamed column name + """ + if x in to_rename and suffix is not None: + return '{x}{suffix}'.format(x=x, suffix=suffix) return x + lrenamer = partial(renamer, suffix=lsuffix) + rrenamer = partial(renamer, suffix=rsuffix) + return (_transform_index(left, lrenamer), _transform_index(right, rrenamer)) diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py index 1dd19a7c1514e..ad3327e694b67 100644 --- a/pandas/core/reshape/merge.py +++ b/pandas/core/reshape/merge.py @@ -159,9 +159,15 @@ def merge_ordered(left, right, on=None, left DataFrame fill_method : {'ffill', None}, default None Interpolation method for data - suffixes : 2-length sequence (tuple, list, ...) - Suffix to apply to overlapping column names in the left and right - side, respectively + suffixes : Sequence, default is ("_x", "_y") + A length-2 sequence where each element is optionally a string + indicating the suffix to add to overlapping column names in + `left` and `right` respectively. Pass a value of `None` instead + of a string to indicate that the column name from `left` or + `right` should be left as-is, with no suffix. At least one of the + values must not be None. + + .. versionchanged:: 0.25.0 how : {'left', 'right', 'outer', 'inner'}, default 'outer' * left: use only keys from left frame (SQL: left outer join) * right: use only keys from right frame (SQL: right outer join) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index a0a20d1da6cef..25487ccc76e62 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -1526,3 +1526,65 @@ def test_merge_series(on, left_on, right_on, left_index, right_index, nm): with pytest.raises(ValueError, match=msg): result = pd.merge(a, b, on=on, left_on=left_on, right_on=right_on, left_index=left_index, right_index=right_index) + + +@pytest.mark.parametrize("col1, col2, kwargs, expected_cols", [ + (0, 0, dict(suffixes=("", "_dup")), ["0", "0_dup"]), + (0, 0, dict(suffixes=(None, "_dup")), [0, "0_dup"]), + (0, 0, dict(suffixes=("_x", "_y")), ["0_x", "0_y"]), + ("a", 0, dict(suffixes=(None, "_y")), ["a", 0]), + (0.0, 0.0, dict(suffixes=("_x", None)), ["0.0_x", 0.0]), + ("b", "b", dict(suffixes=(None, "_y")), ["b", "b_y"]), + ("a", "a", dict(suffixes=("_x", None)), ["a_x", "a"]), + ("a", "b", dict(suffixes=("_x", None)), ["a", "b"]), + ("a", "a", dict(suffixes=[None, "_x"]), ["a", "a_x"]), + (0, 0, dict(suffixes=["_a", None]), ["0_a", 0]), + ("a", "a", dict(), ["a_x", "a_y"]), + (0, 0, dict(), ["0_x", "0_y"]) +]) +def test_merge_suffix(col1, col2, kwargs, expected_cols): + # issue: 24782 + a = pd.DataFrame({col1: [1, 2, 3]}) + b = pd.DataFrame({col2: [4, 5, 6]}) + + expected = pd.DataFrame([[1, 4], [2, 5], [3, 6]], + columns=expected_cols) + + result = a.merge(b, left_index=True, right_index=True, **kwargs) + tm.assert_frame_equal(result, expected) + + result = pd.merge(a, b, left_index=True, right_index=True, **kwargs) + tm.assert_frame_equal(result, expected) + + +@pytest.mark.parametrize("col1, col2, suffixes", [ + ("a", "a", [None, None]), + ("a", "a", (None, None)), + ("a", "a", ("", None)), + (0, 0, [None, None]), + (0, 0, (None, "")) +]) +def test_merge_suffix_error(col1, col2, suffixes): + # issue: 24782 + a = pd.DataFrame({col1: [1, 2, 3]}) + b = pd.DataFrame({col2: [3, 4, 5]}) + + # TODO: might reconsider current raise behaviour, see issue 24782 + msg = "columns overlap but no suffix specified" + with pytest.raises(ValueError, match=msg): + pd.merge(a, b, left_index=True, right_index=True, suffixes=suffixes) + + +@pytest.mark.parametrize("col1, col2, suffixes", [ + ("a", "a", None), + (0, 0, None) +]) +def test_merge_suffix_none_error(col1, col2, suffixes): + # issue: 24782 + a = pd.DataFrame({col1: [1, 2, 3]}) + b = pd.DataFrame({col2: [3, 4, 5]}) + + # TODO: might reconsider current raise behaviour, see GH24782 + msg = "iterable" + with pytest.raises(TypeError, match=msg): + pd.merge(a, b, left_index=True, right_index=True, suffixes=suffixes) From 93568cc3c529a10a897879561b4cd75486c618be Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 6 Feb 2019 16:07:06 +0000 Subject: [PATCH 070/215] fix MacPython pandas-wheels failure (#25186) --- pandas/tests/indexes/multi/test_analytics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/tests/indexes/multi/test_analytics.py b/pandas/tests/indexes/multi/test_analytics.py index beec8e544f94b..27a5ba9e5434a 100644 --- a/pandas/tests/indexes/multi/test_analytics.py +++ b/pandas/tests/indexes/multi/test_analytics.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pandas.compat import lrange +from pandas.compat import PY2, lrange from pandas.compat.numpy import _np_version_under1p16 import pandas as pd @@ -275,6 +275,7 @@ def test_map_dictlike(idx, mapper): tm.assert_index_equal(result, expected) +@pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") @pytest.mark.parametrize('func', [ np.exp, np.exp2, np.expm1, np.log, np.log2, np.log10, np.log1p, np.sqrt, np.sin, np.cos, np.tan, np.arcsin, From 638ddebaa7da1e569836a5b89593b120dbbf491c Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 6 Feb 2019 10:44:38 -0800 Subject: [PATCH 071/215] modernize compat imports (#25192) --- pandas/compat/__init__.py | 11 ----------- pandas/compat/numpy/function.py | 2 +- pandas/core/base.py | 11 ++++++----- pandas/core/common.py | 3 ++- pandas/core/computation/ops.py | 11 ++++++----- pandas/core/computation/pytables.py | 5 +++-- pandas/core/computation/scope.py | 4 ++-- pandas/core/frame.py | 3 ++- pandas/core/groupby/groupby.py | 2 +- pandas/core/groupby/grouper.py | 2 +- pandas/core/panel.py | 3 ++- pandas/core/resample.py | 2 +- pandas/core/series.py | 3 ++- pandas/core/sparse/scipy_sparse.py | 4 +++- pandas/io/excel.py | 5 +++-- pandas/io/formats/html.py | 3 ++- pandas/io/packers.py | 2 +- pandas/tests/frame/test_constructors.py | 4 ++-- pandas/tests/frame/test_dtypes.py | 7 ++++--- pandas/tests/groupby/aggregate/test_aggregate.py | 3 +-- pandas/tests/groupby/test_groupby.py | 5 ++--- pandas/tests/internals/test_internals.py | 4 ++-- pandas/tests/io/json/test_pandas.py | 4 ++-- pandas/tests/io/msgpack/test_pack.py | 4 ++-- pandas/tests/resample/test_resample_api.py | 3 ++- pandas/tests/test_panel.py | 4 ++-- pandas/util/_decorators.py | 2 +- pandas/util/testing.py | 5 +++-- 28 files changed, 61 insertions(+), 60 deletions(-) diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index f9c659106a516..d7ca7f8963f70 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -9,7 +9,6 @@ * lists: lrange(), lmap(), lzip(), lfilter() * unicode: u() [no unicode builtin in Python 3] * longs: long (int in Python 3) -* callable * iterable method compatibility: iteritems, iterkeys, itervalues * Uses the original method if available, otherwise uses items, keys, values. * types: @@ -378,14 +377,6 @@ class ResourceWarning(Warning): string_and_binary_types = string_types + (binary_type,) -try: - # callable reintroduced in later versions of Python - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - if PY2: # In PY2 functools.wraps doesn't provide metadata pytest needs to generate # decorated tests using parametrization. See pytest GH issue #2782 @@ -411,8 +402,6 @@ def wrapper(cls): return metaclass(cls.__name__, cls.__bases__, orig_vars) return wrapper -from collections import OrderedDict, Counter - if PY3: def raise_with_traceback(exc, traceback=Ellipsis): if traceback == Ellipsis: diff --git a/pandas/compat/numpy/function.py b/pandas/compat/numpy/function.py index 417ddd0d8af17..f15783ad642b4 100644 --- a/pandas/compat/numpy/function.py +++ b/pandas/compat/numpy/function.py @@ -17,10 +17,10 @@ and methods that are spread throughout the codebase. This module will make it easier to adjust to future upstream changes in the analogous numpy signatures. """ +from collections import OrderedDict from numpy import ndarray -from pandas.compat import OrderedDict from pandas.errors import UnsupportedFunctionCall from pandas.util._validators import ( validate_args, validate_args_and_kwargs, validate_kwargs) diff --git a/pandas/core/base.py b/pandas/core/base.py index 24695dd4cfd9c..726266b39cdee 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -1,6 +1,7 @@ """ Base and utility classes for pandas objects. """ +from collections import OrderedDict import textwrap import warnings @@ -8,7 +9,7 @@ import pandas._libs.lib as lib import pandas.compat as compat -from pandas.compat import PYPY, OrderedDict, builtins, map, range +from pandas.compat import PYPY, builtins, map, range from pandas.compat.numpy import function as nv from pandas.errors import AbstractMethodError from pandas.util._decorators import Appender, Substitution, cache_readonly @@ -376,7 +377,7 @@ def nested_renaming_depr(level=4): # eg. {'A' : ['mean']}, normalize all to # be list-likes if any(is_aggregator(x) for x in compat.itervalues(arg)): - new_arg = compat.OrderedDict() + new_arg = OrderedDict() for k, v in compat.iteritems(arg): if not isinstance(v, (tuple, list, dict)): new_arg[k] = [v] @@ -444,14 +445,14 @@ def _agg(arg, func): run the aggregations over the arg with func return an OrderedDict """ - result = compat.OrderedDict() + result = OrderedDict() for fname, agg_how in compat.iteritems(arg): result[fname] = func(fname, agg_how) return result # set the final keys keys = list(compat.iterkeys(arg)) - result = compat.OrderedDict() + result = OrderedDict() # nested renamer if is_nested_renamer: @@ -459,7 +460,7 @@ def _agg(arg, func): if all(isinstance(r, dict) for r in result): - result, results = compat.OrderedDict(), result + result, results = OrderedDict(), result for r in results: result.update(r) keys = list(compat.iterkeys(result)) diff --git a/pandas/core/common.py b/pandas/core/common.py index 0e92a5056daae..5b83cb344b1e7 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -5,6 +5,7 @@ """ import collections +from collections import OrderedDict from datetime import datetime, timedelta from functools import partial import inspect @@ -13,7 +14,7 @@ from pandas._libs import lib, tslibs import pandas.compat as compat -from pandas.compat import PY36, OrderedDict, iteritems +from pandas.compat import PY36, iteritems from pandas.core.dtypes.cast import construct_1d_object_array_from_listlike from pandas.core.dtypes.common import ( diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 8c3218a976b6b..5c70255982e54 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -8,11 +8,11 @@ import numpy as np +from pandas._libs.tslibs import Timestamp from pandas.compat import PY3, string_types, text_type from pandas.core.dtypes.common import is_list_like, is_scalar -import pandas as pd from pandas.core.base import StringMixin import pandas.core.common as com from pandas.core.computation.common import _ensure_decoded, _result_type_many @@ -399,8 +399,9 @@ def evaluate(self, env, engine, parser, term_type, eval_in_python): if self.op in eval_in_python: res = self.func(left.value, right.value) else: - res = pd.eval(self, local_dict=env, engine=engine, - parser=parser) + from pandas.core.computation.eval import eval + res = eval(self, local_dict=env, engine=engine, + parser=parser) name = env.add_tmp(res) return term_type(name, env=env) @@ -422,7 +423,7 @@ def stringify(value): v = rhs.value if isinstance(v, (int, float)): v = stringify(v) - v = pd.Timestamp(_ensure_decoded(v)) + v = Timestamp(_ensure_decoded(v)) if v.tz is not None: v = v.tz_convert('UTC') self.rhs.update(v) @@ -431,7 +432,7 @@ def stringify(value): v = lhs.value if isinstance(v, (int, float)): v = stringify(v) - v = pd.Timestamp(_ensure_decoded(v)) + v = Timestamp(_ensure_decoded(v)) if v.tz is not None: v = v.tz_convert('UTC') self.lhs.update(v) diff --git a/pandas/core/computation/pytables.py b/pandas/core/computation/pytables.py index 00de29b07c75d..678c1e678fe19 100644 --- a/pandas/core/computation/pytables.py +++ b/pandas/core/computation/pytables.py @@ -5,6 +5,7 @@ import numpy as np +from pandas._libs.tslibs import Timedelta, Timestamp from pandas.compat import DeepChainMap, string_types, u from pandas.core.dtypes.common import is_list_like @@ -185,12 +186,12 @@ def stringify(value): if isinstance(v, (int, float)): v = stringify(v) v = _ensure_decoded(v) - v = pd.Timestamp(v) + v = Timestamp(v) if v.tz is not None: v = v.tz_convert('UTC') return TermValue(v, v.value, kind) elif kind == u('timedelta64') or kind == u('timedelta'): - v = pd.Timedelta(v, unit='s').value + v = Timedelta(v, unit='s').value return TermValue(int(v), v, kind) elif meta == u('category'): metadata = com.values_from_object(self.metadata) diff --git a/pandas/core/computation/scope.py b/pandas/core/computation/scope.py index 33c5a1c2e0f0a..e158bc8c568eb 100644 --- a/pandas/core/computation/scope.py +++ b/pandas/core/computation/scope.py @@ -11,9 +11,9 @@ import numpy as np +from pandas._libs.tslibs import Timestamp from pandas.compat import DeepChainMap, StringIO, map -import pandas as pd # noqa from pandas.core.base import StringMixin import pandas.core.computation as compu @@ -48,7 +48,7 @@ def _raw_hex_id(obj): _DEFAULT_GLOBALS = { - 'Timestamp': pd._libs.tslib.Timestamp, + 'Timestamp': Timestamp, 'datetime': datetime.datetime, 'True': True, 'False': False, diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 0c160d1a2bb54..9c2f000c26c79 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -13,6 +13,7 @@ from __future__ import division import collections +from collections import OrderedDict import functools import itertools import sys @@ -33,7 +34,7 @@ from pandas import compat from pandas.compat import (range, map, zip, lmap, lzip, StringIO, u, - OrderedDict, PY36, raise_with_traceback, + PY36, raise_with_traceback, string_and_binary_types) from pandas.compat.numpy import function as nv from pandas.core.dtypes.cast import ( diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index bee806df824df..bfb5ba4fc8c90 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -18,7 +18,7 @@ class providing the base-class of operations. from pandas._libs import Timestamp, groupby as libgroupby import pandas.compat as compat -from pandas.compat import callable, range, set_function_name, zip +from pandas.compat import range, set_function_name, zip from pandas.compat.numpy import function as nv from pandas.errors import AbstractMethodError from pandas.util._decorators import Appender, Substitution, cache_readonly diff --git a/pandas/core/groupby/grouper.py b/pandas/core/groupby/grouper.py index 260417bc0d598..b0d7cf9d431cc 100644 --- a/pandas/core/groupby/grouper.py +++ b/pandas/core/groupby/grouper.py @@ -8,7 +8,7 @@ import numpy as np import pandas.compat as compat -from pandas.compat import callable, zip +from pandas.compat import zip from pandas.util._decorators import cache_readonly from pandas.core.dtypes.common import ( diff --git a/pandas/core/panel.py b/pandas/core/panel.py index de535eeea4b5e..16bcc17a6b4ea 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -4,12 +4,13 @@ # pylint: disable=E1103,W0231,W0212,W0621 from __future__ import division +from collections import OrderedDict import warnings import numpy as np import pandas.compat as compat -from pandas.compat import OrderedDict, map, range, u, zip +from pandas.compat import map, range, u, zip from pandas.compat.numpy import function as nv from pandas.util._decorators import Appender, Substitution, deprecate_kwarg from pandas.util._validators import validate_axis_style_args diff --git a/pandas/core/resample.py b/pandas/core/resample.py index fbddc9ff29ce9..42ca488572e36 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -343,7 +343,7 @@ def _groupby_and_aggregate(self, how, grouper=None, *args, **kwargs): grouped = groupby(obj, by=None, grouper=grouper, axis=self.axis) try: - if isinstance(obj, ABCDataFrame) and compat.callable(how): + if isinstance(obj, ABCDataFrame) and callable(how): # Check if the function is reducing or not. result = grouped._aggregate_item_by_item(how, *args, **kwargs) else: diff --git a/pandas/core/series.py b/pandas/core/series.py index fb84a36d215e5..ae6da1d5f015b 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3,6 +3,7 @@ """ from __future__ import division +from collections import OrderedDict from textwrap import dedent import warnings @@ -10,7 +11,7 @@ from pandas._libs import iNaT, index as libindex, lib, tslibs import pandas.compat as compat -from pandas.compat import PY36, OrderedDict, StringIO, u, zip +from pandas.compat import PY36, StringIO, u, zip from pandas.compat.numpy import function as nv from pandas.util._decorators import Appender, Substitution, deprecate from pandas.util._validators import validate_bool_kwarg diff --git a/pandas/core/sparse/scipy_sparse.py b/pandas/core/sparse/scipy_sparse.py index 8bb79d753e5f9..5a39a1529a33a 100644 --- a/pandas/core/sparse/scipy_sparse.py +++ b/pandas/core/sparse/scipy_sparse.py @@ -3,7 +3,9 @@ Currently only includes SparseSeries.to_coo helpers. """ -from pandas.compat import OrderedDict, lmap +from collections import OrderedDict + +from pandas.compat import lmap from pandas.core.index import Index, MultiIndex from pandas.core.series import Series diff --git a/pandas/io/excel.py b/pandas/io/excel.py index 3d85ae7fd1f46..11e5e78fa3e80 100644 --- a/pandas/io/excel.py +++ b/pandas/io/excel.py @@ -5,6 +5,7 @@ # --------------------------------------------------------------------- # ExcelFile class import abc +from collections import OrderedDict from datetime import date, datetime, time, timedelta from distutils.version import LooseVersion from io import UnsupportedOperation @@ -17,7 +18,7 @@ import pandas._libs.json as json import pandas.compat as compat from pandas.compat import ( - OrderedDict, add_metaclass, lrange, map, range, string_types, u, zip) + add_metaclass, lrange, map, range, string_types, u, zip) from pandas.errors import EmptyDataError from pandas.util._decorators import Appender, deprecate_kwarg @@ -274,7 +275,7 @@ def register_writer(klass): """Adds engine to the excel writer registry. You must use this method to integrate with ``to_excel``. Also adds config options for any new ``supported_extensions`` defined on the writer.""" - if not compat.callable(klass): + if not callable(klass): raise ValueError("Can only register callables as engines") engine_name = klass.engine _writers[engine_name] = klass diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 2d8b40016c9af..456583509565e 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -5,9 +5,10 @@ from __future__ import print_function +from collections import OrderedDict from textwrap import dedent -from pandas.compat import OrderedDict, lzip, map, range, u, unichr, zip +from pandas.compat import lzip, map, range, u, unichr, zip from pandas.core.dtypes.generic import ABCMultiIndex diff --git a/pandas/io/packers.py b/pandas/io/packers.py index efe4e3a91c69c..588d63d73515f 100644 --- a/pandas/io/packers.py +++ b/pandas/io/packers.py @@ -219,7 +219,7 @@ def read(fh): finally: if fh is not None: fh.close() - elif hasattr(path_or_buf, 'read') and compat.callable(path_or_buf.read): + elif hasattr(path_or_buf, 'read') and callable(path_or_buf.read): # treat as a buffer like return read(path_or_buf) diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index b97f5e0b6edf9..a8a78b26e317c 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -2,6 +2,7 @@ from __future__ import print_function +from collections import OrderedDict from datetime import datetime, timedelta import functools import itertools @@ -11,8 +12,7 @@ import pytest from pandas.compat import ( - PY3, PY36, OrderedDict, is_platform_little_endian, lmap, long, lrange, - lzip, range, zip) + PY3, PY36, is_platform_little_endian, lmap, long, lrange, lzip, range, zip) from pandas.core.dtypes.cast import construct_1d_object_array_from_listlike from pandas.core.dtypes.common import is_integer_dtype diff --git a/pandas/tests/frame/test_dtypes.py b/pandas/tests/frame/test_dtypes.py index a9f8ab47b16de..a8776c84b98ca 100644 --- a/pandas/tests/frame/test_dtypes.py +++ b/pandas/tests/frame/test_dtypes.py @@ -2,6 +2,7 @@ from __future__ import print_function +from collections import OrderedDict from datetime import timedelta import numpy as np @@ -66,7 +67,7 @@ def test_empty_frame_dtypes_ftypes(self): assert_series_equal(norows_int_df.ftypes, pd.Series( 'int32:dense', index=list("abc"))) - odict = compat.OrderedDict + odict = OrderedDict df = pd.DataFrame(odict([('a', 1), ('b', True), ('c', 1.0)]), index=[1, 2, 3]) ex_dtypes = pd.Series(odict([('a', np.int64), @@ -100,7 +101,7 @@ def test_datetime_with_tz_dtypes(self): def test_dtypes_are_correct_after_column_slice(self): # GH6525 df = pd.DataFrame(index=range(5), columns=list("abc"), dtype=np.float_) - odict = compat.OrderedDict + odict = OrderedDict assert_series_equal(df.dtypes, pd.Series(odict([('a', np.float_), ('b', np.float_), @@ -295,7 +296,7 @@ def test_select_dtypes_include_exclude_mixed_scalars_lists(self): def test_select_dtypes_duplicate_columns(self): # GH20839 - odict = compat.OrderedDict + odict = OrderedDict df = DataFrame(odict([('a', list('abc')), ('b', list(range(1, 4))), ('c', np.arange(3, 6).astype('u1')), diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py index 62ec0555f9033..9de8a08809009 100644 --- a/pandas/tests/groupby/aggregate/test_aggregate.py +++ b/pandas/tests/groupby/aggregate/test_aggregate.py @@ -3,12 +3,11 @@ """ test .agg behavior / note that .apply is tested generally in test_groupby.py """ +from collections import OrderedDict import numpy as np import pytest -from pandas.compat import OrderedDict - import pandas as pd from pandas import DataFrame, Index, MultiIndex, Series, concat from pandas.core.base import SpecificationError diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py index 0bfc7ababd18a..f14c9dcdd8e42 100644 --- a/pandas/tests/groupby/test_groupby.py +++ b/pandas/tests/groupby/test_groupby.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- from __future__ import print_function -from collections import defaultdict +from collections import OrderedDict, defaultdict from datetime import datetime from decimal import Decimal import numpy as np import pytest -from pandas.compat import ( - OrderedDict, StringIO, lmap, lrange, lzip, map, range, zip) +from pandas.compat import StringIO, lmap, lrange, lzip, map, range, zip from pandas.errors import PerformanceWarning import pandas as pd diff --git a/pandas/tests/internals/test_internals.py b/pandas/tests/internals/test_internals.py index fe0706efdc4f8..bda486411e01e 100644 --- a/pandas/tests/internals/test_internals.py +++ b/pandas/tests/internals/test_internals.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # pylint: disable=W0102 - +from collections import OrderedDict from datetime import date, datetime from distutils.version import LooseVersion import itertools @@ -12,7 +12,7 @@ import pytest from pandas._libs.internals import BlockPlacement -from pandas.compat import OrderedDict, lrange, u, zip +from pandas.compat import lrange, u, zip import pandas as pd from pandas import ( diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py index 23c40276072d6..c5fcb9fb0f672 100644 --- a/pandas/tests/io/json/test_pandas.py +++ b/pandas/tests/io/json/test_pandas.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # pylint: disable-msg=W0612,E1101 +from collections import OrderedDict from datetime import timedelta import json import os @@ -7,8 +8,7 @@ import numpy as np import pytest -from pandas.compat import ( - OrderedDict, StringIO, is_platform_32bit, lrange, range) +from pandas.compat import StringIO, is_platform_32bit, lrange, range import pandas.util._test_decorators as td import pandas as pd diff --git a/pandas/tests/io/msgpack/test_pack.py b/pandas/tests/io/msgpack/test_pack.py index 8c82d0d2cf870..078d9f4ceb649 100644 --- a/pandas/tests/io/msgpack/test_pack.py +++ b/pandas/tests/io/msgpack/test_pack.py @@ -1,10 +1,10 @@ # coding: utf-8 - +from collections import OrderedDict import struct import pytest -from pandas.compat import OrderedDict, u +from pandas.compat import u from pandas import compat diff --git a/pandas/tests/resample/test_resample_api.py b/pandas/tests/resample/test_resample_api.py index a694942cc4c40..69acf4ba6bde8 100644 --- a/pandas/tests/resample/test_resample_api.py +++ b/pandas/tests/resample/test_resample_api.py @@ -1,11 +1,12 @@ # pylint: disable=E1101 +from collections import OrderedDict from datetime import datetime import numpy as np import pytest -from pandas.compat import OrderedDict, range +from pandas.compat import range import pandas as pd from pandas import DataFrame, Series diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index 6b20acc844829..1fd791fc81009 100644 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # pylint: disable=W0612,E1101 - +from collections import OrderedDict from datetime import datetime import operator from warnings import catch_warnings, simplefilter @@ -8,7 +8,7 @@ import numpy as np import pytest -from pandas.compat import OrderedDict, StringIO, lrange, range, signature +from pandas.compat import StringIO, lrange, range, signature import pandas.util._test_decorators as td from pandas.core.dtypes.common import is_float_dtype diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py index 2f7816e3a6d00..e92051ebbea9a 100644 --- a/pandas/util/_decorators.py +++ b/pandas/util/_decorators.py @@ -4,7 +4,7 @@ import warnings from pandas._libs.properties import cache_readonly # noqa -from pandas.compat import PY2, callable, signature +from pandas.compat import PY2, signature def deprecate(name, alternative, version, alt_name=None, diff --git a/pandas/util/testing.py b/pandas/util/testing.py index f441dd20f3982..47bde267156ed 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -1,5 +1,6 @@ from __future__ import division +from collections import Counter from contextlib import contextmanager from datetime import datetime from functools import wraps @@ -20,8 +21,8 @@ from pandas._libs import testing as _testing import pandas.compat as compat from pandas.compat import ( - PY2, PY3, Counter, callable, filter, httplib, lmap, lrange, lzip, map, - raise_with_traceback, range, string_types, u, unichr, zip) + PY2, PY3, filter, httplib, lmap, lrange, lzip, map, raise_with_traceback, + range, string_types, u, unichr, zip) from pandas.core.dtypes.common import ( is_bool, is_categorical_dtype, is_datetime64_dtype, is_datetime64tz_dtype, From 51fca4cc93bf3fcd529284b2c2e2ae88a44a5f40 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Thu, 7 Feb 2019 01:23:52 +0000 Subject: [PATCH 072/215] TST: follow-up to Test nested pandas array #24993 (#25155) * revert changes to tests in gh-24993 * Test nested PandasArray * isort test_numpy.py * change NP_VERSION_INFO * use LooseVersion * add _np_version_under1p16 * remove blank line from merge master * add doctstrings to fixtures --- pandas/tests/extension/base/groupby.py | 17 +- pandas/tests/extension/base/methods.py | 6 - pandas/tests/extension/base/missing.py | 8 +- pandas/tests/extension/base/setitem.py | 1 - pandas/tests/extension/conftest.py | 57 +++ pandas/tests/extension/numpy_/__init__.py | 0 pandas/tests/extension/numpy_/conftest.py | 38 -- pandas/tests/extension/numpy_/test_numpy.py | 182 -------- .../extension/numpy_/test_numpy_nested.py | 286 ------------ pandas/tests/extension/test_numpy.py | 430 ++++++++++++++++++ pandas/tests/extension/test_sparse.py | 3 +- 11 files changed, 497 insertions(+), 531 deletions(-) delete mode 100644 pandas/tests/extension/numpy_/__init__.py delete mode 100644 pandas/tests/extension/numpy_/conftest.py delete mode 100644 pandas/tests/extension/numpy_/test_numpy.py delete mode 100644 pandas/tests/extension/numpy_/test_numpy_nested.py create mode 100644 pandas/tests/extension/test_numpy.py diff --git a/pandas/tests/extension/base/groupby.py b/pandas/tests/extension/base/groupby.py index dd406ca0cd5ed..1929dad075695 100644 --- a/pandas/tests/extension/base/groupby.py +++ b/pandas/tests/extension/base/groupby.py @@ -55,19 +55,14 @@ def test_groupby_extension_transform(self, data_for_grouping): self.assert_series_equal(result, expected) - @pytest.mark.parametrize('op', [ - lambda x: 1, - lambda x: [1] * len(x), - lambda x: pd.Series([1] * len(x)), - lambda x: x, - ], ids=['scalar', 'list', 'series', 'object']) - def test_groupby_extension_apply(self, data_for_grouping, op): + def test_groupby_extension_apply( + self, data_for_grouping, groupby_apply_op): df = pd.DataFrame({"A": [1, 1, 2, 2, 3, 3, 1, 4], "B": data_for_grouping}) - df.groupby("B").apply(op) - df.groupby("B").A.apply(op) - df.groupby("A").apply(op) - df.groupby("A").B.apply(op) + df.groupby("B").apply(groupby_apply_op) + df.groupby("B").A.apply(groupby_apply_op) + df.groupby("A").apply(groupby_apply_op) + df.groupby("A").B.apply(groupby_apply_op) def test_in_numeric_groupby(self, data_for_grouping): df = pd.DataFrame({"A": [1, 1, 2, 2, 3, 3, 1, 4], diff --git a/pandas/tests/extension/base/methods.py b/pandas/tests/extension/base/methods.py index f64df7a84b7c0..1852edaa9e748 100644 --- a/pandas/tests/extension/base/methods.py +++ b/pandas/tests/extension/base/methods.py @@ -240,7 +240,6 @@ def test_shift_fill_value(self, data): expected = data.take([2, 3, 0, 0]) self.assert_extension_array_equal(result, expected) - @pytest.mark.parametrize("as_frame", [True, False]) def test_hash_pandas_object_works(self, data, as_frame): # https://github.com/pandas-dev/pandas/issues/23066 data = pd.Series(data) @@ -250,7 +249,6 @@ def test_hash_pandas_object_works(self, data, as_frame): b = pd.util.hash_pandas_object(data) self.assert_equal(a, b) - @pytest.mark.parametrize("as_series", [True, False]) def test_searchsorted(self, data_for_sorting, as_series): b, c, a = data_for_sorting arr = type(data_for_sorting)._from_sequence([a, b, c]) @@ -275,7 +273,6 @@ def test_searchsorted(self, data_for_sorting, as_series): sorter = np.array([1, 2, 0]) assert data_for_sorting.searchsorted(a, sorter=sorter) == 0 - @pytest.mark.parametrize("as_frame", [True, False]) def test_where_series(self, data, na_value, as_frame): assert data[0] != data[1] cls = type(data) @@ -309,8 +306,6 @@ def test_where_series(self, data, na_value, as_frame): expected = expected.to_frame(name='a') self.assert_equal(result, expected) - @pytest.mark.parametrize("use_numpy", [True, False]) - @pytest.mark.parametrize("as_series", [True, False]) @pytest.mark.parametrize("repeats", [0, 1, 2, [1, 2, 3]]) def test_repeat(self, data, repeats, as_series, use_numpy): arr = type(data)._from_sequence(data[:3], dtype=data.dtype) @@ -327,7 +322,6 @@ def test_repeat(self, data, repeats, as_series, use_numpy): self.assert_equal(result, expected) - @pytest.mark.parametrize("use_numpy", [True, False]) @pytest.mark.parametrize('repeats, kwargs, error, msg', [ (2, dict(axis=1), ValueError, "'axis"), (-1, dict(), ValueError, "negative"), diff --git a/pandas/tests/extension/base/missing.py b/pandas/tests/extension/base/missing.py index 2fe547e50a34b..834f49f0461f0 100644 --- a/pandas/tests/extension/base/missing.py +++ b/pandas/tests/extension/base/missing.py @@ -1,5 +1,4 @@ import numpy as np -import pytest import pandas as pd import pandas.util.testing as tm @@ -89,14 +88,13 @@ def test_fillna_series(self, data_missing): result = ser.fillna(ser) self.assert_series_equal(result, ser) - @pytest.mark.parametrize('method', ['ffill', 'bfill']) - def test_fillna_series_method(self, data_missing, method): + def test_fillna_series_method(self, data_missing, fillna_method): fill_value = data_missing[1] - if method == 'ffill': + if fillna_method == 'ffill': data_missing = data_missing[::-1] - result = pd.Series(data_missing).fillna(method=method) + result = pd.Series(data_missing).fillna(method=fillna_method) expected = pd.Series(data_missing._from_sequence( [fill_value, fill_value], dtype=data_missing.dtype)) diff --git a/pandas/tests/extension/base/setitem.py b/pandas/tests/extension/base/setitem.py index 42fda982f7339..db6328e39e6cc 100644 --- a/pandas/tests/extension/base/setitem.py +++ b/pandas/tests/extension/base/setitem.py @@ -24,7 +24,6 @@ def test_setitem_sequence(self, data, box_in_series): assert data[0] == original[1] assert data[1] == original[0] - @pytest.mark.parametrize('as_array', [True, False]) def test_setitem_sequence_mismatched_length_raises(self, data, as_array): ser = pd.Series(data) original = ser.copy() diff --git a/pandas/tests/extension/conftest.py b/pandas/tests/extension/conftest.py index 5349dd919f2a2..3cc2d313b09f5 100644 --- a/pandas/tests/extension/conftest.py +++ b/pandas/tests/extension/conftest.py @@ -2,6 +2,8 @@ import pytest +from pandas import Series + @pytest.fixture def dtype(): @@ -108,3 +110,58 @@ def data_for_grouping(): def box_in_series(request): """Whether to box the data in a Series""" return request.param + + +@pytest.fixture(params=[ + lambda x: 1, + lambda x: [1] * len(x), + lambda x: Series([1] * len(x)), + lambda x: x, +], ids=['scalar', 'list', 'series', 'object']) +def groupby_apply_op(request): + """ + Functions to test groupby.apply(). + """ + return request.param + + +@pytest.fixture(params=[True, False]) +def as_frame(request): + """ + Boolean fixture to support Series and Series.to_frame() comparison testing. + """ + return request.param + + +@pytest.fixture(params=[True, False]) +def as_series(request): + """ + Boolean fixture to support arr and Series(arr) comparison testing. + """ + return request.param + + +@pytest.fixture(params=[True, False]) +def use_numpy(request): + """ + Boolean fixture to support comparison testing of ExtensionDtype array + and numpy array. + """ + return request.param + + +@pytest.fixture(params=['ffill', 'bfill']) +def fillna_method(request): + """ + Parametrized fixture giving method parameters 'ffill' and 'bfill' for + Series.fillna(method=) testing. + """ + return request.param + + +@pytest.fixture(params=[True, False]) +def as_array(request): + """ + Boolean fixture to support ExtensionDtype _from_sequence method testing. + """ + return request.param diff --git a/pandas/tests/extension/numpy_/__init__.py b/pandas/tests/extension/numpy_/__init__.py deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/pandas/tests/extension/numpy_/conftest.py b/pandas/tests/extension/numpy_/conftest.py deleted file mode 100644 index daa93571c2957..0000000000000 --- a/pandas/tests/extension/numpy_/conftest.py +++ /dev/null @@ -1,38 +0,0 @@ -import numpy as np -import pytest - -from pandas.core.arrays.numpy_ import PandasArray - - -@pytest.fixture -def allow_in_pandas(monkeypatch): - """ - A monkeypatch to tell pandas to let us in. - - By default, passing a PandasArray to an index / series / frame - constructor will unbox that PandasArray to an ndarray, and treat - it as a non-EA column. We don't want people using EAs without - reason. - - The mechanism for this is a check against ABCPandasArray - in each constructor. - - But, for testing, we need to allow them in pandas. So we patch - the _typ of PandasArray, so that we evade the ABCPandasArray - check. - """ - with monkeypatch.context() as m: - m.setattr(PandasArray, '_typ', 'extension') - yield - - -@pytest.fixture -def na_value(): - return np.nan - - -@pytest.fixture -def na_cmp(): - def cmp(a, b): - return np.isnan(a) and np.isnan(b) - return cmp diff --git a/pandas/tests/extension/numpy_/test_numpy.py b/pandas/tests/extension/numpy_/test_numpy.py deleted file mode 100644 index 4c93d5ee0b9d7..0000000000000 --- a/pandas/tests/extension/numpy_/test_numpy.py +++ /dev/null @@ -1,182 +0,0 @@ -import numpy as np -import pytest - -import pandas as pd -from pandas import compat -from pandas.core.arrays.numpy_ import PandasArray, PandasDtype -import pandas.util.testing as tm - -from .. import base - - -@pytest.fixture -def dtype(): - return PandasDtype(np.dtype('float')) - - -@pytest.fixture -def data(allow_in_pandas, dtype): - return PandasArray(np.arange(1, 101, dtype=dtype._dtype)) - - -@pytest.fixture -def data_missing(allow_in_pandas): - return PandasArray(np.array([np.nan, 1.0])) - - -@pytest.fixture -def data_for_sorting(allow_in_pandas): - """Length-3 array with a known sort order. - - This should be three items [B, C, A] with - A < B < C - """ - return PandasArray( - np.array([1, 2, 0]) - ) - - -@pytest.fixture -def data_missing_for_sorting(allow_in_pandas): - """Length-3 array with a known sort order. - - This should be three items [B, NA, A] with - A < B and NA missing. - """ - return PandasArray( - np.array([1, np.nan, 0]) - ) - - -@pytest.fixture -def data_for_grouping(allow_in_pandas): - """Data for factorization, grouping, and unique tests. - - Expected to be like [B, B, NA, NA, A, A, B, C] - - Where A < B < C and NA is missing - """ - a, b, c = np.arange(3) - return PandasArray(np.array( - [b, b, np.nan, np.nan, a, a, b, c] - )) - - -class BaseNumPyTests(object): - pass - - -class TestCasting(BaseNumPyTests, base.BaseCastingTests): - pass - - -class TestConstructors(BaseNumPyTests, base.BaseConstructorsTests): - @pytest.mark.skip(reason="We don't register our dtype") - # We don't want to register. This test should probably be split in two. - def test_from_dtype(self, data): - pass - - -class TestDtype(BaseNumPyTests, base.BaseDtypeTests): - - @pytest.mark.skip(reason="Incorrect expected.") - # we unsurprisingly clash with a NumPy name. - def test_check_dtype(self, data): - pass - - -class TestGetitem(BaseNumPyTests, base.BaseGetitemTests): - pass - - -class TestGroupby(BaseNumPyTests, base.BaseGroupbyTests): - pass - - -class TestInterface(BaseNumPyTests, base.BaseInterfaceTests): - pass - - -class TestMethods(BaseNumPyTests, base.BaseMethodsTests): - - @pytest.mark.skip(reason="TODO: remove?") - def test_value_counts(self, all_data, dropna): - pass - - @pytest.mark.skip(reason="Incorrect expected") - # We have a bool dtype, so the result is an ExtensionArray - # but expected is not - def test_combine_le(self, data_repeated): - super(TestMethods, self).test_combine_le(data_repeated) - - -class TestArithmetics(BaseNumPyTests, base.BaseArithmeticOpsTests): - divmod_exc = None - series_scalar_exc = None - frame_scalar_exc = None - series_array_exc = None - - def test_divmod_series_array(self, data): - s = pd.Series(data) - self._check_divmod_op(s, divmod, data, exc=None) - - @pytest.mark.skip("We implement ops") - def test_error(self, data, all_arithmetic_operators): - pass - - def test_arith_series_with_scalar(self, data, all_arithmetic_operators): - if (compat.PY2 and - all_arithmetic_operators in {'__div__', '__rdiv__'}): - raise pytest.skip( - "Matching NumPy int / int -> float behavior." - ) - super(TestArithmetics, self).test_arith_series_with_scalar( - data, all_arithmetic_operators - ) - - def test_arith_series_with_array(self, data, all_arithmetic_operators): - if (compat.PY2 and - all_arithmetic_operators in {'__div__', '__rdiv__'}): - raise pytest.skip( - "Matching NumPy int / int -> float behavior." - ) - super(TestArithmetics, self).test_arith_series_with_array( - data, all_arithmetic_operators - ) - - -class TestPrinting(BaseNumPyTests, base.BasePrintingTests): - pass - - -class TestNumericReduce(BaseNumPyTests, base.BaseNumericReduceTests): - - def check_reduce(self, s, op_name, skipna): - result = getattr(s, op_name)(skipna=skipna) - # avoid coercing int -> float. Just cast to the actual numpy type. - expected = getattr(s.astype(s.dtype._dtype), op_name)(skipna=skipna) - tm.assert_almost_equal(result, expected) - - -class TestBooleanReduce(BaseNumPyTests, base.BaseBooleanReduceTests): - pass - - -class TestMising(BaseNumPyTests, base.BaseMissingTests): - pass - - -class TestReshaping(BaseNumPyTests, base.BaseReshapingTests): - - @pytest.mark.skip("Incorrect parent test") - # not actually a mixed concat, since we concat int and int. - def test_concat_mixed_dtypes(self, data): - super(TestReshaping, self).test_concat_mixed_dtypes(data) - - -class TestSetitem(BaseNumPyTests, base.BaseSetitemTests): - pass - - -class TestParsing(BaseNumPyTests, base.BaseParsingTests): - pass diff --git a/pandas/tests/extension/numpy_/test_numpy_nested.py b/pandas/tests/extension/numpy_/test_numpy_nested.py deleted file mode 100644 index cf9b34dd08798..0000000000000 --- a/pandas/tests/extension/numpy_/test_numpy_nested.py +++ /dev/null @@ -1,286 +0,0 @@ -""" -Tests for PandasArray with nested data. Users typically won't create -these objects via `pd.array`, but they can show up through `.array` -on a Series with nested data. - -We partition these tests into their own file, as many of the base -tests fail, as they aren't appropriate for nested data. It is easier -to have a seperate file with its own data generating fixtures, than -trying to skip based upon the value of a fixture. -""" -import pytest - -import pandas as pd -from pandas.core.arrays.numpy_ import PandasArray, PandasDtype - -from .. import base - -# For NumPy <1.16, np.array([np.nan, (1,)]) raises -# ValueError: setting an array element with a sequence. -np = pytest.importorskip('numpy', minversion='1.16.0') - - -@pytest.fixture -def dtype(): - return PandasDtype(np.dtype('object')) - - -@pytest.fixture -def data(allow_in_pandas, dtype): - return pd.Series([(i,) for i in range(100)]).array - - -@pytest.fixture -def data_missing(allow_in_pandas): - return PandasArray(np.array([np.nan, (1,)])) - - -@pytest.fixture -def data_for_sorting(allow_in_pandas): - """Length-3 array with a known sort order. - - This should be three items [B, C, A] with - A < B < C - """ - # Use an empty tuple for first element, then remove, - # to disable np.array's shape inference. - return PandasArray( - np.array([(), (2,), (3,), (1,)])[1:] - ) - - -@pytest.fixture -def data_missing_for_sorting(allow_in_pandas): - """Length-3 array with a known sort order. - - This should be three items [B, NA, A] with - A < B and NA missing. - """ - return PandasArray( - np.array([(1,), np.nan, (0,)]) - ) - - -@pytest.fixture -def data_for_grouping(allow_in_pandas): - """Data for factorization, grouping, and unique tests. - - Expected to be like [B, B, NA, NA, A, A, B, C] - - Where A < B < C and NA is missing - """ - a, b, c = (1,), (2,), (3,) - return PandasArray(np.array( - [b, b, np.nan, np.nan, a, a, b, c] - )) - - -skip_nested = pytest.mark.skip(reason="Skipping for nested PandasArray") - - -class BaseNumPyTests(object): - pass - - -class TestCasting(BaseNumPyTests, base.BaseCastingTests): - - @skip_nested - def test_astype_str(self, data): - pass - - -class TestConstructors(BaseNumPyTests, base.BaseConstructorsTests): - @pytest.mark.skip(reason="We don't register our dtype") - # We don't want to register. This test should probably be split in two. - def test_from_dtype(self, data): - pass - - @skip_nested - def test_array_from_scalars(self, data): - pass - - -class TestDtype(BaseNumPyTests, base.BaseDtypeTests): - - @pytest.mark.skip(reason="Incorrect expected.") - # we unsurprisingly clash with a NumPy name. - def test_check_dtype(self, data): - pass - - -class TestGetitem(BaseNumPyTests, base.BaseGetitemTests): - - @skip_nested - def test_getitem_scalar(self, data): - pass - - @skip_nested - def test_take_series(self, data): - pass - - -class TestGroupby(BaseNumPyTests, base.BaseGroupbyTests): - @skip_nested - def test_groupby_extension_apply(self, data_for_grouping, op): - pass - - -class TestInterface(BaseNumPyTests, base.BaseInterfaceTests): - @skip_nested - def test_array_interface(self, data): - # NumPy array shape inference - pass - - -class TestMethods(BaseNumPyTests, base.BaseMethodsTests): - - @pytest.mark.skip(reason="TODO: remove?") - def test_value_counts(self, all_data, dropna): - pass - - @pytest.mark.skip(reason="Incorrect expected") - # We have a bool dtype, so the result is an ExtensionArray - # but expected is not - def test_combine_le(self, data_repeated): - super(TestMethods, self).test_combine_le(data_repeated) - - @skip_nested - def test_combine_add(self, data_repeated): - # Not numeric - pass - - @skip_nested - def test_shift_fill_value(self, data): - # np.array shape inference. Shift implementation fails. - super().test_shift_fill_value(data) - - @skip_nested - def test_unique(self, data, box, method): - # Fails creating expected - pass - - @skip_nested - def test_fillna_copy_frame(self, data_missing): - # The "scalar" for this array isn't a scalar. - pass - - @skip_nested - def test_fillna_copy_series(self, data_missing): - # The "scalar" for this array isn't a scalar. - pass - - @skip_nested - def test_hash_pandas_object_works(self, data, as_frame): - # ndarray of tuples not hashable - pass - - @skip_nested - def test_searchsorted(self, data_for_sorting, as_series): - # Test setup fails. - pass - - @skip_nested - def test_where_series(self, data, na_value, as_frame): - # Test setup fails. - pass - - @skip_nested - def test_repeat(self, data, repeats, as_series, use_numpy): - # Fails creating expected - pass - - -class TestPrinting(BaseNumPyTests, base.BasePrintingTests): - pass - - -class TestMissing(BaseNumPyTests, base.BaseMissingTests): - - @skip_nested - def test_fillna_scalar(self, data_missing): - # Non-scalar "scalar" values. - pass - - @skip_nested - def test_fillna_series_method(self, data_missing, method): - # Non-scalar "scalar" values. - pass - - @skip_nested - def test_fillna_series(self, data_missing): - # Non-scalar "scalar" values. - pass - - @skip_nested - def test_fillna_frame(self, data_missing): - # Non-scalar "scalar" values. - pass - - -class TestReshaping(BaseNumPyTests, base.BaseReshapingTests): - - @pytest.mark.skip("Incorrect parent test") - # not actually a mixed concat, since we concat int and int. - def test_concat_mixed_dtypes(self, data): - super(TestReshaping, self).test_concat_mixed_dtypes(data) - - @skip_nested - def test_merge(self, data, na_value): - # Fails creating expected - pass - - @skip_nested - def test_merge_on_extension_array(self, data): - # Fails creating expected - pass - - @skip_nested - def test_merge_on_extension_array_duplicates(self, data): - # Fails creating expected - pass - - -class TestSetitem(BaseNumPyTests, base.BaseSetitemTests): - - @skip_nested - def test_setitem_scalar_series(self, data, box_in_series): - pass - - @skip_nested - def test_setitem_sequence(self, data, box_in_series): - pass - - @skip_nested - def test_setitem_sequence_mismatched_length_raises(self, data, as_array): - pass - - @skip_nested - def test_setitem_sequence_broadcasts(self, data, box_in_series): - pass - - @skip_nested - def test_setitem_loc_scalar_mixed(self, data): - pass - - @skip_nested - def test_setitem_loc_scalar_multiple_homogoneous(self, data): - pass - - @skip_nested - def test_setitem_iloc_scalar_mixed(self, data): - pass - - @skip_nested - def test_setitem_iloc_scalar_multiple_homogoneous(self, data): - pass - - @skip_nested - def test_setitem_mask_broadcast(self, data, setter): - pass - - @skip_nested - def test_setitem_scalar_key_sequence_raise(self, data): - pass - - -# Skip Arithmetics, NumericReduce, BooleanReduce, Parsing diff --git a/pandas/tests/extension/test_numpy.py b/pandas/tests/extension/test_numpy.py new file mode 100644 index 0000000000000..41f5beb8c885d --- /dev/null +++ b/pandas/tests/extension/test_numpy.py @@ -0,0 +1,430 @@ +import numpy as np +import pytest + +from pandas.compat.numpy import _np_version_under1p16 + +import pandas as pd +from pandas import compat +from pandas.core.arrays.numpy_ import PandasArray, PandasDtype +import pandas.util.testing as tm + +from . import base + + +@pytest.fixture(params=['float', 'object']) +def dtype(request): + return PandasDtype(np.dtype(request.param)) + + +@pytest.fixture +def allow_in_pandas(monkeypatch): + """ + A monkeypatch to tells pandas to let us in. + + By default, passing a PandasArray to an index / series / frame + constructor will unbox that PandasArray to an ndarray, and treat + it as a non-EA column. We don't want people using EAs without + reason. + + The mechanism for this is a check against ABCPandasArray + in each constructor. + + But, for testing, we need to allow them in pandas. So we patch + the _typ of PandasArray, so that we evade the ABCPandasArray + check. + """ + with monkeypatch.context() as m: + m.setattr(PandasArray, '_typ', 'extension') + yield + + +@pytest.fixture +def data(allow_in_pandas, dtype): + if dtype.numpy_dtype == 'object': + return pd.Series([(i,) for i in range(100)]).array + return PandasArray(np.arange(1, 101, dtype=dtype._dtype)) + + +@pytest.fixture +def data_missing(allow_in_pandas, dtype): + # For NumPy <1.16, np.array([np.nan, (1,)]) raises + # ValueError: setting an array element with a sequence. + if dtype.numpy_dtype == 'object': + if _np_version_under1p16: + raise pytest.skip("Skipping for NumPy <1.16") + return PandasArray(np.array([np.nan, (1,)])) + return PandasArray(np.array([np.nan, 1.0])) + + +@pytest.fixture +def na_value(): + return np.nan + + +@pytest.fixture +def na_cmp(): + def cmp(a, b): + return np.isnan(a) and np.isnan(b) + return cmp + + +@pytest.fixture +def data_for_sorting(allow_in_pandas, dtype): + """Length-3 array with a known sort order. + + This should be three items [B, C, A] with + A < B < C + """ + if dtype.numpy_dtype == 'object': + # Use an empty tuple for first element, then remove, + # to disable np.array's shape inference. + return PandasArray( + np.array([(), (2,), (3,), (1,)])[1:] + ) + return PandasArray( + np.array([1, 2, 0]) + ) + + +@pytest.fixture +def data_missing_for_sorting(allow_in_pandas, dtype): + """Length-3 array with a known sort order. + + This should be three items [B, NA, A] with + A < B and NA missing. + """ + if dtype.numpy_dtype == 'object': + return PandasArray( + np.array([(1,), np.nan, (0,)]) + ) + return PandasArray( + np.array([1, np.nan, 0]) + ) + + +@pytest.fixture +def data_for_grouping(allow_in_pandas, dtype): + """Data for factorization, grouping, and unique tests. + + Expected to be like [B, B, NA, NA, A, A, B, C] + + Where A < B < C and NA is missing + """ + if dtype.numpy_dtype == 'object': + a, b, c = (1,), (2,), (3,) + else: + a, b, c = np.arange(3) + return PandasArray(np.array( + [b, b, np.nan, np.nan, a, a, b, c] + )) + + +@pytest.fixture +def skip_numpy_object(dtype): + """ + Tests for PandasArray with nested data. Users typically won't create + these objects via `pd.array`, but they can show up through `.array` + on a Series with nested data. Many of the base tests fail, as they aren't + appropriate for nested data. + + This fixture allows these tests to be skipped when used as a usefixtures + marker to either an individual test or a test class. + """ + if dtype == 'object': + raise pytest.skip("Skipping for object dtype.") + + +skip_nested = pytest.mark.usefixtures('skip_numpy_object') + + +class BaseNumPyTests(object): + pass + + +class TestCasting(BaseNumPyTests, base.BaseCastingTests): + + @skip_nested + def test_astype_str(self, data): + # ValueError: setting an array element with a sequence + super(TestCasting, self).test_astype_str(data) + + +class TestConstructors(BaseNumPyTests, base.BaseConstructorsTests): + @pytest.mark.skip(reason="We don't register our dtype") + # We don't want to register. This test should probably be split in two. + def test_from_dtype(self, data): + pass + + @skip_nested + def test_array_from_scalars(self, data): + # ValueError: PandasArray must be 1-dimensional. + super(TestConstructors, self).test_array_from_scalars(data) + + +class TestDtype(BaseNumPyTests, base.BaseDtypeTests): + + @pytest.mark.skip(reason="Incorrect expected.") + # we unsurprisingly clash with a NumPy name. + def test_check_dtype(self, data): + pass + + +class TestGetitem(BaseNumPyTests, base.BaseGetitemTests): + + @skip_nested + def test_getitem_scalar(self, data): + # AssertionError + super(TestGetitem, self).test_getitem_scalar(data) + + @skip_nested + def test_take_series(self, data): + # ValueError: PandasArray must be 1-dimensional. + super(TestGetitem, self).test_take_series(data) + + +class TestGroupby(BaseNumPyTests, base.BaseGroupbyTests): + @skip_nested + def test_groupby_extension_apply( + self, data_for_grouping, groupby_apply_op): + # ValueError: Names should be list-like for a MultiIndex + super(TestGroupby, self).test_groupby_extension_apply( + data_for_grouping, groupby_apply_op) + + +class TestInterface(BaseNumPyTests, base.BaseInterfaceTests): + @skip_nested + def test_array_interface(self, data): + # NumPy array shape inference + super(TestInterface, self).test_array_interface(data) + + +class TestMethods(BaseNumPyTests, base.BaseMethodsTests): + + @pytest.mark.skip(reason="TODO: remove?") + def test_value_counts(self, all_data, dropna): + pass + + @pytest.mark.skip(reason="Incorrect expected") + # We have a bool dtype, so the result is an ExtensionArray + # but expected is not + def test_combine_le(self, data_repeated): + super(TestMethods, self).test_combine_le(data_repeated) + + @skip_nested + def test_combine_add(self, data_repeated): + # Not numeric + super(TestMethods, self).test_combine_add(data_repeated) + + @skip_nested + def test_shift_fill_value(self, data): + # np.array shape inference. Shift implementation fails. + super(TestMethods, self).test_shift_fill_value(data) + + @skip_nested + @pytest.mark.parametrize('box', [pd.Series, lambda x: x]) + @pytest.mark.parametrize('method', [lambda x: x.unique(), pd.unique]) + def test_unique(self, data, box, method): + # Fails creating expected + super(TestMethods, self).test_unique(data, box, method) + + @skip_nested + def test_fillna_copy_frame(self, data_missing): + # The "scalar" for this array isn't a scalar. + super(TestMethods, self).test_fillna_copy_frame(data_missing) + + @skip_nested + def test_fillna_copy_series(self, data_missing): + # The "scalar" for this array isn't a scalar. + super(TestMethods, self).test_fillna_copy_series(data_missing) + + @skip_nested + def test_hash_pandas_object_works(self, data, as_frame): + # ndarray of tuples not hashable + super(TestMethods, self).test_hash_pandas_object_works(data, as_frame) + + @skip_nested + def test_searchsorted(self, data_for_sorting, as_series): + # Test setup fails. + super(TestMethods, self).test_searchsorted(data_for_sorting, as_series) + + @skip_nested + def test_where_series(self, data, na_value, as_frame): + # Test setup fails. + super(TestMethods, self).test_where_series(data, na_value, as_frame) + + @skip_nested + @pytest.mark.parametrize("repeats", [0, 1, 2, [1, 2, 3]]) + def test_repeat(self, data, repeats, as_series, use_numpy): + # Fails creating expected + super(TestMethods, self).test_repeat( + data, repeats, as_series, use_numpy) + + +@skip_nested +class TestArithmetics(BaseNumPyTests, base.BaseArithmeticOpsTests): + divmod_exc = None + series_scalar_exc = None + frame_scalar_exc = None + series_array_exc = None + + def test_divmod_series_array(self, data): + s = pd.Series(data) + self._check_divmod_op(s, divmod, data, exc=None) + + @pytest.mark.skip("We implement ops") + def test_error(self, data, all_arithmetic_operators): + pass + + def test_arith_series_with_scalar(self, data, all_arithmetic_operators): + if (compat.PY2 and + all_arithmetic_operators in {'__div__', '__rdiv__'}): + raise pytest.skip( + "Matching NumPy int / int -> float behavior." + ) + super(TestArithmetics, self).test_arith_series_with_scalar( + data, all_arithmetic_operators + ) + + def test_arith_series_with_array(self, data, all_arithmetic_operators): + if (compat.PY2 and + all_arithmetic_operators in {'__div__', '__rdiv__'}): + raise pytest.skip( + "Matching NumPy int / int -> float behavior." + ) + super(TestArithmetics, self).test_arith_series_with_array( + data, all_arithmetic_operators + ) + + +class TestPrinting(BaseNumPyTests, base.BasePrintingTests): + pass + + +@skip_nested +class TestNumericReduce(BaseNumPyTests, base.BaseNumericReduceTests): + + def check_reduce(self, s, op_name, skipna): + result = getattr(s, op_name)(skipna=skipna) + # avoid coercing int -> float. Just cast to the actual numpy type. + expected = getattr(s.astype(s.dtype._dtype), op_name)(skipna=skipna) + tm.assert_almost_equal(result, expected) + + +@skip_nested +class TestBooleanReduce(BaseNumPyTests, base.BaseBooleanReduceTests): + pass + + +class TestMissing(BaseNumPyTests, base.BaseMissingTests): + + @skip_nested + def test_fillna_scalar(self, data_missing): + # Non-scalar "scalar" values. + super(TestMissing, self).test_fillna_scalar(data_missing) + + @skip_nested + def test_fillna_series_method(self, data_missing, fillna_method): + # Non-scalar "scalar" values. + super(TestMissing, self).test_fillna_series_method( + data_missing, fillna_method) + + @skip_nested + def test_fillna_series(self, data_missing): + # Non-scalar "scalar" values. + super(TestMissing, self).test_fillna_series(data_missing) + + @skip_nested + def test_fillna_frame(self, data_missing): + # Non-scalar "scalar" values. + super(TestMissing, self).test_fillna_frame(data_missing) + + +class TestReshaping(BaseNumPyTests, base.BaseReshapingTests): + + @pytest.mark.skip("Incorrect parent test") + # not actually a mixed concat, since we concat int and int. + def test_concat_mixed_dtypes(self, data): + super(TestReshaping, self).test_concat_mixed_dtypes(data) + + @skip_nested + def test_merge(self, data, na_value): + # Fails creating expected + super(TestReshaping, self).test_merge(data, na_value) + + @skip_nested + def test_merge_on_extension_array(self, data): + # Fails creating expected + super(TestReshaping, self).test_merge_on_extension_array(data) + + @skip_nested + def test_merge_on_extension_array_duplicates(self, data): + # Fails creating expected + super(TestReshaping, self).test_merge_on_extension_array_duplicates( + data) + + +class TestSetitem(BaseNumPyTests, base.BaseSetitemTests): + + @skip_nested + def test_setitem_scalar_series(self, data, box_in_series): + # AssertionError + super(TestSetitem, self).test_setitem_scalar_series( + data, box_in_series) + + @skip_nested + def test_setitem_sequence(self, data, box_in_series): + # ValueError: shape mismatch: value array of shape (2,1) could not + # be broadcast to indexing result of shape (2,) + super(TestSetitem, self).test_setitem_sequence(data, box_in_series) + + @skip_nested + def test_setitem_sequence_mismatched_length_raises(self, data, as_array): + # ValueError: PandasArray must be 1-dimensional. + (super(TestSetitem, self). + test_setitem_sequence_mismatched_length_raises(data, as_array)) + + @skip_nested + def test_setitem_sequence_broadcasts(self, data, box_in_series): + # ValueError: cannot set using a list-like indexer with a different + # length than the value + super(TestSetitem, self).test_setitem_sequence_broadcasts( + data, box_in_series) + + @skip_nested + def test_setitem_loc_scalar_mixed(self, data): + # AssertionError + super(TestSetitem, self).test_setitem_loc_scalar_mixed(data) + + @skip_nested + def test_setitem_loc_scalar_multiple_homogoneous(self, data): + # AssertionError + super(TestSetitem, self).test_setitem_loc_scalar_multiple_homogoneous( + data) + + @skip_nested + def test_setitem_iloc_scalar_mixed(self, data): + # AssertionError + super(TestSetitem, self).test_setitem_iloc_scalar_mixed(data) + + @skip_nested + def test_setitem_iloc_scalar_multiple_homogoneous(self, data): + # AssertionError + super(TestSetitem, self).test_setitem_iloc_scalar_multiple_homogoneous( + data) + + @skip_nested + @pytest.mark.parametrize('setter', ['loc', None]) + def test_setitem_mask_broadcast(self, data, setter): + # ValueError: cannot set using a list-like indexer with a different + # length than the value + super(TestSetitem, self).test_setitem_mask_broadcast(data, setter) + + @skip_nested + def test_setitem_scalar_key_sequence_raise(self, data): + # Failed: DID NOT RAISE + super(TestSetitem, self).test_setitem_scalar_key_sequence_raise(data) + + +@skip_nested +class TestParsing(BaseNumPyTests, base.BaseParsingTests): + pass diff --git a/pandas/tests/extension/test_sparse.py b/pandas/tests/extension/test_sparse.py index 21dbf9524961c..146dea2b65d83 100644 --- a/pandas/tests/extension/test_sparse.py +++ b/pandas/tests/extension/test_sparse.py @@ -287,11 +287,10 @@ def test_combine_first(self, data): pytest.skip("TODO(SparseArray.__setitem__ will preserve dtype.") super(TestMethods, self).test_combine_first(data) - @pytest.mark.parametrize("as_series", [True, False]) def test_searchsorted(self, data_for_sorting, as_series): with tm.assert_produces_warning(PerformanceWarning): super(TestMethods, self).test_searchsorted(data_for_sorting, - as_series=as_series) + as_series) class TestCasting(BaseSparseTests, base.BaseCastingTests): From 5278cc6dc674c225c204532aeedca9df342f67c7 Mon Sep 17 00:00:00 2001 From: Daniel Saxton Date: Wed, 6 Feb 2019 22:56:54 -0600 Subject: [PATCH 073/215] DOC/CLN: Fix errors in Series docstrings (#24945) --- pandas/core/series.py | 265 +++++++++++++++++++++++++++--------------- 1 file changed, 169 insertions(+), 96 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index ae6da1d5f015b..0b90146d3a548 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -130,7 +130,7 @@ class Series(base.IndexOpsMixin, generic.NDFrame): sequence are used, the index will override the keys found in the dict. dtype : str, numpy.dtype, or ExtensionDtype, optional - dtype for the output Series. If not specified, this will be + Data type for the output Series. If not specified, this will be inferred from `data`. See the :ref:`user guide ` for more usages. copy : bool, default False @@ -445,7 +445,7 @@ def values(self): Returns ------- - arr : numpy.ndarray or ndarray-like + numpy.ndarray or ndarray-like See Also -------- @@ -514,6 +514,11 @@ def ravel(self, order='C'): """ Return the flattened underlying data as an ndarray. + Returns + ------- + numpy.ndarray or ndarray-like + Flattened data of the Series. + See Also -------- numpy.ndarray.ravel @@ -831,7 +836,7 @@ def _ixs(self, i, axis=0): Returns ------- - value : scalar (int) or Series (slice, sequence) + scalar (int) or Series (slice, sequence) """ try: @@ -1174,7 +1179,7 @@ def get_value(self, label, takeable=False): Returns ------- - value : scalar value + scalar value """ warnings.warn("get_value is deprecated and will be removed " "in a future release. Please use " @@ -1208,7 +1213,7 @@ def set_value(self, label, value, takeable=False): Returns ------- - series : Series + Series If label is contained, will be reference to calling Series, otherwise a new object """ @@ -1395,29 +1400,30 @@ def to_string(self, buf=None, na_rep='NaN', float_format=None, header=True, Parameters ---------- buf : StringIO-like, optional - buffer to write to - na_rep : string, optional - string representation of NAN to use, default 'NaN' + Buffer to write to. + na_rep : str, optional + String representation of NaN to use, default 'NaN'. float_format : one-parameter function, optional - formatter function to apply to columns' elements if they are floats - default None - header : boolean, default True - Add the Series header (index name) + Formatter function to apply to columns' elements if they are + floats, default None. + header : bool, default True + Add the Series header (index name). index : bool, optional - Add index (row) labels, default True - length : boolean, default False - Add the Series length - dtype : boolean, default False - Add the Series dtype - name : boolean, default False - Add the Series name if not None + Add index (row) labels, default True. + length : bool, default False + Add the Series length. + dtype : bool, default False + Add the Series dtype. + name : bool, default False + Add the Series name if not None. max_rows : int, optional Maximum number of rows to show before truncating. If None, show all. Returns ------- - formatted : string (if not buffer passed) + str or None + String representation of Series if ``buf=None``, otherwise None. """ formatter = fmt.SeriesFormatter(self, name=name, length=length, @@ -1477,7 +1483,8 @@ def to_dict(self, into=dict): Returns ------- - value_dict : collections.Mapping + collections.Mapping + Key-value representation of Series. Examples -------- @@ -1489,7 +1496,7 @@ def to_dict(self, into=dict): OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) >>> dd = defaultdict(list) >>> s.to_dict(dd) - defaultdict(, {0: 1, 1: 2, 2: 3, 3: 4}) + defaultdict(, {0: 1, 1: 2, 2: 3, 3: 4}) """ # GH16122 into_c = com.standardize_mapping(into) @@ -1507,7 +1514,18 @@ def to_frame(self, name=None): Returns ------- - data_frame : DataFrame + DataFrame + DataFrame representation of Series. + + Examples + -------- + >>> s = pd.Series(["a", "b", "c"], + ... name="vals") + >>> s.to_frame() + vals + 0 a + 1 b + 2 c """ if name is None: df = self._constructor_expanddim(self) @@ -1522,12 +1540,14 @@ def to_sparse(self, kind='block', fill_value=None): Parameters ---------- - kind : {'block', 'integer'} + kind : {'block', 'integer'}, default 'block' fill_value : float, defaults to NaN (missing) + Value to use for filling NaN values. Returns ------- - sp : SparseSeries + SparseSeries + Sparse representation of the Series. """ # TODO: deprecate from pandas.core.sparse.series import SparseSeries @@ -1565,11 +1585,18 @@ def count(self, level=None): ---------- level : int or level name, default None If the axis is a MultiIndex (hierarchical), count along a - particular level, collapsing into a smaller Series + particular level, collapsing into a smaller Series. Returns ------- - nobs : int or Series (if level specified) + int or Series (if level specified) + Number of non-null values in the Series. + + Examples + -------- + >>> s = pd.Series([0.0, 1.0, np.nan]) + >>> s.count() + 2 """ if level is None: return notna(com.values_from_object(self)).sum() @@ -1598,14 +1625,15 @@ def mode(self, dropna=True): Parameters ---------- - dropna : boolean, default True + dropna : bool, default True Don't consider counts of NaN/NaT. .. versionadded:: 0.24.0 Returns ------- - modes : Series (sorted) + Series + Modes of the Series in sorted order. """ # TODO: Add option for bins like value_counts() return algorithms.mode(self, dropna=dropna) @@ -1678,12 +1706,13 @@ def drop_duplicates(self, keep='first', inplace=False): - 'first' : Drop duplicates except for the first occurrence. - 'last' : Drop duplicates except for the last occurrence. - ``False`` : Drop all duplicates. - inplace : boolean, default ``False`` + inplace : bool, default ``False`` If ``True``, performs operation inplace and returns None. Returns ------- - deduplicated : Series + Series + Series with duplicates dropped. See Also -------- @@ -1760,7 +1789,9 @@ def duplicated(self, keep='first'): Returns ------- - pandas.core.series.Series + Series + Series indicating whether each value has occurred in the + preceding values. See Also -------- @@ -1824,7 +1855,7 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): Parameters ---------- - skipna : boolean, default True + skipna : bool, default True Exclude NA/null values. If the entire Series is NA, the result will be NA. axis : int, default 0 @@ -1836,7 +1867,8 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): Returns ------- - idxmin : Index of minimum of values. + Index + Label of the minimum value. Raises ------ @@ -1861,7 +1893,7 @@ def idxmin(self, axis=0, skipna=True, *args, **kwargs): Examples -------- >>> s = pd.Series(data=[1, None, 4, 1], - ... index=['A' ,'B' ,'C' ,'D']) + ... index=['A', 'B', 'C', 'D']) >>> s A 1.0 B NaN @@ -1893,7 +1925,7 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs): Parameters ---------- - skipna : boolean, default True + skipna : bool, default True Exclude NA/null values. If the entire Series is NA, the result will be NA. axis : int, default 0 @@ -1905,7 +1937,8 @@ def idxmax(self, axis=0, skipna=True, *args, **kwargs): Returns ------- - idxmax : Index of maximum of values. + Index + Label of the maximum value. Raises ------ @@ -1989,12 +2022,22 @@ def round(self, decimals=0, *args, **kwargs): Returns ------- - Series object + Series + Rounded values of the Series. See Also -------- - numpy.around - DataFrame.round + numpy.around : Round values of an np.array. + DataFrame.round : Round values of a DataFrame. + + Examples + -------- + >>> s = pd.Series([0.1, 1.3, 2.7]) + >>> s.round() + 0 0.0 + 1 1.0 + 2 3.0 + dtype: float64 """ nv.validate_round(args, kwargs) result = com.values_from_object(self).round(decimals) @@ -2009,7 +2052,7 @@ def quantile(self, q=0.5, interpolation='linear'): Parameters ---------- q : float or array-like, default 0.5 (50% quantile) - 0 <= q <= 1, the quantile(s) to compute + 0 <= q <= 1, the quantile(s) to compute. interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'} .. versionadded:: 0.18.0 @@ -2025,9 +2068,10 @@ def quantile(self, q=0.5, interpolation='linear'): Returns ------- - quantile : float or Series + float or Series If ``q`` is an array, a Series will be returned where the - index is ``q`` and the values are the quantiles. + index is ``q`` and the values are the quantiles, otherwise + a float will be returned. See Also -------- @@ -2073,6 +2117,7 @@ def corr(self, other, method='pearson', min_periods=None): Parameters ---------- other : Series + Series with which to compute the correlation. method : {'pearson', 'kendall', 'spearman'} or callable * pearson : standard correlation coefficient * kendall : Kendall Tau correlation coefficient @@ -2082,16 +2127,18 @@ def corr(self, other, method='pearson', min_periods=None): .. versionadded:: 0.24.0 min_periods : int, optional - Minimum number of observations needed to have a valid result + Minimum number of observations needed to have a valid result. Returns ------- - correlation : float + float + Correlation with other. Examples -------- - >>> histogram_intersection = lambda a, b: np.minimum(a, b - ... ).sum().round(decimals=1) + >>> def histogram_intersection(a, b): + ... v = np.minimum(a, b).sum().round(decimals=1) + ... return v >>> s1 = pd.Series([.2, .0, .6, .2]) >>> s2 = pd.Series([.3, .6, .0, .1]) >>> s1.corr(s2, method=histogram_intersection) @@ -2116,14 +2163,22 @@ def cov(self, other, min_periods=None): Parameters ---------- other : Series + Series with which to compute the covariance. min_periods : int, optional - Minimum number of observations needed to have a valid result + Minimum number of observations needed to have a valid result. Returns ------- - covariance : float + float + Covariance between Series and other normalized by N-1 + (unbiased estimator). - Normalized by N-1 (unbiased estimator). + Examples + -------- + >>> s1 = pd.Series([0.90010907, 0.13484424, 0.62036035]) + >>> s2 = pd.Series([0.12528585, 0.26962463, 0.51111198]) + >>> s1.cov(s2) + -0.01685762652715874 """ this, other = self.align(other, join='inner', copy=False) if len(this) == 0: @@ -2146,7 +2201,8 @@ def diff(self, periods=1): Returns ------- - diffed : Series + Series + First differences of the Series. See Also -------- @@ -2280,7 +2336,7 @@ def dot(self, other): 8 >>> s @ other 8 - >>> df = pd.DataFrame([[0 ,1], [-2, 3], [4, -5], [6, 7]]) + >>> df = pd.DataFrame([[0, 1], [-2, 3], [4, -5], [6, 7]]) >>> s.dot(df) 0 24 1 14 @@ -2349,17 +2405,19 @@ def append(self, to_append, ignore_index=False, verify_integrity=False): Parameters ---------- to_append : Series or list/tuple of Series - ignore_index : boolean, default False + Series to append with self. + ignore_index : bool, default False If True, do not use the index labels. .. versionadded:: 0.19.0 - verify_integrity : boolean, default False - If True, raise Exception on creating index with duplicates + verify_integrity : bool, default False + If True, raise Exception on creating index with duplicates. Returns ------- - appended : Series + Series + Concatenated Series. See Also -------- @@ -2377,7 +2435,7 @@ def append(self, to_append, ignore_index=False, verify_integrity=False): -------- >>> s1 = pd.Series([1, 2, 3]) >>> s2 = pd.Series([4, 5, 6]) - >>> s3 = pd.Series([4, 5, 6], index=[3,4,5]) + >>> s3 = pd.Series([4, 5, 6], index=[3, 4, 5]) >>> s1.append(s2) 0 1 1 2 @@ -2440,7 +2498,7 @@ def _binop(self, other, func, level=None, fill_value=None): Returns ------- - combined : Series + Series """ if not isinstance(other, Series): raise AssertionError('Other operand must be Series') @@ -2863,7 +2921,7 @@ def sort_index(self, axis=0, level=None, ascending=True, inplace=False, Returns ------- - pandas.Series + Series The original Series sorted by the labels. See Also @@ -3003,7 +3061,9 @@ def argsort(self, axis=0, kind='quicksort', order=None): Returns ------- - argsorted : Series, with -1 indicated where nan values are present + Series + Positions of values within the sort order with -1 indicating + nan values. See Also -------- @@ -3221,12 +3281,13 @@ def swaplevel(self, i=-2, j=-1, copy=True): Parameters ---------- - i, j : int, string (can be mixed) + i, j : int, str (can be mixed) Level of index to be swapped. Can pass level name as string. Returns ------- - swapped : Series + Series + Series with levels swapped in MultiIndex. .. versionchanged:: 0.18.1 @@ -3266,21 +3327,23 @@ def unstack(self, level=-1, fill_value=None): Parameters ---------- - level : int, string, or list of these, default last level - Level(s) to unstack, can pass level name - fill_value : replace NaN with this value if the unstack produces - missing values + level : int, str, or list of these, default last level + Level(s) to unstack, can pass level name. + fill_value : scalar value, default None + Value to use when replacing NaN values. .. versionadded:: 0.18.0 Returns ------- - unstacked : DataFrame + DataFrame + Unstacked Series. Examples -------- >>> s = pd.Series([1, 2, 3, 4], - ... index=pd.MultiIndex.from_product([['one', 'two'], ['a', 'b']])) + ... index=pd.MultiIndex.from_product([['one', 'two'], + ... ['a', 'b']])) >>> s one a 1 b 2 @@ -3680,7 +3743,7 @@ def rename(self, index=None, **kwargs): Scalar or hashable sequence-like will alter the ``Series.name`` attribute. copy : bool, default True - Also copy underlying data + Whether to copy underlying data. inplace : bool, default False Whether to return a new Series. If True then value of copy is ignored. @@ -3690,11 +3753,12 @@ def rename(self, index=None, **kwargs): Returns ------- - renamed : Series (new object) + Series + Series with index labels or name altered. See Also -------- - Series.rename_axis + Series.rename_axis : Set the name of the axis. Examples -------- @@ -3704,7 +3768,7 @@ def rename(self, index=None, **kwargs): 1 2 2 3 dtype: int64 - >>> s.rename("my_name") # scalar, changes Series.name + >>> s.rename("my_name") # scalar, changes Series.name 0 1 1 2 2 3 @@ -3763,7 +3827,8 @@ def drop(self, labels=None, axis=0, index=None, columns=None, Returns ------- - dropped : pandas.Series + Series + Series with specified index labels removed. Raises ------ @@ -3779,7 +3844,7 @@ def drop(self, labels=None, axis=0, index=None, columns=None, Examples -------- - >>> s = pd.Series(data=np.arange(3), index=['A','B','C']) + >>> s = pd.Series(data=np.arange(3), index=['A', 'B', 'C']) >>> s A 0 B 1 @@ -3788,7 +3853,7 @@ def drop(self, labels=None, axis=0, index=None, columns=None, Drop labels B en C - >>> s.drop(labels=['B','C']) + >>> s.drop(labels=['B', 'C']) A 0 dtype: int64 @@ -3961,7 +4026,8 @@ def isin(self, values): Returns ------- - isin : Series (bool dtype) + Series + Series of booleans indicating if each element is in values. Raises ------ @@ -4020,7 +4086,8 @@ def between(self, left, right, inclusive=True): Returns ------- Series - Each element will be a boolean. + Series representing whether each element is between left and + right (inclusive). See Also -------- @@ -4102,27 +4169,27 @@ def from_csv(cls, path, sep=',', parse_dates=True, header=None, Parameters ---------- - path : string file path or file handle / StringIO - sep : string, default ',' - Field delimiter - parse_dates : boolean, default True - Parse dates. Different default from read_table + path : str, file path, or file handle / StringIO + sep : str, default ',' + Field delimiter. + parse_dates : bool, default True + Parse dates. Different default from read_table. header : int, default None - Row to use as header (skip prior rows) + Row to use as header (skip prior rows). index_col : int or sequence, default 0 Column to use for index. If a sequence is given, a MultiIndex - is used. Different default from read_table - encoding : string, optional - a string representing the encoding to use if the contents are - non-ascii, for python versions prior to 3 - infer_datetime_format : boolean, default False + is used. Different default from read_table. + encoding : str, optional + A string representing the encoding to use if the contents are + non-ascii, for python versions prior to 3. + infer_datetime_format : bool, default False If True and `parse_dates` is True for a column, try to infer the datetime format based on the first datetime string. If the format can be inferred, there often will be a large parsing speed-up. Returns ------- - y : Series + Series See Also -------- @@ -4323,19 +4390,21 @@ def valid(self, inplace=False, **kwargs): def to_timestamp(self, freq=None, how='start', copy=True): """ - Cast to datetimeindex of timestamps, at *beginning* of period. + Cast to DatetimeIndex of Timestamps, at *beginning* of period. Parameters ---------- - freq : string, default frequency of PeriodIndex - Desired frequency + freq : str, default frequency of PeriodIndex + Desired frequency. how : {'s', 'e', 'start', 'end'} Convention for converting period to timestamp; start of period - vs. end + vs. end. + copy : bool, default True + Whether or not to return a copy. Returns ------- - ts : Series with DatetimeIndex + Series with DatetimeIndex """ new_values = self._values if copy: @@ -4352,11 +4421,15 @@ def to_period(self, freq=None, copy=True): Parameters ---------- - freq : string, default + freq : str, default None + Frequency associated with the PeriodIndex. + copy : bool, default True + Whether or not to return a copy. Returns ------- - ts : Series with PeriodIndex + Series + Series with index converted to PeriodIndex. """ new_values = self._values if copy: From 05351802e28e386da639d98093e7a812a4d15565 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Thu, 7 Feb 2019 04:54:47 -0800 Subject: [PATCH 074/215] REF: Add more pytest idiom to test_holiday.py (#25204) --- pandas/tests/tseries/holiday/__init__.py | 0 pandas/tests/tseries/holiday/test_calendar.py | 77 ++++ pandas/tests/tseries/holiday/test_federal.py | 36 ++ pandas/tests/tseries/holiday/test_holiday.py | 193 +++++++++ .../tests/tseries/holiday/test_observance.py | 93 +++++ pandas/tests/tseries/test_holiday.py | 382 ------------------ 6 files changed, 399 insertions(+), 382 deletions(-) create mode 100644 pandas/tests/tseries/holiday/__init__.py create mode 100644 pandas/tests/tseries/holiday/test_calendar.py create mode 100644 pandas/tests/tseries/holiday/test_federal.py create mode 100644 pandas/tests/tseries/holiday/test_holiday.py create mode 100644 pandas/tests/tseries/holiday/test_observance.py delete mode 100644 pandas/tests/tseries/test_holiday.py diff --git a/pandas/tests/tseries/holiday/__init__.py b/pandas/tests/tseries/holiday/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/pandas/tests/tseries/holiday/test_calendar.py b/pandas/tests/tseries/holiday/test_calendar.py new file mode 100644 index 0000000000000..a5cc4095ce583 --- /dev/null +++ b/pandas/tests/tseries/holiday/test_calendar.py @@ -0,0 +1,77 @@ +from datetime import datetime + +import pytest + +from pandas import DatetimeIndex +import pandas.util.testing as tm + +from pandas.tseries.holiday import ( + AbstractHolidayCalendar, Holiday, Timestamp, USFederalHolidayCalendar, + USThanksgivingDay, get_calendar) + + +@pytest.mark.parametrize("transform", [ + lambda x: x, + lambda x: x.strftime("%Y-%m-%d"), + lambda x: Timestamp(x) +]) +def test_calendar(transform): + start_date = datetime(2012, 1, 1) + end_date = datetime(2012, 12, 31) + + calendar = USFederalHolidayCalendar() + holidays = calendar.holidays(transform(start_date), transform(end_date)) + + expected = [ + datetime(2012, 1, 2), + datetime(2012, 1, 16), + datetime(2012, 2, 20), + datetime(2012, 5, 28), + datetime(2012, 7, 4), + datetime(2012, 9, 3), + datetime(2012, 10, 8), + datetime(2012, 11, 12), + datetime(2012, 11, 22), + datetime(2012, 12, 25) + ] + + assert list(holidays.to_pydatetime()) == expected + + +def test_calendar_caching(): + # see gh-9552. + + class TestCalendar(AbstractHolidayCalendar): + def __init__(self, name=None, rules=None): + super(TestCalendar, self).__init__(name=name, rules=rules) + + jan1 = TestCalendar(rules=[Holiday("jan1", year=2015, month=1, day=1)]) + jan2 = TestCalendar(rules=[Holiday("jan2", year=2015, month=1, day=2)]) + + # Getting holidays for Jan 1 should not alter results for Jan 2. + tm.assert_index_equal(jan1.holidays(), DatetimeIndex(["01-Jan-2015"])) + tm.assert_index_equal(jan2.holidays(), DatetimeIndex(["02-Jan-2015"])) + + +def test_calendar_observance_dates(): + # see gh-11477 + us_fed_cal = get_calendar("USFederalHolidayCalendar") + holidays0 = us_fed_cal.holidays(datetime(2015, 7, 3), datetime( + 2015, 7, 3)) # <-- same start and end dates + holidays1 = us_fed_cal.holidays(datetime(2015, 7, 3), datetime( + 2015, 7, 6)) # <-- different start and end dates + holidays2 = us_fed_cal.holidays(datetime(2015, 7, 3), datetime( + 2015, 7, 3)) # <-- same start and end dates + + # These should all produce the same result. + # + # In addition, calling with different start and end + # dates should not alter the output if we call the + # function again with the same start and end date. + tm.assert_index_equal(holidays0, holidays1) + tm.assert_index_equal(holidays0, holidays2) + + +def test_rule_from_name(): + us_fed_cal = get_calendar("USFederalHolidayCalendar") + assert us_fed_cal.rule_from_name("Thanksgiving") == USThanksgivingDay diff --git a/pandas/tests/tseries/holiday/test_federal.py b/pandas/tests/tseries/holiday/test_federal.py new file mode 100644 index 0000000000000..62b5ab2b849ae --- /dev/null +++ b/pandas/tests/tseries/holiday/test_federal.py @@ -0,0 +1,36 @@ +from datetime import datetime + +from pandas.tseries.holiday import ( + AbstractHolidayCalendar, USMartinLutherKingJr, USMemorialDay) + + +def test_no_mlk_before_1986(): + # see gh-10278 + class MLKCalendar(AbstractHolidayCalendar): + rules = [USMartinLutherKingJr] + + holidays = MLKCalendar().holidays(start="1984", + end="1988").to_pydatetime().tolist() + + # Testing to make sure holiday is not incorrectly observed before 1986. + assert holidays == [datetime(1986, 1, 20, 0, 0), + datetime(1987, 1, 19, 0, 0)] + + +def test_memorial_day(): + class MemorialDay(AbstractHolidayCalendar): + rules = [USMemorialDay] + + holidays = MemorialDay().holidays(start="1971", + end="1980").to_pydatetime().tolist() + + # Fixes 5/31 error and checked manually against Wikipedia. + assert holidays == [datetime(1971, 5, 31, 0, 0), + datetime(1972, 5, 29, 0, 0), + datetime(1973, 5, 28, 0, 0), + datetime(1974, 5, 27, 0, 0), + datetime(1975, 5, 26, 0, 0), + datetime(1976, 5, 31, 0, 0), + datetime(1977, 5, 30, 0, 0), + datetime(1978, 5, 29, 0, 0), + datetime(1979, 5, 28, 0, 0)] diff --git a/pandas/tests/tseries/holiday/test_holiday.py b/pandas/tests/tseries/holiday/test_holiday.py new file mode 100644 index 0000000000000..27bba1cc89dee --- /dev/null +++ b/pandas/tests/tseries/holiday/test_holiday.py @@ -0,0 +1,193 @@ +from datetime import datetime + +import pytest +from pytz import utc + +import pandas.util.testing as tm + +from pandas.tseries.holiday import ( + MO, SA, AbstractHolidayCalendar, DateOffset, EasterMonday, GoodFriday, + Holiday, HolidayCalendarFactory, Timestamp, USColumbusDay, USLaborDay, + USMartinLutherKingJr, USMemorialDay, USPresidentsDay, USThanksgivingDay, + get_calendar, next_monday) + + +def _check_holiday_results(holiday, start, end, expected): + """ + Check that the dates for a given holiday match in date and timezone. + + Parameters + ---------- + holiday : Holiday + The holiday to check. + start : datetime-like + The start date of range in which to collect dates for a given holiday. + end : datetime-like + The end date of range in which to collect dates for a given holiday. + expected : list + The list of dates we expect to get. + """ + assert list(holiday.dates(start, end)) == expected + + # Verify that timezone info is preserved. + assert (list(holiday.dates(utc.localize(Timestamp(start)), + utc.localize(Timestamp(end)))) == + [utc.localize(dt) for dt in expected]) + + +@pytest.mark.parametrize("holiday,start_date,end_date,expected", [ + (USMemorialDay, datetime(2011, 1, 1), datetime(2020, 12, 31), + [datetime(2011, 5, 30), datetime(2012, 5, 28), datetime(2013, 5, 27), + datetime(2014, 5, 26), datetime(2015, 5, 25), datetime(2016, 5, 30), + datetime(2017, 5, 29), datetime(2018, 5, 28), datetime(2019, 5, 27), + datetime(2020, 5, 25)]), + + (Holiday("July 4th Eve", month=7, day=3), "2001-01-01", "2003-03-03", + [Timestamp("2001-07-03 00:00:00"), Timestamp("2002-07-03 00:00:00")]), + (Holiday("July 4th Eve", month=7, day=3, days_of_week=(0, 1, 2, 3)), + "2001-01-01", "2008-03-03", [ + Timestamp("2001-07-03 00:00:00"), Timestamp("2002-07-03 00:00:00"), + Timestamp("2003-07-03 00:00:00"), Timestamp("2006-07-03 00:00:00"), + Timestamp("2007-07-03 00:00:00")]), + + (EasterMonday, datetime(2011, 1, 1), datetime(2020, 12, 31), + [Timestamp("2011-04-25 00:00:00"), Timestamp("2012-04-09 00:00:00"), + Timestamp("2013-04-01 00:00:00"), Timestamp("2014-04-21 00:00:00"), + Timestamp("2015-04-06 00:00:00"), Timestamp("2016-03-28 00:00:00"), + Timestamp("2017-04-17 00:00:00"), Timestamp("2018-04-02 00:00:00"), + Timestamp("2019-04-22 00:00:00"), Timestamp("2020-04-13 00:00:00")]), + (GoodFriday, datetime(2011, 1, 1), datetime(2020, 12, 31), + [Timestamp("2011-04-22 00:00:00"), Timestamp("2012-04-06 00:00:00"), + Timestamp("2013-03-29 00:00:00"), Timestamp("2014-04-18 00:00:00"), + Timestamp("2015-04-03 00:00:00"), Timestamp("2016-03-25 00:00:00"), + Timestamp("2017-04-14 00:00:00"), Timestamp("2018-03-30 00:00:00"), + Timestamp("2019-04-19 00:00:00"), Timestamp("2020-04-10 00:00:00")]), + + (USThanksgivingDay, datetime(2011, 1, 1), datetime(2020, 12, 31), + [datetime(2011, 11, 24), datetime(2012, 11, 22), datetime(2013, 11, 28), + datetime(2014, 11, 27), datetime(2015, 11, 26), datetime(2016, 11, 24), + datetime(2017, 11, 23), datetime(2018, 11, 22), datetime(2019, 11, 28), + datetime(2020, 11, 26)]) +]) +def test_holiday_dates(holiday, start_date, end_date, expected): + _check_holiday_results(holiday, start_date, end_date, expected) + + +@pytest.mark.parametrize("holiday,start,expected", [ + (USMemorialDay, datetime(2015, 7, 1), []), + (USMemorialDay, "2015-05-25", "2015-05-25"), + + (USLaborDay, datetime(2015, 7, 1), []), + (USLaborDay, "2015-09-07", "2015-09-07"), + + (USColumbusDay, datetime(2015, 7, 1), []), + (USColumbusDay, "2015-10-12", "2015-10-12"), + + (USThanksgivingDay, datetime(2015, 7, 1), []), + (USThanksgivingDay, "2015-11-26", "2015-11-26"), + + (USMartinLutherKingJr, datetime(2015, 7, 1), []), + (USMartinLutherKingJr, "2015-01-19", "2015-01-19"), + + (USPresidentsDay, datetime(2015, 7, 1), []), + (USPresidentsDay, "2015-02-16", "2015-02-16"), + + (GoodFriday, datetime(2015, 7, 1), []), + (GoodFriday, "2015-04-03", "2015-04-03"), + + (EasterMonday, "2015-04-06", "2015-04-06"), + (EasterMonday, datetime(2015, 7, 1), []), + (EasterMonday, "2015-04-05", []), + + ("New Years Day", "2015-01-01", "2015-01-01"), + ("New Years Day", "2010-12-31", "2010-12-31"), + ("New Years Day", datetime(2015, 7, 1), []), + ("New Years Day", "2011-01-01", []), + + ("July 4th", "2015-07-03", "2015-07-03"), + ("July 4th", datetime(2015, 7, 1), []), + ("July 4th", "2015-07-04", []), + + ("Veterans Day", "2012-11-12", "2012-11-12"), + ("Veterans Day", datetime(2015, 7, 1), []), + ("Veterans Day", "2012-11-11", []), + + ("Christmas", "2011-12-26", "2011-12-26"), + ("Christmas", datetime(2015, 7, 1), []), + ("Christmas", "2011-12-25", []), +]) +def test_holidays_within_dates(holiday, start, expected): + # see gh-11477 + # + # Fix holiday behavior where holiday.dates returned dates outside + # start/end date, or observed rules could not be applied because the + # holiday was not in the original date range (e.g., 7/4/2015 -> 7/3/2015). + if isinstance(holiday, str): + calendar = get_calendar("USFederalHolidayCalendar") + holiday = calendar.rule_from_name(holiday) + + if isinstance(expected, str): + expected = [Timestamp(expected)] + + _check_holiday_results(holiday, start, start, expected) + + +@pytest.mark.parametrize("transform", [ + lambda x: x.strftime("%Y-%m-%d"), + lambda x: Timestamp(x) +]) +def test_argument_types(transform): + start_date = datetime(2011, 1, 1) + end_date = datetime(2020, 12, 31) + + holidays = USThanksgivingDay.dates(start_date, end_date) + holidays2 = USThanksgivingDay.dates( + transform(start_date), transform(end_date)) + tm.assert_index_equal(holidays, holidays2) + + +@pytest.mark.parametrize("name,kwargs", [ + ("One-Time", dict(year=2012, month=5, day=28)), + ("Range", dict(month=5, day=28, start_date=datetime(2012, 1, 1), + end_date=datetime(2012, 12, 31), + offset=DateOffset(weekday=MO(1)))) +]) +def test_special_holidays(name, kwargs): + base_date = [datetime(2012, 5, 28)] + holiday = Holiday(name, **kwargs) + + start_date = datetime(2011, 1, 1) + end_date = datetime(2020, 12, 31) + + assert base_date == holiday.dates(start_date, end_date) + + +def test_get_calendar(): + class TestCalendar(AbstractHolidayCalendar): + rules = [] + + calendar = get_calendar("TestCalendar") + assert TestCalendar == calendar.__class__ + + +def test_factory(): + class_1 = HolidayCalendarFactory("MemorialDay", + AbstractHolidayCalendar, + USMemorialDay) + class_2 = HolidayCalendarFactory("Thanksgiving", + AbstractHolidayCalendar, + USThanksgivingDay) + class_3 = HolidayCalendarFactory("Combined", class_1, class_2) + + assert len(class_1.rules) == 1 + assert len(class_2.rules) == 1 + assert len(class_3.rules) == 2 + + +def test_both_offset_observance_raises(): + # see gh-10217 + msg = "Cannot use both offset and observance" + with pytest.raises(NotImplementedError, match=msg): + Holiday("Cyber Monday", month=11, day=1, + offset=[DateOffset(weekday=SA(4))], + observance=next_monday) diff --git a/pandas/tests/tseries/holiday/test_observance.py b/pandas/tests/tseries/holiday/test_observance.py new file mode 100644 index 0000000000000..1c22918b2efd8 --- /dev/null +++ b/pandas/tests/tseries/holiday/test_observance.py @@ -0,0 +1,93 @@ +from datetime import datetime + +import pytest + +from pandas.tseries.holiday import ( + after_nearest_workday, before_nearest_workday, nearest_workday, + next_monday, next_monday_or_tuesday, next_workday, previous_friday, + previous_workday, sunday_to_monday, weekend_to_monday) + +_WEDNESDAY = datetime(2014, 4, 9) +_THURSDAY = datetime(2014, 4, 10) +_FRIDAY = datetime(2014, 4, 11) +_SATURDAY = datetime(2014, 4, 12) +_SUNDAY = datetime(2014, 4, 13) +_MONDAY = datetime(2014, 4, 14) +_TUESDAY = datetime(2014, 4, 15) + + +@pytest.mark.parametrize("day", [_SATURDAY, _SUNDAY]) +def test_next_monday(day): + assert next_monday(day) == _MONDAY + + +@pytest.mark.parametrize("day,expected", [ + (_SATURDAY, _MONDAY), + (_SUNDAY, _TUESDAY), + (_MONDAY, _TUESDAY) +]) +def test_next_monday_or_tuesday(day, expected): + assert next_monday_or_tuesday(day) == expected + + +@pytest.mark.parametrize("day", [_SATURDAY, _SUNDAY]) +def test_previous_friday(day): + assert previous_friday(day) == _FRIDAY + + +def test_sunday_to_monday(): + assert sunday_to_monday(_SUNDAY) == _MONDAY + + +@pytest.mark.parametrize("day,expected", [ + (_SATURDAY, _FRIDAY), + (_SUNDAY, _MONDAY), + (_MONDAY, _MONDAY) +]) +def test_nearest_workday(day, expected): + assert nearest_workday(day) == expected + + +@pytest.mark.parametrize("day,expected", [ + (_SATURDAY, _MONDAY), + (_SUNDAY, _MONDAY), + (_MONDAY, _MONDAY) +]) +def test_weekend_to_monday(day, expected): + assert weekend_to_monday(day) == expected + + +@pytest.mark.parametrize("day,expected", [ + (_SATURDAY, _MONDAY), + (_SUNDAY, _MONDAY), + (_MONDAY, _TUESDAY) +]) +def test_next_workday(day, expected): + assert next_workday(day) == expected + + +@pytest.mark.parametrize("day,expected", [ + (_SATURDAY, _FRIDAY), + (_SUNDAY, _FRIDAY), + (_TUESDAY, _MONDAY) +]) +def test_previous_workday(day, expected): + assert previous_workday(day) == expected + + +@pytest.mark.parametrize("day,expected", [ + (_SATURDAY, _THURSDAY), + (_SUNDAY, _FRIDAY), + (_TUESDAY, _MONDAY) +]) +def test_before_nearest_workday(day, expected): + assert before_nearest_workday(day) == expected + + +@pytest.mark.parametrize("day,expected", [ + (_SATURDAY, _MONDAY), + (_SUNDAY, _TUESDAY), + (_FRIDAY, _MONDAY) +]) +def test_after_nearest_workday(day, expected): + assert after_nearest_workday(day) == expected diff --git a/pandas/tests/tseries/test_holiday.py b/pandas/tests/tseries/test_holiday.py deleted file mode 100644 index 86f154ed1acc2..0000000000000 --- a/pandas/tests/tseries/test_holiday.py +++ /dev/null @@ -1,382 +0,0 @@ -from datetime import datetime - -import pytest -from pytz import utc - -from pandas import DatetimeIndex, compat -import pandas.util.testing as tm - -from pandas.tseries.holiday import ( - MO, SA, AbstractHolidayCalendar, DateOffset, EasterMonday, GoodFriday, - Holiday, HolidayCalendarFactory, Timestamp, USColumbusDay, - USFederalHolidayCalendar, USLaborDay, USMartinLutherKingJr, USMemorialDay, - USPresidentsDay, USThanksgivingDay, after_nearest_workday, - before_nearest_workday, get_calendar, nearest_workday, next_monday, - next_monday_or_tuesday, next_workday, previous_friday, previous_workday, - sunday_to_monday, weekend_to_monday) - - -class TestCalendar(object): - - def setup_method(self, method): - self.holiday_list = [ - datetime(2012, 1, 2), - datetime(2012, 1, 16), - datetime(2012, 2, 20), - datetime(2012, 5, 28), - datetime(2012, 7, 4), - datetime(2012, 9, 3), - datetime(2012, 10, 8), - datetime(2012, 11, 12), - datetime(2012, 11, 22), - datetime(2012, 12, 25)] - - self.start_date = datetime(2012, 1, 1) - self.end_date = datetime(2012, 12, 31) - - def test_calendar(self): - - calendar = USFederalHolidayCalendar() - holidays = calendar.holidays(self.start_date, self.end_date) - - holidays_1 = calendar.holidays( - self.start_date.strftime('%Y-%m-%d'), - self.end_date.strftime('%Y-%m-%d')) - holidays_2 = calendar.holidays( - Timestamp(self.start_date), - Timestamp(self.end_date)) - - assert list(holidays.to_pydatetime()) == self.holiday_list - assert list(holidays_1.to_pydatetime()) == self.holiday_list - assert list(holidays_2.to_pydatetime()) == self.holiday_list - - def test_calendar_caching(self): - # Test for issue #9552 - - class TestCalendar(AbstractHolidayCalendar): - - def __init__(self, name=None, rules=None): - super(TestCalendar, self).__init__(name=name, rules=rules) - - jan1 = TestCalendar(rules=[Holiday('jan1', year=2015, month=1, day=1)]) - jan2 = TestCalendar(rules=[Holiday('jan2', year=2015, month=1, day=2)]) - - tm.assert_index_equal(jan1.holidays(), DatetimeIndex(['01-Jan-2015'])) - tm.assert_index_equal(jan2.holidays(), DatetimeIndex(['02-Jan-2015'])) - - def test_calendar_observance_dates(self): - # Test for issue 11477 - USFedCal = get_calendar('USFederalHolidayCalendar') - holidays0 = USFedCal.holidays(datetime(2015, 7, 3), datetime( - 2015, 7, 3)) # <-- same start and end dates - holidays1 = USFedCal.holidays(datetime(2015, 7, 3), datetime( - 2015, 7, 6)) # <-- different start and end dates - holidays2 = USFedCal.holidays(datetime(2015, 7, 3), datetime( - 2015, 7, 3)) # <-- same start and end dates - - tm.assert_index_equal(holidays0, holidays1) - tm.assert_index_equal(holidays0, holidays2) - - def test_rule_from_name(self): - USFedCal = get_calendar('USFederalHolidayCalendar') - assert USFedCal.rule_from_name('Thanksgiving') == USThanksgivingDay - - -class TestHoliday(object): - - def setup_method(self, method): - self.start_date = datetime(2011, 1, 1) - self.end_date = datetime(2020, 12, 31) - - def check_results(self, holiday, start, end, expected): - assert list(holiday.dates(start, end)) == expected - - # Verify that timezone info is preserved. - assert (list(holiday.dates(utc.localize(Timestamp(start)), - utc.localize(Timestamp(end)))) == - [utc.localize(dt) for dt in expected]) - - def test_usmemorialday(self): - self.check_results(holiday=USMemorialDay, - start=self.start_date, - end=self.end_date, - expected=[ - datetime(2011, 5, 30), - datetime(2012, 5, 28), - datetime(2013, 5, 27), - datetime(2014, 5, 26), - datetime(2015, 5, 25), - datetime(2016, 5, 30), - datetime(2017, 5, 29), - datetime(2018, 5, 28), - datetime(2019, 5, 27), - datetime(2020, 5, 25), - ], ) - - def test_non_observed_holiday(self): - - self.check_results( - Holiday('July 4th Eve', month=7, day=3), - start="2001-01-01", - end="2003-03-03", - expected=[ - Timestamp('2001-07-03 00:00:00'), - Timestamp('2002-07-03 00:00:00') - ] - ) - - self.check_results( - Holiday('July 4th Eve', month=7, day=3, days_of_week=(0, 1, 2, 3)), - start="2001-01-01", - end="2008-03-03", - expected=[ - Timestamp('2001-07-03 00:00:00'), - Timestamp('2002-07-03 00:00:00'), - Timestamp('2003-07-03 00:00:00'), - Timestamp('2006-07-03 00:00:00'), - Timestamp('2007-07-03 00:00:00'), - ] - ) - - def test_easter(self): - - self.check_results(EasterMonday, - start=self.start_date, - end=self.end_date, - expected=[ - Timestamp('2011-04-25 00:00:00'), - Timestamp('2012-04-09 00:00:00'), - Timestamp('2013-04-01 00:00:00'), - Timestamp('2014-04-21 00:00:00'), - Timestamp('2015-04-06 00:00:00'), - Timestamp('2016-03-28 00:00:00'), - Timestamp('2017-04-17 00:00:00'), - Timestamp('2018-04-02 00:00:00'), - Timestamp('2019-04-22 00:00:00'), - Timestamp('2020-04-13 00:00:00'), - ], ) - self.check_results(GoodFriday, - start=self.start_date, - end=self.end_date, - expected=[ - Timestamp('2011-04-22 00:00:00'), - Timestamp('2012-04-06 00:00:00'), - Timestamp('2013-03-29 00:00:00'), - Timestamp('2014-04-18 00:00:00'), - Timestamp('2015-04-03 00:00:00'), - Timestamp('2016-03-25 00:00:00'), - Timestamp('2017-04-14 00:00:00'), - Timestamp('2018-03-30 00:00:00'), - Timestamp('2019-04-19 00:00:00'), - Timestamp('2020-04-10 00:00:00'), - ], ) - - def test_usthanksgivingday(self): - - self.check_results(USThanksgivingDay, - start=self.start_date, - end=self.end_date, - expected=[ - datetime(2011, 11, 24), - datetime(2012, 11, 22), - datetime(2013, 11, 28), - datetime(2014, 11, 27), - datetime(2015, 11, 26), - datetime(2016, 11, 24), - datetime(2017, 11, 23), - datetime(2018, 11, 22), - datetime(2019, 11, 28), - datetime(2020, 11, 26), - ], ) - - def test_holidays_within_dates(self): - # Fix holiday behavior found in #11477 - # where holiday.dates returned dates outside start/end date - # or observed rules could not be applied as the holiday - # was not in the original date range (e.g., 7/4/2015 -> 7/3/2015) - start_date = datetime(2015, 7, 1) - end_date = datetime(2015, 7, 1) - - calendar = get_calendar('USFederalHolidayCalendar') - new_years = calendar.rule_from_name('New Years Day') - july_4th = calendar.rule_from_name('July 4th') - veterans_day = calendar.rule_from_name('Veterans Day') - christmas = calendar.rule_from_name('Christmas') - - # Holiday: (start/end date, holiday) - holidays = {USMemorialDay: ("2015-05-25", "2015-05-25"), - USLaborDay: ("2015-09-07", "2015-09-07"), - USColumbusDay: ("2015-10-12", "2015-10-12"), - USThanksgivingDay: ("2015-11-26", "2015-11-26"), - USMartinLutherKingJr: ("2015-01-19", "2015-01-19"), - USPresidentsDay: ("2015-02-16", "2015-02-16"), - GoodFriday: ("2015-04-03", "2015-04-03"), - EasterMonday: [("2015-04-06", "2015-04-06"), - ("2015-04-05", [])], - new_years: [("2015-01-01", "2015-01-01"), - ("2011-01-01", []), - ("2010-12-31", "2010-12-31")], - july_4th: [("2015-07-03", "2015-07-03"), - ("2015-07-04", [])], - veterans_day: [("2012-11-11", []), - ("2012-11-12", "2012-11-12")], - christmas: [("2011-12-25", []), - ("2011-12-26", "2011-12-26")]} - - for rule, dates in compat.iteritems(holidays): - empty_dates = rule.dates(start_date, end_date) - assert empty_dates.tolist() == [] - - if isinstance(dates, tuple): - dates = [dates] - - for start, expected in dates: - if len(expected): - expected = [Timestamp(expected)] - self.check_results(rule, start, start, expected) - - def test_argument_types(self): - holidays = USThanksgivingDay.dates(self.start_date, self.end_date) - - holidays_1 = USThanksgivingDay.dates( - self.start_date.strftime('%Y-%m-%d'), - self.end_date.strftime('%Y-%m-%d')) - - holidays_2 = USThanksgivingDay.dates( - Timestamp(self.start_date), - Timestamp(self.end_date)) - - tm.assert_index_equal(holidays, holidays_1) - tm.assert_index_equal(holidays, holidays_2) - - def test_special_holidays(self): - base_date = [datetime(2012, 5, 28)] - holiday_1 = Holiday('One-Time', year=2012, month=5, day=28) - holiday_2 = Holiday('Range', month=5, day=28, - start_date=datetime(2012, 1, 1), - end_date=datetime(2012, 12, 31), - offset=DateOffset(weekday=MO(1))) - - assert base_date == holiday_1.dates(self.start_date, self.end_date) - assert base_date == holiday_2.dates(self.start_date, self.end_date) - - def test_get_calendar(self): - class TestCalendar(AbstractHolidayCalendar): - rules = [] - - calendar = get_calendar('TestCalendar') - assert TestCalendar == calendar.__class__ - - def test_factory(self): - class_1 = HolidayCalendarFactory('MemorialDay', - AbstractHolidayCalendar, - USMemorialDay) - class_2 = HolidayCalendarFactory('Thansksgiving', - AbstractHolidayCalendar, - USThanksgivingDay) - class_3 = HolidayCalendarFactory('Combined', class_1, class_2) - - assert len(class_1.rules) == 1 - assert len(class_2.rules) == 1 - assert len(class_3.rules) == 2 - - -class TestObservanceRules(object): - - def setup_method(self, method): - self.we = datetime(2014, 4, 9) - self.th = datetime(2014, 4, 10) - self.fr = datetime(2014, 4, 11) - self.sa = datetime(2014, 4, 12) - self.su = datetime(2014, 4, 13) - self.mo = datetime(2014, 4, 14) - self.tu = datetime(2014, 4, 15) - - def test_next_monday(self): - assert next_monday(self.sa) == self.mo - assert next_monday(self.su) == self.mo - - def test_next_monday_or_tuesday(self): - assert next_monday_or_tuesday(self.sa) == self.mo - assert next_monday_or_tuesday(self.su) == self.tu - assert next_monday_or_tuesday(self.mo) == self.tu - - def test_previous_friday(self): - assert previous_friday(self.sa) == self.fr - assert previous_friday(self.su) == self.fr - - def test_sunday_to_monday(self): - assert sunday_to_monday(self.su) == self.mo - - def test_nearest_workday(self): - assert nearest_workday(self.sa) == self.fr - assert nearest_workday(self.su) == self.mo - assert nearest_workday(self.mo) == self.mo - - def test_weekend_to_monday(self): - assert weekend_to_monday(self.sa) == self.mo - assert weekend_to_monday(self.su) == self.mo - assert weekend_to_monday(self.mo) == self.mo - - def test_next_workday(self): - assert next_workday(self.sa) == self.mo - assert next_workday(self.su) == self.mo - assert next_workday(self.mo) == self.tu - - def test_previous_workday(self): - assert previous_workday(self.sa) == self.fr - assert previous_workday(self.su) == self.fr - assert previous_workday(self.tu) == self.mo - - def test_before_nearest_workday(self): - assert before_nearest_workday(self.sa) == self.th - assert before_nearest_workday(self.su) == self.fr - assert before_nearest_workday(self.tu) == self.mo - - def test_after_nearest_workday(self): - assert after_nearest_workday(self.sa) == self.mo - assert after_nearest_workday(self.su) == self.tu - assert after_nearest_workday(self.fr) == self.mo - - -class TestFederalHolidayCalendar(object): - - def test_no_mlk_before_1986(self): - # see gh-10278 - class MLKCalendar(AbstractHolidayCalendar): - rules = [USMartinLutherKingJr] - - holidays = MLKCalendar().holidays(start='1984', - end='1988').to_pydatetime().tolist() - - # Testing to make sure holiday is not incorrectly observed before 1986 - assert holidays == [datetime(1986, 1, 20, 0, 0), - datetime(1987, 1, 19, 0, 0)] - - def test_memorial_day(self): - class MemorialDay(AbstractHolidayCalendar): - rules = [USMemorialDay] - - holidays = MemorialDay().holidays(start='1971', - end='1980').to_pydatetime().tolist() - - # Fixes 5/31 error and checked manually against Wikipedia - assert holidays == [datetime(1971, 5, 31, 0, 0), - datetime(1972, 5, 29, 0, 0), - datetime(1973, 5, 28, 0, 0), - datetime(1974, 5, 27, 0, 0), - datetime(1975, 5, 26, 0, 0), - datetime(1976, 5, 31, 0, 0), - datetime(1977, 5, 30, 0, 0), - datetime(1978, 5, 29, 0, 0), - datetime(1979, 5, 28, 0, 0)] - - -class TestHolidayConflictingArguments(object): - - def test_both_offset_observance_raises(self): - # see gh-10217 - with pytest.raises(NotImplementedError): - Holiday("Cyber Monday", month=11, day=1, - offset=[DateOffset(weekday=SA(4))], - observance=next_monday) From 683c7b55f5195fdf4f524239066cbf6f1301f0e7 Mon Sep 17 00:00:00 2001 From: Kara de la Marck Date: Thu, 7 Feb 2019 12:56:42 +0000 Subject: [PATCH 075/215] DOC: Fix validation type error SA05 (#25208) Create check for SA05 errors in CI --- ci/code_checks.sh | 4 +-- pandas/core/algorithms.py | 4 +-- pandas/core/arrays/categorical.py | 2 +- pandas/core/computation/eval.py | 6 ++-- pandas/core/dtypes/base.py | 6 ++-- pandas/core/dtypes/dtypes.py | 2 +- pandas/core/dtypes/inference.py | 2 +- pandas/core/frame.py | 48 +++++++++++++++---------------- pandas/core/generic.py | 20 ++++++------- pandas/core/groupby/groupby.py | 12 ++++---- pandas/core/indexes/base.py | 28 +++++++++--------- pandas/core/indexes/datetimes.py | 8 +++--- pandas/core/resample.py | 14 ++++----- pandas/core/reshape/tile.py | 15 +++++----- pandas/core/series.py | 2 +- pandas/core/tools/datetimes.py | 4 +-- pandas/core/tools/numeric.py | 6 ++-- pandas/core/window.py | 20 ++++++------- pandas/errors/__init__.py | 4 +-- pandas/io/formats/style.py | 2 +- pandas/io/gbq.py | 2 +- pandas/io/html.py | 6 ++-- pandas/io/pytables.py | 4 +-- pandas/io/stata.py | 4 +-- pandas/plotting/_core.py | 30 +++++++++---------- pandas/plotting/_misc.py | 6 ++-- pandas/tseries/frequencies.py | 2 +- 27 files changed, 131 insertions(+), 132 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index d16249724127f..2bd6aa2f9c7a5 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -240,8 +240,8 @@ fi ### DOCSTRINGS ### if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then - MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, EX04, RT04, SS05)' ; echo $MSG - $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR05,EX04,RT04,SS05 + MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, EX04, RT04, SS05, SA05)' ; echo $MSG + $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR05,EX04,RT04,SS05,SA05 RET=$(($RET + $?)) ; echo $MSG "DONE" fi diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index 02f33c49c79ae..a70a3ff06f202 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -295,8 +295,8 @@ def unique(values): See Also -------- - pandas.Index.unique - pandas.Series.unique + Index.unique + Series.unique Examples -------- diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index e26f6cb03a768..52fc7d259f39c 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -276,7 +276,7 @@ class Categorical(ExtensionArray, PandasObject): See Also -------- - pandas.api.types.CategoricalDtype : Type for categorical data. + api.types.CategoricalDtype : Type for categorical data. CategoricalIndex : An Index with an underlying ``Categorical``. Notes diff --git a/pandas/core/computation/eval.py b/pandas/core/computation/eval.py index b768ed6df303e..23c3e0eaace81 100644 --- a/pandas/core/computation/eval.py +++ b/pandas/core/computation/eval.py @@ -205,7 +205,7 @@ def eval(expr, parser='pandas', engine=None, truediv=True, A list of objects implementing the ``__getitem__`` special method that you can use to inject an additional collection of namespaces to use for variable lookup. For example, this is used in the - :meth:`~pandas.DataFrame.query` method to inject the + :meth:`~DataFrame.query` method to inject the ``DataFrame.index`` and ``DataFrame.columns`` variables that refer to their respective :class:`~pandas.DataFrame` instance attributes. @@ -248,8 +248,8 @@ def eval(expr, parser='pandas', engine=None, truediv=True, See Also -------- - pandas.DataFrame.query - pandas.DataFrame.eval + DataFrame.query + DataFrame.eval Notes ----- diff --git a/pandas/core/dtypes/base.py b/pandas/core/dtypes/base.py index ab1cb9cf2499a..88bbdcf342d66 100644 --- a/pandas/core/dtypes/base.py +++ b/pandas/core/dtypes/base.py @@ -153,8 +153,8 @@ class ExtensionDtype(_DtypeOpsMixin): See Also -------- - pandas.api.extensions.register_extension_dtype - pandas.api.extensions.ExtensionArray + extensions.register_extension_dtype + extensions.ExtensionArray Notes ----- @@ -173,7 +173,7 @@ class ExtensionDtype(_DtypeOpsMixin): Optionally one can override construct_array_type for construction with the name of this dtype via the Registry. See - :meth:`pandas.api.extensions.register_extension_dtype`. + :meth:`extensions.register_extension_dtype`. * construct_array_type diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index ac38b2fbbe957..f9eea8c63cfa9 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -195,7 +195,7 @@ class CategoricalDtype(PandasExtensionDtype, ExtensionDtype): See Also -------- - pandas.Categorical + Categorical Notes ----- diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index b11542622451c..92972c83cea53 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -44,7 +44,7 @@ def is_number(obj): See Also -------- - pandas.api.types.is_integer: Checks a subgroup of numbers. + api.types.is_integer: Checks a subgroup of numbers. Examples -------- diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 9c2f000c26c79..19da8ba5c547d 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -320,7 +320,7 @@ class DataFrame(NDFrame): DataFrame.from_records : Constructor from tuples, also record arrays. DataFrame.from_dict : From dicts of Series, arrays, or dicts. DataFrame.from_items : From sequence of (key, value) pairs - pandas.read_csv, pandas.read_table, pandas.read_clipboard. + read_csv, pandas.read_table, pandas.read_clipboard. Examples -------- @@ -735,7 +735,7 @@ def style(self): See Also -------- - pandas.io.formats.style.Styler + io.formats.style.Styler """ from pandas.io.formats.style import Styler return Styler(self) @@ -1417,7 +1417,7 @@ def to_gbq(self, destination_table, project_id=None, chunksize=None, See Also -------- pandas_gbq.to_gbq : This function in the pandas-gbq library. - pandas.read_gbq : Read a DataFrame from Google BigQuery. + read_gbq : Read a DataFrame from Google BigQuery. """ from pandas.io import gbq return gbq.to_gbq( @@ -1843,14 +1843,14 @@ def from_csv(cls, path, header=0, sep=',', index_col=0, parse_dates=True, Read CSV file. .. deprecated:: 0.21.0 - Use :func:`pandas.read_csv` instead. + Use :func:`read_csv` instead. - It is preferable to use the more powerful :func:`pandas.read_csv` + It is preferable to use the more powerful :func:`read_csv` for most general purposes, but ``from_csv`` makes for an easy roundtrip to and from a file (the exact counterpart of ``to_csv``), especially with a DataFrame of time series data. - This method only differs from the preferred :func:`pandas.read_csv` + This method only differs from the preferred :func:`read_csv` in some defaults: - `index_col` is ``0`` instead of ``None`` (take first column as index @@ -1887,7 +1887,7 @@ def from_csv(cls, path, header=0, sep=',', index_col=0, parse_dates=True, See Also -------- - pandas.read_csv + read_csv """ warnings.warn("from_csv is deprecated. Please use read_csv(...) " @@ -2504,7 +2504,7 @@ def memory_usage(self, index=True, deep=False): numpy.ndarray.nbytes : Total bytes consumed by the elements of an ndarray. Series.memory_usage : Bytes consumed by a Series. - pandas.Categorical : Memory-efficient array for string values with + Categorical : Memory-efficient array for string values with many repeated values. DataFrame.info : Concise summary of a DataFrame. @@ -2987,7 +2987,7 @@ def query(self, expr, inplace=False, **kwargs): Whether the query should modify the data in place or return a modified copy. **kwargs - See the documentation for :func:`pandas.eval` for complete details + See the documentation for :func:`eval` for complete details on the keyword arguments accepted by :meth:`DataFrame.query`. .. versionadded:: 0.18.0 @@ -3011,7 +3011,7 @@ def query(self, expr, inplace=False, **kwargs): multidimensional key (e.g., a DataFrame) then the result will be passed to :meth:`DataFrame.__getitem__`. - This method uses the top-level :func:`pandas.eval` function to + This method uses the top-level :func:`eval` function to evaluate the passed query. The :meth:`~pandas.DataFrame.query` method uses a slightly @@ -3098,7 +3098,7 @@ def eval(self, expr, inplace=False, **kwargs): .. versionadded:: 0.18.0. kwargs : dict - See the documentation for :func:`~pandas.eval` for complete details + See the documentation for :func:`eval` for complete details on the keyword arguments accepted by :meth:`~pandas.DataFrame.query`. @@ -3113,12 +3113,12 @@ def eval(self, expr, inplace=False, **kwargs): of a frame. DataFrame.assign : Can evaluate an expression or function to create new values for a column. - pandas.eval : Evaluate a Python expression as a string using various + eval : Evaluate a Python expression as a string using various backends. Notes ----- - For more details see the API documentation for :func:`~pandas.eval`. + For more details see the API documentation for :func:`~eval`. For detailed examples see :ref:`enhancing performance with eval `. @@ -3957,7 +3957,7 @@ def rename(self, *args, **kwargs): See Also -------- - pandas.DataFrame.rename_axis + DataFrame.rename_axis Examples -------- @@ -6203,11 +6203,11 @@ def _gotitem(self, -------- DataFrame.apply : Perform any type of operations. DataFrame.transform : Perform transformation type operations. - pandas.core.groupby.GroupBy : Perform operations over groups. - pandas.core.resample.Resampler : Perform operations over resampled bins. - pandas.core.window.Rolling : Perform operations over rolling window. - pandas.core.window.Expanding : Perform operations over expanding window. - pandas.core.window.EWM : Perform operation over exponential weighted + core.groupby.GroupBy : Perform operations over groups. + core.resample.Resampler : Perform operations over resampled bins. + core.window.Rolling : Perform operations over rolling window. + core.window.Expanding : Perform operations over expanding window. + core.window.EWM : Perform operation over exponential weighted window. """) @@ -6559,7 +6559,7 @@ def append(self, other, ignore_index=False, See Also -------- - pandas.concat : General function to concatenate DataFrame, Series + concat : General function to concatenate DataFrame, Series or Panel objects. Notes @@ -7069,10 +7069,10 @@ def cov(self, min_periods=None): See Also -------- - pandas.Series.cov : Compute covariance with another Series. - pandas.core.window.EWM.cov: Exponential weighted sample covariance. - pandas.core.window.Expanding.cov : Expanding sample covariance. - pandas.core.window.Rolling.cov : Rolling sample covariance. + Series.cov : Compute covariance with another Series. + core.window.EWM.cov: Exponential weighted sample covariance. + core.window.Expanding.cov : Expanding sample covariance. + core.window.Rolling.cov : Rolling sample covariance. Notes ----- diff --git a/pandas/core/generic.py b/pandas/core/generic.py index d03d78bd9f18c..bb5c0e49e4dd1 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -986,7 +986,7 @@ def rename(self, *args, **kwargs): See Also -------- - pandas.NDFrame.rename_axis + NDFrame.rename_axis Examples -------- @@ -1861,8 +1861,8 @@ def empty(self): See Also -------- - pandas.Series.dropna - pandas.DataFrame.dropna + Series.dropna + DataFrame.dropna Notes ----- @@ -5272,8 +5272,8 @@ def values(self): See Also -------- DataFrame.to_numpy : Recommended alternative to this method. - pandas.DataFrame.index : Retrieve the index labels. - pandas.DataFrame.columns : Retrieving the column names. + DataFrame.index : Retrieve the index labels. + DataFrame.columns : Retrieving the column names. Notes ----- @@ -5344,7 +5344,7 @@ def get_values(self): Return an ndarray after converting sparse values to dense. This is the same as ``.values`` for non-sparse data. For sparse - data contained in a `pandas.SparseArray`, the data are first + data contained in a `SparseArray`, the data are first converted to a dense representation. Returns @@ -5355,7 +5355,7 @@ def get_values(self): See Also -------- values : Numpy representation of DataFrame. - pandas.SparseArray : Container for sparse data. + SparseArray : Container for sparse data. Examples -------- @@ -5476,7 +5476,7 @@ def dtypes(self): See Also -------- - pandas.DataFrame.ftypes : Dtype and sparsity information. + DataFrame.ftypes : Dtype and sparsity information. Examples -------- @@ -5512,8 +5512,8 @@ def ftypes(self): See Also -------- - pandas.DataFrame.dtypes: Series with just dtype information. - pandas.SparseDataFrame : Container for sparse tabular data. + DataFrame.dtypes: Series with just dtype information. + SparseDataFrame : Container for sparse tabular data. Notes ----- diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index bfb5ba4fc8c90..c8f1a75b2eff5 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -44,9 +44,9 @@ class providing the base-class of operations. _common_see_also = """ See Also -------- - pandas.Series.%(name)s - pandas.DataFrame.%(name)s - pandas.Panel.%(name)s + Series.%(name)s + DataFrame.%(name)s + Panel.%(name)s """ _apply_docs = dict( @@ -206,8 +206,8 @@ class providing the base-class of operations. See Also -------- -pandas.Series.pipe : Apply a function with arguments to a series. -pandas.DataFrame.pipe: Apply a function with arguments to a dataframe. +Series.pipe : Apply a function with arguments to a series. +DataFrame.pipe: Apply a function with arguments to a dataframe. apply : Apply function to each group instead of to the full %(klass)s object. @@ -1351,7 +1351,7 @@ def resample(self, rule, *args, **kwargs): See Also -------- - pandas.Grouper : Specify a frequency to resample with when + Grouper : Specify a frequency to resample with when grouping by a key. DatetimeIndex.resample : Frequency conversion and resampling of time series. diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 664bf3a4040d8..cf813f4c3030b 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1832,9 +1832,9 @@ def isna(self): See Also -------- - pandas.Index.notna : Boolean inverse of isna. - pandas.Index.dropna : Omit entries with missing values. - pandas.isna : Top-level isna. + Index.notna : Boolean inverse of isna. + Index.dropna : Omit entries with missing values. + isna : Top-level isna. Series.isna : Detect missing values in Series object. Examples @@ -1892,7 +1892,7 @@ def notna(self): -------- Index.notnull : Alias of notna. Index.isna: Inverse of notna. - pandas.notna : Top-level notna. + notna : Top-level notna. Examples -------- @@ -2074,9 +2074,9 @@ def duplicated(self, keep='first'): See Also -------- - pandas.Series.duplicated : Equivalent method on pandas.Series. - pandas.DataFrame.duplicated : Equivalent method on pandas.DataFrame. - pandas.Index.drop_duplicates : Remove duplicate values from Index. + Series.duplicated : Equivalent method on pandas.Series. + DataFrame.duplicated : Equivalent method on pandas.DataFrame. + Index.drop_duplicates : Remove duplicate values from Index. Examples -------- @@ -4204,8 +4204,8 @@ def sort_values(self, return_indexer=False, ascending=True): See Also -------- - pandas.Series.sort_values : Sort values of a Series. - pandas.DataFrame.sort_values : Sort values in a DataFrame. + Series.sort_values : Sort values of a Series. + DataFrame.sort_values : Sort values in a DataFrame. Examples -------- @@ -5177,9 +5177,9 @@ def _add_logical_methods(cls): See Also -------- - pandas.Index.any : Return whether any element in an Index is True. - pandas.Series.any : Return whether any element in a Series is True. - pandas.Series.all : Return whether all elements in a Series are True. + Index.any : Return whether any element in an Index is True. + Series.any : Return whether any element in a Series is True. + Series.all : Return whether all elements in a Series are True. Notes ----- @@ -5217,8 +5217,8 @@ def _add_logical_methods(cls): See Also -------- - pandas.Index.all : Return whether all elements are True. - pandas.Series.all : Return whether all elements are True. + Index.all : Return whether all elements are True. + Series.all : Return whether all elements are True. Notes ----- diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index f34eb75d352a1..df91c71cfe238 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1413,10 +1413,10 @@ def date_range(start=None, end=None, periods=None, freq=None, tz=None, See Also -------- - pandas.DatetimeIndex : An immutable container for datetimes. - pandas.timedelta_range : Return a fixed frequency TimedeltaIndex. - pandas.period_range : Return a fixed frequency PeriodIndex. - pandas.interval_range : Return a fixed frequency IntervalIndex. + DatetimeIndex : An immutable container for datetimes. + timedelta_range : Return a fixed frequency TimedeltaIndex. + period_range : Return a fixed frequency PeriodIndex. + interval_range : Return a fixed frequency IntervalIndex. Notes ----- diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 42ca488572e36..e26176cffc66d 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -213,9 +213,9 @@ def pipe(self, func, *args, **kwargs): _agg_see_also_doc = dedent(""" See Also -------- - pandas.DataFrame.groupby.aggregate - pandas.DataFrame.resample.transform - pandas.DataFrame.aggregate + DataFrame.groupby.aggregate + DataFrame.resample.transform + DataFrame.aggregate """) _agg_examples_doc = dedent(""" @@ -517,9 +517,9 @@ def backfill(self, limit=None): 'backfill'. nearest : Fill NaN values with nearest neighbor starting from center. pad : Forward fill NaN values. - pandas.Series.fillna : Fill NaN values in the Series using the + Series.fillna : Fill NaN values in the Series using the specified method, which can be 'backfill'. - pandas.DataFrame.fillna : Fill NaN values in the DataFrame using the + DataFrame.fillna : Fill NaN values in the DataFrame using the specified method, which can be 'backfill'. References @@ -630,9 +630,9 @@ def fillna(self, method, limit=None): nearest : Fill NaN values in the resampled data with nearest neighbor starting from center. interpolate : Fill NaN values using interpolation. - pandas.Series.fillna : Fill NaN values in the Series using the + Series.fillna : Fill NaN values in the Series using the specified method, which can be 'bfill' and 'ffill'. - pandas.DataFrame.fillna : Fill NaN values in the DataFrame using the + DataFrame.fillna : Fill NaN values in the DataFrame using the specified method, which can be 'bfill' and 'ffill'. References diff --git a/pandas/core/reshape/tile.py b/pandas/core/reshape/tile.py index c107ed51226b0..7ad2549bd22c3 100644 --- a/pandas/core/reshape/tile.py +++ b/pandas/core/reshape/tile.py @@ -35,7 +35,7 @@ def cut(x, bins, right=True, labels=None, retbins=False, precision=3, ---------- x : array-like The input array to be binned. Must be 1-dimensional. - bins : int, sequence of scalars, or pandas.IntervalIndex + bins : int, sequence of scalars, or IntervalIndex The criteria to bin by. * int : Defines the number of equal-width bins in the range of `x`. The @@ -70,16 +70,16 @@ def cut(x, bins, right=True, labels=None, retbins=False, precision=3, Returns ------- - out : pandas.Categorical, Series, or ndarray + out : Categorical, Series, or ndarray An array-like object representing the respective bin for each value of `x`. The type depends on the value of `labels`. * True (default) : returns a Series for Series `x` or a - pandas.Categorical for all other inputs. The values stored within + Categorical for all other inputs. The values stored within are Interval dtype. * sequence of scalars : returns a Series for Series `x` or a - pandas.Categorical for all other inputs. The values stored within + Categorical for all other inputs. The values stored within are whatever the type in the sequence is. * False : returns an ndarray of integers. @@ -94,16 +94,15 @@ def cut(x, bins, right=True, labels=None, retbins=False, precision=3, -------- qcut : Discretize variable into equal-sized buckets based on rank or based on sample quantiles. - pandas.Categorical : Array type for storing data that come from a + Categorical : Array type for storing data that come from a fixed set of values. Series : One-dimensional array with axis labels (including time series). - pandas.IntervalIndex : Immutable Index implementing an ordered, - sliceable set. + IntervalIndex : Immutable Index implementing an ordered, sliceable set. Notes ----- Any NA values will be NA in the result. Out of bounds values will be NA in - the resulting Series or pandas.Categorical object. + the resulting Series or Categorical object. Examples -------- diff --git a/pandas/core/series.py b/pandas/core/series.py index 0b90146d3a548..b2011fdcdee98 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -693,7 +693,7 @@ def __array__(self, dtype=None): See Also -------- - pandas.array : Create a new array from data. + array : Create a new array from data. Series.array : Zero-copy view to the array backing the Series. Series.to_numpy : Series method for similar behavior. diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py index e6478da400d76..3da349c570274 100644 --- a/pandas/core/tools/datetimes.py +++ b/pandas/core/tools/datetimes.py @@ -497,8 +497,8 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, See Also -------- - pandas.DataFrame.astype : Cast argument to a specified dtype. - pandas.to_timedelta : Convert argument to timedelta. + DataFrame.astype : Cast argument to a specified dtype. + to_timedelta : Convert argument to timedelta. Examples -------- diff --git a/pandas/core/tools/numeric.py b/pandas/core/tools/numeric.py index 24f3e6753e500..bfe78b9296c40 100644 --- a/pandas/core/tools/numeric.py +++ b/pandas/core/tools/numeric.py @@ -63,9 +63,9 @@ def to_numeric(arg, errors='raise', downcast=None): See Also -------- - pandas.DataFrame.astype : Cast argument to a specified dtype. - pandas.to_datetime : Convert argument to datetime. - pandas.to_timedelta : Convert argument to timedelta. + DataFrame.astype : Cast argument to a specified dtype. + to_datetime : Convert argument to datetime. + to_timedelta : Convert argument to timedelta. numpy.ndarray.astype : Cast a numpy array to a specified type. Examples diff --git a/pandas/core/window.py b/pandas/core/window.py index 060226d94cca0..5f3ea7db53d09 100644 --- a/pandas/core/window.py +++ b/pandas/core/window.py @@ -900,9 +900,9 @@ class _Rolling_and_Expanding(_Rolling): See Also -------- - pandas.Series.%(name)s : Calling object with Series data. - pandas.DataFrame.%(name)s : Calling object with DataFrames. - pandas.DataFrame.count : Count of the full DataFrame. + Series.%(name)s : Calling object with Series data. + DataFrame.%(name)s : Calling object with DataFrames. + DataFrame.count : Count of the full DataFrame. Examples -------- @@ -1322,9 +1322,9 @@ def kurt(self, **kwargs): See Also -------- - pandas.Series.quantile : Computes value at the given quantile over all data + Series.quantile : Computes value at the given quantile over all data in Series. - pandas.DataFrame.quantile : Computes values at the given quantile over + DataFrame.quantile : Computes values at the given quantile over requested axis in DataFrame. Examples @@ -1626,8 +1626,8 @@ def _validate_freq(self): _agg_see_also_doc = dedent(""" See Also -------- - pandas.Series.rolling - pandas.DataFrame.rolling + Series.rolling + DataFrame.rolling """) _agg_examples_doc = dedent(""" @@ -1916,9 +1916,9 @@ def _get_window(self, other=None): _agg_see_also_doc = dedent(""" See Also -------- - pandas.DataFrame.expanding.aggregate - pandas.DataFrame.rolling.aggregate - pandas.DataFrame.aggregate + DataFrame.expanding.aggregate + DataFrame.rolling.aggregate + DataFrame.aggregate """) _agg_examples_doc = dedent(""" diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index eb6a4674a7497..c57d27ff03ac6 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -45,8 +45,8 @@ class DtypeWarning(Warning): See Also -------- - pandas.read_csv : Read CSV (comma-separated) file into a DataFrame. - pandas.read_table : Read general delimited file into a DataFrame. + read_csv : Read CSV (comma-separated) file into a DataFrame. + read_table : Read general delimited file into a DataFrame. Notes ----- diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 790cff95827fc..d241528d9779b 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -81,7 +81,7 @@ class Styler(object): See Also -------- - pandas.DataFrame.style + DataFrame.style Notes ----- diff --git a/pandas/io/gbq.py b/pandas/io/gbq.py index 639b68d433ac6..a6cec7ea8fb16 100644 --- a/pandas/io/gbq.py +++ b/pandas/io/gbq.py @@ -127,7 +127,7 @@ def read_gbq(query, project_id=None, index_col=None, col_order=None, See Also -------- pandas_gbq.read_gbq : This function in the pandas-gbq library. - pandas.DataFrame.to_gbq : Write a DataFrame to Google BigQuery. + DataFrame.to_gbq : Write a DataFrame to Google BigQuery. """ pandas_gbq = _try_import() diff --git a/pandas/io/html.py b/pandas/io/html.py index 74934740a6957..347bb3eec54af 100644 --- a/pandas/io/html.py +++ b/pandas/io/html.py @@ -988,7 +988,7 @@ def read_html(io, match='.+', flavor=None, header=None, index_col=None, latest information on table attributes for the modern web. parse_dates : bool, optional - See :func:`~pandas.read_csv` for more details. + See :func:`~read_csv` for more details. tupleize_cols : bool, optional If ``False`` try to parse multiple header rows into a @@ -1043,7 +1043,7 @@ def read_html(io, match='.+', flavor=None, header=None, index_col=None, See Also -------- - pandas.read_csv + read_csv Notes ----- @@ -1066,7 +1066,7 @@ def read_html(io, match='.+', flavor=None, header=None, index_col=None, .. versionadded:: 0.21.0 - Similar to :func:`~pandas.read_csv` the `header` argument is applied + Similar to :func:`~read_csv` the `header` argument is applied **after** `skiprows` is applied. This function will *always* return a list of :class:`DataFrame` *or* diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index 00fa01bb23c8c..2813d722246bd 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -326,8 +326,8 @@ def read_hdf(path_or_buf, key=None, mode='r', **kwargs): See Also -------- - pandas.DataFrame.to_hdf : Write a HDF file from a DataFrame. - pandas.HDFStore : Low-level access to HDF files. + DataFrame.to_hdf : Write a HDF file from a DataFrame. + HDFStore : Low-level access to HDF files. Examples -------- diff --git a/pandas/io/stata.py b/pandas/io/stata.py index 9ba3290c0b51a..62a9dbdc4657e 100644 --- a/pandas/io/stata.py +++ b/pandas/io/stata.py @@ -100,8 +100,8 @@ See Also -------- -pandas.io.stata.StataReader : Low-level reader for Stata data files. -pandas.DataFrame.to_stata: Export Stata data files. +io.stata.StataReader : Low-level reader for Stata data files. +DataFrame.to_stata: Export Stata data files. Examples -------- diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index 85549bafa8dc0..1790c70a3c8ac 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -2957,7 +2957,7 @@ def line(self, x=None, y=None, **kwds): Either the location or the label of the columns to be used. By default, it will use the remaining DataFrame numeric columns. **kwds - Keyword arguments to pass on to :meth:`pandas.DataFrame.plot`. + Keyword arguments to pass on to :meth:`DataFrame.plot`. Returns ------- @@ -3022,7 +3022,7 @@ def bar(self, x=None, y=None, **kwds): all numerical columns are used. **kwds Additional keyword arguments are documented in - :meth:`pandas.DataFrame.plot`. + :meth:`DataFrame.plot`. Returns ------- @@ -3032,8 +3032,8 @@ def bar(self, x=None, y=None, **kwds): See Also -------- - pandas.DataFrame.plot.barh : Horizontal bar plot. - pandas.DataFrame.plot : Make plots of a DataFrame. + DataFrame.plot.barh : Horizontal bar plot. + DataFrame.plot : Make plots of a DataFrame. matplotlib.pyplot.bar : Make a bar plot with matplotlib. Examples @@ -3104,7 +3104,7 @@ def barh(self, x=None, y=None, **kwds): y : label or position, default All numeric columns in dataframe Columns to be plotted from the DataFrame. **kwds - Keyword arguments to pass on to :meth:`pandas.DataFrame.plot`. + Keyword arguments to pass on to :meth:`DataFrame.plot`. Returns ------- @@ -3112,8 +3112,8 @@ def barh(self, x=None, y=None, **kwds): See Also -------- - pandas.DataFrame.plot.bar: Vertical bar plot. - pandas.DataFrame.plot : Make plots of DataFrame using matplotlib. + DataFrame.plot.bar: Vertical bar plot. + DataFrame.plot : Make plots of DataFrame using matplotlib. matplotlib.axes.Axes.bar : Plot a vertical bar plot using matplotlib. Examples @@ -3191,7 +3191,7 @@ def box(self, by=None, **kwds): Column in the DataFrame to group by. **kwds : optional Additional keywords are documented in - :meth:`pandas.DataFrame.plot`. + :meth:`DataFrame.plot`. Returns ------- @@ -3199,8 +3199,8 @@ def box(self, by=None, **kwds): See Also -------- - pandas.DataFrame.boxplot: Another method to draw a box plot. - pandas.Series.plot.box: Draw a box plot from a Series object. + DataFrame.boxplot: Another method to draw a box plot. + Series.plot.box: Draw a box plot from a Series object. matplotlib.pyplot.boxplot: Draw a box plot in matplotlib. Examples @@ -3234,7 +3234,7 @@ def hist(self, by=None, bins=10, **kwds): Number of histogram bins to be used. **kwds Additional keyword arguments are documented in - :meth:`pandas.DataFrame.plot`. + :meth:`DataFrame.plot`. Returns ------- @@ -3327,7 +3327,7 @@ def area(self, x=None, y=None, **kwds): unstacked plot. **kwds : optional Additional keyword arguments are documented in - :meth:`pandas.DataFrame.plot`. + :meth:`DataFrame.plot`. Returns ------- @@ -3398,7 +3398,7 @@ def pie(self, y=None, **kwds): Label or position of the column to plot. If not provided, ``subplots=True`` argument must be passed. **kwds - Keyword arguments to pass on to :meth:`pandas.DataFrame.plot`. + Keyword arguments to pass on to :meth:`DataFrame.plot`. Returns ------- @@ -3474,7 +3474,7 @@ def scatter(self, x, y, s=None, c=None, **kwds): marker points according to a colormap. **kwds - Keyword arguments to pass on to :meth:`pandas.DataFrame.plot`. + Keyword arguments to pass on to :meth:`DataFrame.plot`. Returns ------- @@ -3548,7 +3548,7 @@ def hexbin(self, x, y, C=None, reduce_C_function=None, gridsize=None, y-direction. **kwds Additional keyword arguments are documented in - :meth:`pandas.DataFrame.plot`. + :meth:`DataFrame.plot`. Returns ------- diff --git a/pandas/plotting/_misc.py b/pandas/plotting/_misc.py index 01cc8ecc6f957..62a33245f99ef 100644 --- a/pandas/plotting/_misc.py +++ b/pandas/plotting/_misc.py @@ -182,7 +182,7 @@ def radviz(frame, class_column, ax=None, color=None, colormap=None, **kwds): See Also -------- - pandas.plotting.andrews_curves : Plot clustering visualization. + plotting.andrews_curves : Plot clustering visualization. Examples -------- @@ -394,8 +394,8 @@ def bootstrap_plot(series, fig=None, size=50, samples=500, **kwds): See Also -------- - pandas.DataFrame.plot : Basic plotting for DataFrame objects. - pandas.Series.plot : Basic plotting for Series objects. + DataFrame.plot : Basic plotting for DataFrame objects. + Series.plot : Basic plotting for Series objects. Examples -------- diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index c454db3bbdffc..f591b24f5b648 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -77,7 +77,7 @@ def to_offset(freq): See Also -------- - pandas.DateOffset + DateOffset Examples -------- From 2816dba72ae483fb7619c77de0c3aeba3eb61dbc Mon Sep 17 00:00:00 2001 From: Jeremy Schendel Date: Thu, 7 Feb 2019 19:19:19 -0700 Subject: [PATCH 076/215] BUG: Fix Series.is_unique with single occurrence of NaN (#25182) --- doc/source/whatsnew/v0.24.2.rst | 2 +- pandas/core/base.py | 2 +- pandas/tests/indexes/common.py | 21 +++++++++++++++++++ .../tests/indexes/interval/test_interval.py | 19 ++++------------- pandas/tests/indexes/multi/test_duplicates.py | 12 +++++++++++ pandas/tests/indexes/period/test_indexing.py | 12 ----------- pandas/tests/indexes/test_category.py | 9 -------- pandas/tests/series/test_duplicates.py | 18 ++++++++++------ 8 files changed, 51 insertions(+), 44 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 6a9a316da1ec6..73df504c89d5b 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -87,7 +87,7 @@ Bug Fixes **Other** -- +- Bug in :meth:`Series.is_unique` where single occurrences of ``NaN`` were not considered unique (:issue:`25180`) - - diff --git a/pandas/core/base.py b/pandas/core/base.py index 726266b39cdee..5a98e83c65884 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -1365,7 +1365,7 @@ def is_unique(self): ------- is_unique : boolean """ - return self.nunique() == len(self) + return self.nunique(dropna=False) == len(self) @property def is_monotonic(self): diff --git a/pandas/tests/indexes/common.py b/pandas/tests/indexes/common.py index a838779689c44..6d29c147c4a4a 100644 --- a/pandas/tests/indexes/common.py +++ b/pandas/tests/indexes/common.py @@ -913,3 +913,24 @@ def test_astype_category(self, copy, name, ordered): result = index.astype('category', copy=copy) expected = CategoricalIndex(index.values, name=name) tm.assert_index_equal(result, expected) + + def test_is_unique(self): + # initialize a unique index + index = self.create_index().drop_duplicates() + assert index.is_unique is True + + # empty index should be unique + index_empty = index[:0] + assert index_empty.is_unique is True + + # test basic dupes + index_dup = index.insert(0, index[0]) + assert index_dup.is_unique is False + + # single NA should be unique + index_na = index.insert(0, np.nan) + assert index_na.is_unique is True + + # multiple NA should not be unique + index_na_dup = index_na.insert(0, np.nan) + assert index_na_dup.is_unique is False diff --git a/pandas/tests/indexes/interval/test_interval.py b/pandas/tests/indexes/interval/test_interval.py index f1fd06c9cab6e..e4f25ff143273 100644 --- a/pandas/tests/indexes/interval/test_interval.py +++ b/pandas/tests/indexes/interval/test_interval.py @@ -242,12 +242,10 @@ def test_take(self, closed): [0, 0, 1], [1, 1, 2], closed=closed) tm.assert_index_equal(result, expected) - def test_unique(self, closed): - # unique non-overlapping - idx = IntervalIndex.from_tuples( - [(0, 1), (2, 3), (4, 5)], closed=closed) - assert idx.is_unique is True - + def test_is_unique_interval(self, closed): + """ + Interval specific tests for is_unique in addition to base class tests + """ # unique overlapping - distinct endpoints idx = IntervalIndex.from_tuples([(0, 1), (0.5, 1.5)], closed=closed) assert idx.is_unique is True @@ -261,15 +259,6 @@ def test_unique(self, closed): idx = IntervalIndex.from_tuples([(-1, 1), (-2, 2)], closed=closed) assert idx.is_unique is True - # duplicate - idx = IntervalIndex.from_tuples( - [(0, 1), (0, 1), (2, 3)], closed=closed) - assert idx.is_unique is False - - # empty - idx = IntervalIndex([], closed=closed) - assert idx.is_unique is True - def test_monotonic(self, closed): # increasing non-overlapping idx = IntervalIndex.from_tuples( diff --git a/pandas/tests/indexes/multi/test_duplicates.py b/pandas/tests/indexes/multi/test_duplicates.py index af15026de2b34..35034dc57b4b8 100644 --- a/pandas/tests/indexes/multi/test_duplicates.py +++ b/pandas/tests/indexes/multi/test_duplicates.py @@ -143,6 +143,18 @@ def test_has_duplicates(idx, idx_dup): assert mi.is_unique is False assert mi.has_duplicates is True + # single instance of NaN + mi_nan = MultiIndex(levels=[['a', 'b'], [0, 1]], + codes=[[-1, 0, 0, 1, 1], [-1, 0, 1, 0, 1]]) + assert mi_nan.is_unique is True + assert mi_nan.has_duplicates is False + + # multiple instances of NaN + mi_nan_dup = MultiIndex(levels=[['a', 'b'], [0, 1]], + codes=[[-1, -1, 0, 0, 1, 1], [-1, -1, 0, 1, 0, 1]]) + assert mi_nan_dup.is_unique is False + assert mi_nan_dup.has_duplicates is True + def test_has_duplicates_from_tuples(): # GH 9075 diff --git a/pandas/tests/indexes/period/test_indexing.py b/pandas/tests/indexes/period/test_indexing.py index 47c2edfd13395..d6ce4d5e3576e 100644 --- a/pandas/tests/indexes/period/test_indexing.py +++ b/pandas/tests/indexes/period/test_indexing.py @@ -441,18 +441,6 @@ def test_is_monotonic_decreasing(self): assert idx_dec1.is_monotonic_decreasing is True assert idx.is_monotonic_decreasing is False - def test_is_unique(self): - # GH 17717 - p0 = pd.Period('2017-09-01') - p1 = pd.Period('2017-09-02') - p2 = pd.Period('2017-09-03') - - idx0 = pd.PeriodIndex([p0, p1, p2]) - assert idx0.is_unique is True - - idx1 = pd.PeriodIndex([p1, p1, p2]) - assert idx1.is_unique is False - def test_contains(self): # GH 17717 p0 = pd.Period('2017-09-01') diff --git a/pandas/tests/indexes/test_category.py b/pandas/tests/indexes/test_category.py index 582d466c6178e..d889135160ae2 100644 --- a/pandas/tests/indexes/test_category.py +++ b/pandas/tests/indexes/test_category.py @@ -611,15 +611,6 @@ def test_is_monotonic(self, data, non_lexsorted_data): assert c.is_monotonic_increasing is True assert c.is_monotonic_decreasing is False - @pytest.mark.parametrize('values, expected', [ - ([1, 2, 3], True), - ([1, 3, 1], False), - (list('abc'), True), - (list('aba'), False)]) - def test_is_unique(self, values, expected): - ci = CategoricalIndex(values) - assert ci.is_unique is expected - def test_has_duplicates(self): idx = CategoricalIndex([0, 0, 0], name='foo') diff --git a/pandas/tests/series/test_duplicates.py b/pandas/tests/series/test_duplicates.py index fe47975711a17..a975edacc19c7 100644 --- a/pandas/tests/series/test_duplicates.py +++ b/pandas/tests/series/test_duplicates.py @@ -59,12 +59,18 @@ def test_unique_data_ownership(): Series(Series(["a", "c", "b"]).unique()).sort_values() -def test_is_unique(): - # GH11946 - s = Series(np.random.randint(0, 10, size=1000)) - assert s.is_unique is False - s = Series(np.arange(1000)) - assert s.is_unique is True +@pytest.mark.parametrize('data, expected', [ + (np.random.randint(0, 10, size=1000), False), + (np.arange(1000), True), + ([], True), + ([np.nan], True), + (['foo', 'bar', np.nan], True), + (['foo', 'foo', np.nan], False), + (['foo', 'bar', np.nan, np.nan], False)]) +def test_is_unique(data, expected): + # GH11946 / GH25180 + s = Series(data) + assert s.is_unique is expected def test_is_unique_class_ne(capsys): From 833bf70458069296741fe1c720298a7dca16fe45 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Thu, 7 Feb 2019 18:43:46 -0800 Subject: [PATCH 077/215] REF: Remove many Panel tests (#25191) --- pandas/core/internals/blocks.py | 14 - pandas/core/internals/managers.py | 4 - pandas/io/pytables.py | 19 - pandas/tests/dtypes/test_missing.py | 11 +- pandas/tests/frame/test_query_eval.py | 8 - pandas/tests/groupby/test_groupby.py | 20 - .../indexing/test_chaining_and_caching.py | 7 - pandas/tests/io/test_excel.py | 16 +- pandas/tests/io/test_pytables.py | 585 +----------------- pandas/tests/reshape/merge/test_join.py | 91 --- pandas/tests/reshape/test_concat.py | 39 +- pandas/tests/test_panel.py | 15 - pandas/tests/util/test_hashing.py | 3 +- pandas/util/testing.py | 8 - 14 files changed, 19 insertions(+), 821 deletions(-) diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 43798d64d1172..d966b31a22932 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -268,20 +268,6 @@ def _slice(self, slicer): """ return a slice of my values """ return self.values[slicer] - def reshape_nd(self, labels, shape, ref_items): - """ - Parameters - ---------- - labels : list of new axis labels - shape : new shape - ref_items : new ref_items - - return a new block that is transformed to a nd block - """ - return _block2d_to_blocknd(values=self.get_values().T, - placement=self.mgr_locs, shape=shape, - labels=labels, ref_items=ref_items) - def getitem_block(self, slicer, new_mgr_locs=None): """ Perform __getitem__-like, return result as block. diff --git a/pandas/core/internals/managers.py b/pandas/core/internals/managers.py index 5cae6e1a89170..38b719db1709f 100644 --- a/pandas/core/internals/managers.py +++ b/pandas/core/internals/managers.py @@ -584,10 +584,6 @@ def comp(s, regex=False): bm._consolidate_inplace() return bm - def reshape_nd(self, axes, **kwargs): - """ a 2d-nd reshape operation on a BlockManager """ - return self.apply('reshape_nd', axes=axes, **kwargs) - def is_consolidated(self): """ Return True if more than one block with the same dtype diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index 2813d722246bd..5a96b3e2db563 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -197,7 +197,6 @@ class DuplicateWarning(Warning): u'appendable_multiseries': 'AppendableMultiSeriesTable', u'appendable_frame': 'AppendableFrameTable', u'appendable_multiframe': 'AppendableMultiFrameTable', - u'appendable_panel': 'AppendablePanelTable', u'worm': 'WORMTable', u'legacy_frame': 'LegacyFrameTable', u'legacy_panel': 'LegacyPanelTable', @@ -4420,24 +4419,6 @@ def read(self, **kwargs): return df -class AppendablePanelTable(AppendableTable): - - """ suppor the new appendable table formats """ - table_type = u'appendable_panel' - ndim = 3 - obj_type = Panel - - def get_object(self, obj): - """ these are written transposed """ - if self.is_transposed: - obj = obj.transpose(*self.data_orientation) - return obj - - @property - def is_transposed(self): - return self.data_orientation != tuple(range(self.ndim)) - - def _reindex_axis(obj, axis, labels, other=None): ax = obj._get_axis(axis) labels = ensure_index(labels) diff --git a/pandas/tests/dtypes/test_missing.py b/pandas/tests/dtypes/test_missing.py index d913d2ad299ce..7ca01e13a33a9 100644 --- a/pandas/tests/dtypes/test_missing.py +++ b/pandas/tests/dtypes/test_missing.py @@ -2,7 +2,7 @@ from datetime import datetime from decimal import Decimal -from warnings import catch_warnings, filterwarnings, simplefilter +from warnings import catch_warnings, filterwarnings import numpy as np import pytest @@ -94,15 +94,6 @@ def test_isna_isnull(self, isna_f): expected = df.apply(isna_f) tm.assert_frame_equal(result, expected) - # panel - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - for p in [tm.makePanel(), tm.makePeriodPanel(), - tm.add_nans(tm.makePanel())]: - result = isna_f(p) - expected = p.apply(isna_f) - tm.assert_panel_equal(result, expected) - def test_isna_lists(self): result = isna([[False]]) exp = np.array([[False]]) diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 9c4d306ea5720..0d06d0006a9e2 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -14,7 +14,6 @@ from pandas import DataFrame, Index, MultiIndex, Series, date_range from pandas.core.computation.check import _NUMEXPR_INSTALLED from pandas.tests.frame.common import TestData -import pandas.util.testing as tm from pandas.util.testing import ( assert_frame_equal, assert_series_equal, makeCustomDataframe as mkdf) @@ -355,13 +354,6 @@ def to_series(mi, level): else: raise AssertionError("object must be a Series or Index") - @pytest.mark.filterwarnings("ignore::FutureWarning") - def test_raise_on_panel_with_multiindex(self, parser, engine): - p = tm.makePanel(7) - p.items = tm.makeCustomIndex(len(p.items), nlevels=2) - with pytest.raises(NotImplementedError): - pd.eval('p + 1', parser=parser, engine=engine) - @td.skip_if_no_ne class TestDataFrameQueryNumExprPandas(object): diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py index f14c9dcdd8e42..1ae8efd2f6867 100644 --- a/pandas/tests/groupby/test_groupby.py +++ b/pandas/tests/groupby/test_groupby.py @@ -1218,26 +1218,6 @@ def test_groupby_nat_exclude(): grouped.get_group(pd.NaT) -@pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") -def test_sparse_friendly(df): - sdf = df[['C', 'D']].to_sparse() - panel = tm.makePanel() - tm.add_nans(panel) - - def _check_work(gp): - gp.mean() - gp.agg(np.mean) - dict(iter(gp)) - - # it works! - _check_work(sdf.groupby(lambda x: x // 2)) - _check_work(sdf['C'].groupby(lambda x: x // 2)) - _check_work(sdf.groupby(df['A'])) - - # do this someday - # _check_work(panel.groupby(lambda x: x.month, axis=1)) - - def test_groupby_2d_malformed(): d = DataFrame(index=lrange(2)) d['group'] = ['g1', 'g2'] diff --git a/pandas/tests/indexing/test_chaining_and_caching.py b/pandas/tests/indexing/test_chaining_and_caching.py index be0d9c5cf24ca..6070edca075c2 100644 --- a/pandas/tests/indexing/test_chaining_and_caching.py +++ b/pandas/tests/indexing/test_chaining_and_caching.py @@ -357,7 +357,6 @@ def check(result, expected): check(result4, expected) @pytest.mark.filterwarnings("ignore::DeprecationWarning") - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") def test_cache_updating(self): # GH 4939, make sure to update the cache on setitem @@ -367,12 +366,6 @@ def test_cache_updating(self): assert "Hello Friend" in df['A'].index assert "Hello Friend" in df['B'].index - panel = tm.makePanel() - panel.ix[0] # get first item into cache - panel.ix[:, :, 'A+1'] = panel.ix[:, :, 'A'] + 1 - assert "A+1" in panel.ix[0].columns - assert "A+1" in panel.ix[1].columns - # 10264 df = DataFrame(np.zeros((5, 5), dtype='int64'), columns=[ 'a', 'b', 'c', 'd', 'e'], index=range(5)) diff --git a/pandas/tests/io/test_excel.py b/pandas/tests/io/test_excel.py index 717e9bc23c6b1..8c92db734168b 100644 --- a/pandas/tests/io/test_excel.py +++ b/pandas/tests/io/test_excel.py @@ -5,7 +5,6 @@ from functools import partial import os import warnings -from warnings import catch_warnings import numpy as np from numpy import nan @@ -2382,15 +2381,12 @@ def check_called(func): assert isinstance(writer, DummyClass) df = tm.makeCustomDataframe(1, 1) - with catch_warnings(record=True): - panel = tm.makePanel() - func = lambda: df.to_excel('something.test') - check_called(func) - check_called(lambda: panel.to_excel('something.test')) - check_called(lambda: df.to_excel('something.xlsx')) - check_called( - lambda: df.to_excel( - 'something.xls', engine='dummy')) + func = lambda: df.to_excel('something.test') + check_called(func) + check_called(lambda: df.to_excel('something.xlsx')) + check_called( + lambda: df.to_excel( + 'something.xls', engine='dummy')) @pytest.mark.parametrize('engine', [ diff --git a/pandas/tests/io/test_pytables.py b/pandas/tests/io/test_pytables.py index c339c33751b5f..4a806b178c6ee 100644 --- a/pandas/tests/io/test_pytables.py +++ b/pandas/tests/io/test_pytables.py @@ -19,11 +19,11 @@ import pandas as pd from pandas import ( Categorical, DataFrame, DatetimeIndex, Index, Int64Index, MultiIndex, - Panel, RangeIndex, Series, Timestamp, bdate_range, compat, concat, - date_range, isna, timedelta_range) + RangeIndex, Series, Timestamp, bdate_range, compat, concat, date_range, + isna, timedelta_range) import pandas.util.testing as tm from pandas.util.testing import ( - assert_frame_equal, assert_panel_equal, assert_series_equal, set_timezone) + assert_frame_equal, assert_series_equal, set_timezone) from pandas.io import pytables as pytables # noqa:E402 from pandas.io.formats.printing import pprint_thing @@ -185,11 +185,6 @@ def roundtrip(key, obj, **kwargs): o = tm.makeDataFrame() assert_frame_equal(o, roundtrip('frame', o)) - with catch_warnings(record=True): - - o = tm.makePanel() - assert_panel_equal(o, roundtrip('panel', o)) - # table df = DataFrame(dict(A=lrange(5), B=lrange(5))) df.to_hdf(path, 'table', append=True) @@ -348,11 +343,9 @@ def test_keys(self): store['a'] = tm.makeTimeSeries() store['b'] = tm.makeStringSeries() store['c'] = tm.makeDataFrame() - with catch_warnings(record=True): - store['d'] = tm.makePanel() - store['foo/bar'] = tm.makePanel() - assert len(store) == 5 - expected = {'/a', '/b', '/c', '/d', '/foo/bar'} + + assert len(store) == 3 + expected = {'/a', '/b', '/c'} assert set(store.keys()) == expected assert set(store) == expected @@ -388,11 +381,6 @@ def test_repr(self): store['b'] = tm.makeStringSeries() store['c'] = tm.makeDataFrame() - with catch_warnings(record=True): - store['d'] = tm.makePanel() - store['foo/bar'] = tm.makePanel() - store.append('e', tm.makePanel()) - df = tm.makeDataFrame() df['obj1'] = 'foo' df['obj2'] = 'bar' @@ -936,21 +924,6 @@ def test_append(self): store.append('/df3 foo', df[10:]) tm.assert_frame_equal(store['df3 foo'], df) - # panel - wp = tm.makePanel() - _maybe_remove(store, 'wp1') - store.append('wp1', wp.iloc[:, :10, :]) - store.append('wp1', wp.iloc[:, 10:, :]) - assert_panel_equal(store['wp1'], wp) - - # test using differt order of items on the non-index axes - _maybe_remove(store, 'wp1') - wp_append1 = wp.iloc[:, :10, :] - store.append('wp1', wp_append1) - wp_append2 = wp.iloc[:, 10:, :].reindex(items=wp.items[::-1]) - store.append('wp1', wp_append2) - assert_panel_equal(store['wp1'], wp) - # dtype issues - mizxed type in a single object column df = DataFrame(data=[[1, 2], [0, 1], [1, 2], [0, 0]]) df['mixed_column'] = 'testing' @@ -1254,22 +1227,6 @@ def test_append_all_nans(self): reloaded = read_hdf(path, 'df_with_missing') tm.assert_frame_equal(df_with_missing, reloaded) - matrix = [[[np.nan, np.nan, np.nan], [1, np.nan, np.nan]], - [[np.nan, np.nan, np.nan], [np.nan, 5, 6]], - [[np.nan, np.nan, np.nan], [np.nan, 3, np.nan]]] - - with catch_warnings(record=True): - panel_with_missing = Panel(matrix, - items=['Item1', 'Item2', 'Item3'], - major_axis=[1, 2], - minor_axis=['A', 'B', 'C']) - - with ensure_clean_path(self.path) as path: - panel_with_missing.to_hdf( - path, 'panel_with_missing', format='table') - reloaded_panel = read_hdf(path, 'panel_with_missing') - tm.assert_panel_equal(panel_with_missing, reloaded_panel) - def test_append_frame_column_oriented(self): with ensure_clean_store(self.path) as store: @@ -1342,40 +1299,11 @@ def test_append_with_strings(self): with ensure_clean_store(self.path) as store: with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - wp = tm.makePanel() - wp2 = wp.rename( - minor_axis={x: "%s_extra" % x for x in wp.minor_axis}) def check_col(key, name, size): assert getattr(store.get_storer(key) .table.description, name).itemsize == size - store.append('s1', wp, min_itemsize=20) - store.append('s1', wp2) - expected = concat([wp, wp2], axis=2) - expected = expected.reindex( - minor_axis=sorted(expected.minor_axis)) - assert_panel_equal(store['s1'], expected) - check_col('s1', 'minor_axis', 20) - - # test dict format - store.append('s2', wp, min_itemsize={'minor_axis': 20}) - store.append('s2', wp2) - expected = concat([wp, wp2], axis=2) - expected = expected.reindex( - minor_axis=sorted(expected.minor_axis)) - assert_panel_equal(store['s2'], expected) - check_col('s2', 'minor_axis', 20) - - # apply the wrong field (similar to #1) - store.append('s3', wp, min_itemsize={'major_axis': 20}) - pytest.raises(ValueError, store.append, 's3', wp2) - - # test truncation of bigger strings - store.append('s4', wp) - pytest.raises(ValueError, store.append, 's4', wp2) - # avoid truncation on elements df = DataFrame([[123, 'asdqwerty'], [345, 'dggnhebbsdfbdfb']]) store.append('df_big', df) @@ -1674,32 +1602,6 @@ def check_col(key, name, size): (df_dc.string == 'foo')] tm.assert_frame_equal(result, expected) - with ensure_clean_store(self.path) as store: - with catch_warnings(record=True): - # panel - # GH5717 not handling data_columns - np.random.seed(1234) - p = tm.makePanel() - - store.append('p1', p) - tm.assert_panel_equal(store.select('p1'), p) - - store.append('p2', p, data_columns=True) - tm.assert_panel_equal(store.select('p2'), p) - - result = store.select('p2', where='ItemA>0') - expected = p.to_frame() - expected = expected[expected['ItemA'] > 0] - tm.assert_frame_equal(result.to_frame(), expected) - - result = store.select( - 'p2', where='ItemA>0 & minor_axis=["A","B"]') - expected = p.to_frame() - expected = expected[expected['ItemA'] > 0] - expected = expected[expected.reset_index( - level=['major']).index.isin(['A', 'B'])] - tm.assert_frame_equal(result.to_frame(), expected) - def test_create_table_index(self): with ensure_clean_store(self.path) as store: @@ -1708,37 +1610,6 @@ def test_create_table_index(self): def col(t, column): return getattr(store.get_storer(t).table.cols, column) - # index=False - wp = tm.makePanel() - store.append('p5', wp, index=False) - store.create_table_index('p5', columns=['major_axis']) - assert(col('p5', 'major_axis').is_indexed is True) - assert(col('p5', 'minor_axis').is_indexed is False) - - # index=True - store.append('p5i', wp, index=True) - assert(col('p5i', 'major_axis').is_indexed is True) - assert(col('p5i', 'minor_axis').is_indexed is True) - - # default optlevels - store.get_storer('p5').create_index() - assert(col('p5', 'major_axis').index.optlevel == 6) - assert(col('p5', 'minor_axis').index.kind == 'medium') - - # let's change the indexing scheme - store.create_table_index('p5') - assert(col('p5', 'major_axis').index.optlevel == 6) - assert(col('p5', 'minor_axis').index.kind == 'medium') - store.create_table_index('p5', optlevel=9) - assert(col('p5', 'major_axis').index.optlevel == 9) - assert(col('p5', 'minor_axis').index.kind == 'medium') - store.create_table_index('p5', kind='full') - assert(col('p5', 'major_axis').index.optlevel == 9) - assert(col('p5', 'minor_axis').index.kind == 'full') - store.create_table_index('p5', optlevel=1, kind='light') - assert(col('p5', 'major_axis').index.optlevel == 1) - assert(col('p5', 'minor_axis').index.kind == 'light') - # data columns df = tm.makeTimeDataFrame() df['string'] = 'foo' @@ -1761,19 +1632,6 @@ def col(t, column): store.put('f2', df) pytest.raises(TypeError, store.create_table_index, 'f2') - def test_append_diff_item_order(self): - - with catch_warnings(record=True): - wp = tm.makePanel() - wp1 = wp.iloc[:, :10, :] - wp2 = wp.iloc[wp.items.get_indexer(['ItemC', 'ItemB', 'ItemA']), - 10:, :] - - with ensure_clean_store(self.path) as store: - store.put('panel', wp1, format='table') - pytest.raises(ValueError, store.put, 'panel', wp2, - append=True) - def test_append_hierarchical(self): index = MultiIndex(levels=[['foo', 'bar', 'baz', 'qux'], ['one', 'two', 'three']], @@ -1987,10 +1845,6 @@ def check(obj, comparator): df['time2'] = Timestamp('20130102') check(df, tm.assert_frame_equal) - with catch_warnings(record=True): - p = tm.makePanel() - check(p, assert_panel_equal) - # empty frame, GH4273 with ensure_clean_store(self.path) as store: @@ -2011,24 +1865,6 @@ def check(obj, comparator): store.put('df2', df) assert_frame_equal(store.select('df2'), df) - with catch_warnings(record=True): - - # 0 len - p_empty = Panel(items=list('ABC')) - store.append('p', p_empty) - pytest.raises(KeyError, store.select, 'p') - - # repeated append of 0/non-zero frames - p = Panel(np.random.randn(3, 4, 5), items=list('ABC')) - store.append('p', p) - assert_panel_equal(store.select('p'), p) - store.append('p', p_empty) - assert_panel_equal(store.select('p'), p) - - # store - store.put('p2', p_empty) - assert_panel_equal(store.select('p2'), p_empty) - def test_append_raise(self): with ensure_clean_store(self.path) as store: @@ -2143,24 +1979,6 @@ def test_table_mixed_dtypes(self): store.append('df1_mixed', df) tm.assert_frame_equal(store.select('df1_mixed'), df) - with catch_warnings(record=True): - - # panel - wp = tm.makePanel() - wp['obj1'] = 'foo' - wp['obj2'] = 'bar' - wp['bool1'] = wp['ItemA'] > 0 - wp['bool2'] = wp['ItemB'] > 0 - wp['int1'] = 1 - wp['int2'] = 2 - wp = wp._consolidate() - - with catch_warnings(record=True): - - with ensure_clean_store(self.path) as store: - store.append('p1_mixed', wp) - assert_panel_equal(store.select('p1_mixed'), wp) - def test_unimplemented_dtypes_table_columns(self): with ensure_clean_store(self.path) as store: @@ -2308,193 +2126,6 @@ def test_remove(self): del store['b'] assert len(store) == 0 - def test_remove_where(self): - - with ensure_clean_store(self.path) as store: - - with catch_warnings(record=True): - - # non-existance - crit1 = 'index>foo' - pytest.raises(KeyError, store.remove, 'a', [crit1]) - - # try to remove non-table (with crit) - # non-table ok (where = None) - wp = tm.makePanel(30) - store.put('wp', wp, format='table') - store.remove('wp', ["minor_axis=['A', 'D']"]) - rs = store.select('wp') - expected = wp.reindex(minor_axis=['B', 'C']) - assert_panel_equal(rs, expected) - - # empty where - _maybe_remove(store, 'wp') - store.put('wp', wp, format='table') - - # deleted number (entire table) - n = store.remove('wp', []) - assert n == 120 - - # non - empty where - _maybe_remove(store, 'wp') - store.put('wp', wp, format='table') - pytest.raises(ValueError, store.remove, - 'wp', ['foo']) - - def test_remove_startstop(self): - # GH #4835 and #6177 - - with ensure_clean_store(self.path) as store: - - with catch_warnings(record=True): - wp = tm.makePanel(30) - - # start - _maybe_remove(store, 'wp1') - store.put('wp1', wp, format='t') - n = store.remove('wp1', start=32) - assert n == 120 - 32 - result = store.select('wp1') - expected = wp.reindex(major_axis=wp.major_axis[:32 // 4]) - assert_panel_equal(result, expected) - - _maybe_remove(store, 'wp2') - store.put('wp2', wp, format='t') - n = store.remove('wp2', start=-32) - assert n == 32 - result = store.select('wp2') - expected = wp.reindex(major_axis=wp.major_axis[:-32 // 4]) - assert_panel_equal(result, expected) - - # stop - _maybe_remove(store, 'wp3') - store.put('wp3', wp, format='t') - n = store.remove('wp3', stop=32) - assert n == 32 - result = store.select('wp3') - expected = wp.reindex(major_axis=wp.major_axis[32 // 4:]) - assert_panel_equal(result, expected) - - _maybe_remove(store, 'wp4') - store.put('wp4', wp, format='t') - n = store.remove('wp4', stop=-32) - assert n == 120 - 32 - result = store.select('wp4') - expected = wp.reindex(major_axis=wp.major_axis[-32 // 4:]) - assert_panel_equal(result, expected) - - # start n stop - _maybe_remove(store, 'wp5') - store.put('wp5', wp, format='t') - n = store.remove('wp5', start=16, stop=-16) - assert n == 120 - 32 - result = store.select('wp5') - expected = wp.reindex( - major_axis=(wp.major_axis[:16 // 4] - .union(wp.major_axis[-16 // 4:]))) - assert_panel_equal(result, expected) - - _maybe_remove(store, 'wp6') - store.put('wp6', wp, format='t') - n = store.remove('wp6', start=16, stop=16) - assert n == 0 - result = store.select('wp6') - expected = wp.reindex(major_axis=wp.major_axis) - assert_panel_equal(result, expected) - - # with where - _maybe_remove(store, 'wp7') - - # TODO: unused? - date = wp.major_axis.take(np.arange(0, 30, 3)) # noqa - - crit = 'major_axis=date' - store.put('wp7', wp, format='t') - n = store.remove('wp7', where=[crit], stop=80) - assert n == 28 - result = store.select('wp7') - expected = wp.reindex(major_axis=wp.major_axis.difference( - wp.major_axis[np.arange(0, 20, 3)])) - assert_panel_equal(result, expected) - - def test_remove_crit(self): - - with ensure_clean_store(self.path) as store: - - with catch_warnings(record=True): - wp = tm.makePanel(30) - - # group row removal - _maybe_remove(store, 'wp3') - date4 = wp.major_axis.take([0, 1, 2, 4, 5, 6, 8, 9, 10]) - crit4 = 'major_axis=date4' - store.put('wp3', wp, format='t') - n = store.remove('wp3', where=[crit4]) - assert n == 36 - - result = store.select('wp3') - expected = wp.reindex( - major_axis=wp.major_axis.difference(date4)) - assert_panel_equal(result, expected) - - # upper half - _maybe_remove(store, 'wp') - store.put('wp', wp, format='table') - date = wp.major_axis[len(wp.major_axis) // 2] - - crit1 = 'major_axis>date' - crit2 = "minor_axis=['A', 'D']" - n = store.remove('wp', where=[crit1]) - assert n == 56 - - n = store.remove('wp', where=[crit2]) - assert n == 32 - - result = store['wp'] - expected = wp.truncate(after=date).reindex(minor=['B', 'C']) - assert_panel_equal(result, expected) - - # individual row elements - _maybe_remove(store, 'wp2') - store.put('wp2', wp, format='table') - - date1 = wp.major_axis[1:3] - crit1 = 'major_axis=date1' - store.remove('wp2', where=[crit1]) - result = store.select('wp2') - expected = wp.reindex( - major_axis=wp.major_axis.difference(date1)) - assert_panel_equal(result, expected) - - date2 = wp.major_axis[5] - crit2 = 'major_axis=date2' - store.remove('wp2', where=[crit2]) - result = store['wp2'] - expected = wp.reindex( - major_axis=(wp.major_axis - .difference(date1) - .difference(Index([date2])) - )) - assert_panel_equal(result, expected) - - date3 = [wp.major_axis[7], wp.major_axis[9]] - crit3 = 'major_axis=date3' - store.remove('wp2', where=[crit3]) - result = store['wp2'] - expected = wp.reindex(major_axis=wp.major_axis - .difference(date1) - .difference(Index([date2])) - .difference(Index(date3))) - assert_panel_equal(result, expected) - - # corners - _maybe_remove(store, 'wp4') - store.put('wp4', wp, format='table') - n = store.remove( - 'wp4', where="major_axis>wp.major_axis[-1]") - result = store.select('wp4') - assert_panel_equal(result, wp) - def test_invalid_terms(self): with ensure_clean_store(self.path) as store: @@ -2504,27 +2135,16 @@ def test_invalid_terms(self): df = tm.makeTimeDataFrame() df['string'] = 'foo' df.loc[0:4, 'string'] = 'bar' - wp = tm.makePanel() store.put('df', df, format='table') - store.put('wp', wp, format='table') # some invalid terms - pytest.raises(ValueError, store.select, - 'wp', "minor=['A', 'B']") - pytest.raises(ValueError, store.select, - 'wp', ["index=['20121114']"]) - pytest.raises(ValueError, store.select, 'wp', [ - "index=['20121114', '20121114']"]) pytest.raises(TypeError, Term) # more invalid pytest.raises( ValueError, store.select, 'df', 'df.index[3]') pytest.raises(SyntaxError, store.select, 'df', 'index>') - pytest.raises( - ValueError, store.select, 'wp', - "major_axis<'20000108' & minor_axis['A', 'B']") # from the docs with ensure_clean_path(self.path) as path: @@ -2546,127 +2166,6 @@ def test_invalid_terms(self): pytest.raises(ValueError, read_hdf, path, 'dfq', where="A>0 or C>0") - def test_terms(self): - - with ensure_clean_store(self.path) as store: - - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - - wp = tm.makePanel() - wpneg = Panel.fromDict({-1: tm.makeDataFrame(), - 0: tm.makeDataFrame(), - 1: tm.makeDataFrame()}) - - store.put('wp', wp, format='table') - store.put('wpneg', wpneg, format='table') - - # panel - result = store.select( - 'wp', - "major_axis<'20000108' and minor_axis=['A', 'B']") - expected = wp.truncate( - after='20000108').reindex(minor=['A', 'B']) - assert_panel_equal(result, expected) - - # with deprecation - result = store.select( - 'wp', where=("major_axis<'20000108' " - "and minor_axis=['A', 'B']")) - expected = wp.truncate( - after='20000108').reindex(minor=['A', 'B']) - tm.assert_panel_equal(result, expected) - - with catch_warnings(record=True): - - # valid terms - terms = [('major_axis=20121114'), - ('major_axis>20121114'), - (("major_axis=['20121114', '20121114']"),), - ('major_axis=datetime.datetime(2012, 11, 14)'), - 'major_axis> 20121114', - 'major_axis >20121114', - 'major_axis > 20121114', - (("minor_axis=['A', 'B']"),), - (("minor_axis=['A', 'B']"),), - ((("minor_axis==['A', 'B']"),),), - (("items=['ItemA', 'ItemB']"),), - ('items=ItemA'), - ] - - for t in terms: - store.select('wp', t) - - with pytest.raises(TypeError, - match='Only named functions are supported'): - store.select( - 'wp', - 'major_axis == (lambda x: x)("20130101")') - - with catch_warnings(record=True): - # check USub node parsing - res = store.select('wpneg', 'items == -1') - expected = Panel({-1: wpneg[-1]}) - tm.assert_panel_equal(res, expected) - - msg = 'Unary addition not supported' - with pytest.raises(NotImplementedError, match=msg): - store.select('wpneg', 'items == +1') - - def test_term_compat(self): - with ensure_clean_store(self.path) as store: - - with catch_warnings(record=True): - wp = Panel(np.random.randn(2, 5, 4), items=['Item1', 'Item2'], - major_axis=date_range('1/1/2000', periods=5), - minor_axis=['A', 'B', 'C', 'D']) - store.append('wp', wp) - - result = store.select( - 'wp', where=("major_axis>20000102 " - "and minor_axis=['A', 'B']")) - expected = wp.loc[:, wp.major_axis > - Timestamp('20000102'), ['A', 'B']] - assert_panel_equal(result, expected) - - store.remove('wp', 'major_axis>20000103') - result = store.select('wp') - expected = wp.loc[:, wp.major_axis <= Timestamp('20000103'), :] - assert_panel_equal(result, expected) - - with ensure_clean_store(self.path) as store: - - with catch_warnings(record=True): - wp = Panel(np.random.randn(2, 5, 4), - items=['Item1', 'Item2'], - major_axis=date_range('1/1/2000', periods=5), - minor_axis=['A', 'B', 'C', 'D']) - store.append('wp', wp) - - # stringified datetimes - result = store.select( - 'wp', 'major_axis>datetime.datetime(2000, 1, 2)') - expected = wp.loc[:, wp.major_axis > Timestamp('20000102')] - assert_panel_equal(result, expected) - - result = store.select( - 'wp', 'major_axis>datetime.datetime(2000, 1, 2)') - expected = wp.loc[:, wp.major_axis > Timestamp('20000102')] - assert_panel_equal(result, expected) - - result = store.select( - 'wp', - "major_axis=[datetime.datetime(2000, 1, 2, 0, 0), " - "datetime.datetime(2000, 1, 3, 0, 0)]") - expected = wp.loc[:, [Timestamp('20000102'), - Timestamp('20000103')]] - assert_panel_equal(result, expected) - - result = store.select( - 'wp', "minor_axis=['A', 'B']") - expected = wp.loc[:, :, ['A', 'B']] - assert_panel_equal(result, expected) - def test_same_name_scoping(self): with ensure_clean_store(self.path) as store: @@ -2982,12 +2481,6 @@ def _make_one(): self._check_roundtrip(df1['int1'], tm.assert_series_equal, compression=compression) - def test_wide(self): - - with catch_warnings(record=True): - wp = tm.makePanel() - self._check_roundtrip(wp, assert_panel_equal) - @pytest.mark.filterwarnings( "ignore:\\nduplicate:pandas.io.pytables.DuplicateWarning" ) @@ -3096,34 +2589,6 @@ def test_select(self): with ensure_clean_store(self.path) as store: with catch_warnings(record=True): - wp = tm.makePanel() - - # put/select ok - _maybe_remove(store, 'wp') - store.put('wp', wp, format='table') - store.select('wp') - - # non-table ok (where = None) - _maybe_remove(store, 'wp') - store.put('wp2', wp) - store.select('wp2') - - # selection on the non-indexable with a large number of columns - wp = Panel(np.random.randn(100, 100, 100), - items=['Item%03d' % i for i in range(100)], - major_axis=date_range('1/1/2000', periods=100), - minor_axis=['E%03d' % i for i in range(100)]) - - _maybe_remove(store, 'wp') - store.append('wp', wp) - items = ['Item%03d' % i for i in range(80)] - result = store.select('wp', 'items=items') - expected = wp.reindex(items=items) - assert_panel_equal(expected, result) - - # selectin non-table with a where - # pytest.raises(ValueError, store.select, - # 'wp2', ('column', ['A', 'D'])) # select with columns= df = tm.makeTimeDataFrame() @@ -3652,31 +3117,6 @@ def test_retain_index_attributes2(self): assert read_hdf(path, 'data').index.name is None - def test_panel_select(self): - - with ensure_clean_store(self.path) as store: - - with catch_warnings(record=True): - - wp = tm.makePanel() - - store.put('wp', wp, format='table') - date = wp.major_axis[len(wp.major_axis) // 2] - - crit1 = ('major_axis>=date') - crit2 = ("minor_axis=['A', 'D']") - - result = store.select('wp', [crit1, crit2]) - expected = wp.truncate(before=date).reindex(minor=['A', 'D']) - assert_panel_equal(result, expected) - - result = store.select( - 'wp', ['major_axis>="20000124"', - ("minor_axis=['A', 'B']")]) - expected = wp.truncate( - before='20000124').reindex(minor=['A', 'B']) - assert_panel_equal(result, expected) - def test_frame_select(self): df = tm.makeTimeDataFrame() @@ -5300,35 +4740,30 @@ def test_complex_mixed_table(self): reread = read_hdf(path, 'df') assert_frame_equal(df, reread) - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") def test_complex_across_dimensions_fixed(self): with catch_warnings(record=True): complex128 = np.array( [1.0 + 1.0j, 1.0 + 1.0j, 1.0 + 1.0j, 1.0 + 1.0j]) s = Series(complex128, index=list('abcd')) df = DataFrame({'A': s, 'B': s}) - p = Panel({'One': df, 'Two': df}) - objs = [s, df, p] - comps = [tm.assert_series_equal, tm.assert_frame_equal, - tm.assert_panel_equal] + objs = [s, df] + comps = [tm.assert_series_equal, tm.assert_frame_equal] for obj, comp in zip(objs, comps): with ensure_clean_path(self.path) as path: obj.to_hdf(path, 'obj', format='fixed') reread = read_hdf(path, 'obj') comp(obj, reread) - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") def test_complex_across_dimensions(self): complex128 = np.array([1.0 + 1.0j, 1.0 + 1.0j, 1.0 + 1.0j, 1.0 + 1.0j]) s = Series(complex128, index=list('abcd')) df = DataFrame({'A': s, 'B': s}) with catch_warnings(record=True): - p = Panel({'One': df, 'Two': df}) - objs = [df, p] - comps = [tm.assert_frame_equal, tm.assert_panel_equal] + objs = [df] + comps = [tm.assert_frame_equal] for obj, comp in zip(objs, comps): with ensure_clean_path(self.path) as path: obj.to_hdf(path, 'obj', format='table') diff --git a/pandas/tests/reshape/merge/test_join.py b/pandas/tests/reshape/merge/test_join.py index e21f9d0291afa..5d7a9ab6f4cf0 100644 --- a/pandas/tests/reshape/merge/test_join.py +++ b/pandas/tests/reshape/merge/test_join.py @@ -1,7 +1,5 @@ # pylint: disable=E1103 -from warnings import catch_warnings - import numpy as np from numpy.random import randn import pytest @@ -657,95 +655,6 @@ def test_join_dups(self): 'y_y', 'x_x', 'y_x', 'x_y', 'y_y'] assert_frame_equal(dta, expected) - def test_panel_join(self): - with catch_warnings(record=True): - panel = tm.makePanel() - tm.add_nans(panel) - - p1 = panel.iloc[:2, :10, :3] - p2 = panel.iloc[2:, 5:, 2:] - - # left join - result = p1.join(p2) - expected = p1.copy() - expected['ItemC'] = p2['ItemC'] - tm.assert_panel_equal(result, expected) - - # right join - result = p1.join(p2, how='right') - expected = p2.copy() - expected['ItemA'] = p1['ItemA'] - expected['ItemB'] = p1['ItemB'] - expected = expected.reindex(items=['ItemA', 'ItemB', 'ItemC']) - tm.assert_panel_equal(result, expected) - - # inner join - result = p1.join(p2, how='inner') - expected = panel.iloc[:, 5:10, 2:3] - tm.assert_panel_equal(result, expected) - - # outer join - result = p1.join(p2, how='outer') - expected = p1.reindex(major=panel.major_axis, - minor=panel.minor_axis) - expected = expected.join(p2.reindex(major=panel.major_axis, - minor=panel.minor_axis)) - tm.assert_panel_equal(result, expected) - - def test_panel_join_overlap(self): - with catch_warnings(record=True): - panel = tm.makePanel() - tm.add_nans(panel) - - p1 = panel.loc[['ItemA', 'ItemB', 'ItemC']] - p2 = panel.loc[['ItemB', 'ItemC']] - - # Expected index is - # - # ItemA, ItemB_p1, ItemC_p1, ItemB_p2, ItemC_p2 - joined = p1.join(p2, lsuffix='_p1', rsuffix='_p2') - p1_suf = p1.loc[['ItemB', 'ItemC']].add_suffix('_p1') - p2_suf = p2.loc[['ItemB', 'ItemC']].add_suffix('_p2') - no_overlap = panel.loc[['ItemA']] - expected = no_overlap.join(p1_suf.join(p2_suf)) - tm.assert_panel_equal(joined, expected) - - def test_panel_join_many(self): - with catch_warnings(record=True): - tm.K = 10 - panel = tm.makePanel() - tm.K = 4 - - panels = [panel.iloc[:2], panel.iloc[2:6], panel.iloc[6:]] - - joined = panels[0].join(panels[1:]) - tm.assert_panel_equal(joined, panel) - - panels = [panel.iloc[:2, :-5], - panel.iloc[2:6, 2:], - panel.iloc[6:, 5:-7]] - - data_dict = {} - for p in panels: - data_dict.update(p.iteritems()) - - joined = panels[0].join(panels[1:], how='inner') - expected = pd.Panel.from_dict(data_dict, intersect=True) - tm.assert_panel_equal(joined, expected) - - joined = panels[0].join(panels[1:], how='outer') - expected = pd.Panel.from_dict(data_dict, intersect=False) - tm.assert_panel_equal(joined, expected) - - # edge cases - msg = "Suffixes not supported when passing multiple panels" - with pytest.raises(ValueError, match=msg): - panels[0].join(panels[1:], how='outer', lsuffix='foo', - rsuffix='bar') - msg = "Right join not supported with multiple panels" - with pytest.raises(ValueError, match=msg): - panels[0].join(panels[1:], how='right') - def test_join_multi_to_multi(self, join_type): # GH 20475 leftindex = MultiIndex.from_product([list('abc'), list('xy'), [1, 2]], diff --git a/pandas/tests/reshape/test_concat.py b/pandas/tests/reshape/test_concat.py index ec6123bae327e..a186d32ed8800 100644 --- a/pandas/tests/reshape/test_concat.py +++ b/pandas/tests/reshape/test_concat.py @@ -3,7 +3,7 @@ from datetime import datetime from decimal import Decimal from itertools import combinations -from warnings import catch_warnings, simplefilter +from warnings import catch_warnings import dateutil import numpy as np @@ -1499,15 +1499,6 @@ def test_concat_mixed_objs(self): result = concat([s1, df, s2], ignore_index=True) assert_frame_equal(result, expected) - # invalid concatente of mixed dims - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - panel = tm.makePanel() - msg = ("cannot concatenate unaligned mixed dimensional NDFrame" - " objects") - with pytest.raises(ValueError, match=msg): - concat([panel, s1], axis=1) - def test_empty_dtype_coerce(self): # xref to #12411 @@ -1543,34 +1534,6 @@ def test_dtype_coerceion(self): result = concat([df.iloc[[0]], df.iloc[[1]]]) tm.assert_series_equal(result.dtypes, df.dtypes) - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") - def test_panel_concat_other_axes(self): - panel = tm.makePanel() - - p1 = panel.iloc[:, :5, :] - p2 = panel.iloc[:, 5:, :] - - result = concat([p1, p2], axis=1) - tm.assert_panel_equal(result, panel) - - p1 = panel.iloc[:, :, :2] - p2 = panel.iloc[:, :, 2:] - - result = concat([p1, p2], axis=2) - tm.assert_panel_equal(result, panel) - - # if things are a bit misbehaved - p1 = panel.iloc[:2, :, :2] - p2 = panel.iloc[:, :, 2:] - p1['ItemC'] = 'baz' - - result = concat([p1, p2], axis=2) - - expected = panel.copy() - expected['ItemC'] = expected['ItemC'].astype('O') - expected.loc['ItemC', :, :2] = 'baz' - tm.assert_panel_equal(result, expected) - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") # Panel.rename warning we don't care about @pytest.mark.filterwarnings("ignore:Using:FutureWarning") diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index 1fd791fc81009..5d8de3e1f87d5 100644 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -1858,21 +1858,6 @@ def test_shift(self): assert_series_equal(mixed_panel.dtypes, shifted.dtypes) def test_tshift(self): - # PeriodIndex - ps = tm.makePeriodPanel() - shifted = ps.tshift(1) - unshifted = shifted.tshift(-1) - - assert_panel_equal(unshifted, ps) - - shifted2 = ps.tshift(freq='B') - assert_panel_equal(shifted, shifted2) - - shifted3 = ps.tshift(freq=BDay()) - assert_panel_equal(shifted, shifted3) - - with pytest.raises(ValueError, match='does not match'): - ps.tshift(freq='M') # DatetimeIndex panel = make_test_panel() diff --git a/pandas/tests/util/test_hashing.py b/pandas/tests/util/test_hashing.py index d36de931e2610..c80b4483c0482 100644 --- a/pandas/tests/util/test_hashing.py +++ b/pandas/tests/util/test_hashing.py @@ -257,8 +257,7 @@ def test_categorical_with_nan_consistency(): assert result[1] in expected -@pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") -@pytest.mark.parametrize("obj", [pd.Timestamp("20130101"), tm.makePanel()]) +@pytest.mark.parametrize("obj", [pd.Timestamp("20130101")]) def test_pandas_errors(obj): msg = "Unexpected type for hashing" with pytest.raises(TypeError, match=msg): diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 47bde267156ed..053927a77c612 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -2060,14 +2060,6 @@ def makePanel(nper=None): return Panel.fromDict(data) -def makePeriodPanel(nper=None): - with warnings.catch_warnings(record=True): - warnings.filterwarnings("ignore", "\\nPanel", FutureWarning) - cols = ['Item' + c for c in string.ascii_uppercase[:K - 1]] - data = {c: makePeriodFrame(nper) for c in cols} - return Panel.fromDict(data) - - def makeCustomIndex(nentries, nlevels, prefix='#', names=False, ndupe_l=None, idx_type=None): """Create an index/multindex with given dimensions, levels, names, etc' From 1c573088b8fffe0843a8dab0058e465b282bf940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Moron=20Tejero?= <43825325+victormoron@users.noreply.github.com> Date: Fri, 8 Feb 2019 02:49:50 +0000 Subject: [PATCH 078/215] DOC: Fixes to docstrings and add PR10 (space before colon) to validation (#25109) --- ci/code_checks.sh | 2 +- pandas/core/config.py | 4 ++-- pandas/io/excel.py | 2 +- pandas/plotting/_core.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index 2bd6aa2f9c7a5..d0dea72d69996 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -240,7 +240,7 @@ fi ### DOCSTRINGS ### if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then - MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, EX04, RT04, SS05, SA05)' ; echo $MSG + MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, PR10, EX04, RT04, SS05, SA05)' ; echo $MSG $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR05,EX04,RT04,SS05,SA05 RET=$(($RET + $?)) ; echo $MSG "DONE" diff --git a/pandas/core/config.py b/pandas/core/config.py index 0f43ca65d187a..01664fffb1e27 100644 --- a/pandas/core/config.py +++ b/pandas/core/config.py @@ -282,8 +282,8 @@ def __doc__(self): Note: partial matches are supported for convenience, but unless you use the full option name (e.g. x.y.z.option_name), your code may break in future versions if new options with similar names are introduced. -value : - new value of option. +value : object + New value of option. Returns ------- diff --git a/pandas/io/excel.py b/pandas/io/excel.py index 11e5e78fa3e80..9e5e9f4f0d4f6 100644 --- a/pandas/io/excel.py +++ b/pandas/io/excel.py @@ -1003,7 +1003,7 @@ class ExcelWriter(object): mode : {'w' or 'a'}, default 'w' File mode to use (write or append). - .. versionadded:: 0.24.0 + .. versionadded:: 0.24.0 Attributes ---------- diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index 1790c70a3c8ac..a525b9cff1182 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -2548,7 +2548,7 @@ def boxplot_frame_groupby(grouped, subplots=True, column=None, fontsize=None, Parameters ---------- grouped : Grouped DataFrame - subplots : + subplots : bool * ``False`` - no subplots will be used * ``True`` - create a subplot for each group column : column name or list of names, or vector From cf3ad875f863f12990f6de39aa558c01bed2f046 Mon Sep 17 00:00:00 2001 From: Saurav Chakravorty Date: Fri, 8 Feb 2019 08:21:20 +0530 Subject: [PATCH 079/215] DOC: exclude autogenerated c/cpp/html files from 'trailing whitespace' checks (#24549) --- ci/code_checks.sh | 7 ++++--- pandas/util/move.c | 25 +++++++++++++++---------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index d0dea72d69996..5c9d20e483ce4 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -93,7 +93,7 @@ if [[ -z "$CHECK" || "$CHECK" == "lint" ]]; then # this particular codebase (e.g. src/headers, src/klib, src/msgpack). However, # we can lint all header files since they aren't "generated" like C files are. MSG='Linting .c and .h' ; echo $MSG - cpplint --quiet --extensions=c,h --headers=h --recursive --filter=-readability/casting,-runtime/int,-build/include_subdir pandas/_libs/src/*.h pandas/_libs/src/parser pandas/_libs/ujson pandas/_libs/tslibs/src/datetime + cpplint --quiet --extensions=c,h --headers=h --recursive --filter=-readability/casting,-runtime/int,-build/include_subdir pandas/_libs/src/*.h pandas/_libs/src/parser pandas/_libs/ujson pandas/_libs/tslibs/src/datetime pandas/io/msgpack pandas/_libs/*.cpp pandas/util RET=$(($RET + $?)) ; echo $MSG "DONE" echo "isort --version-number" @@ -174,9 +174,10 @@ if [[ -z "$CHECK" || "$CHECK" == "patterns" ]]; then MSG='Check that no file in the repo contains tailing whitespaces' ; echo $MSG set -o pipefail if [[ "$AZURE" == "true" ]]; then - ! grep -n --exclude="*.svg" -RI "\s$" * | awk -F ":" '{print "##vso[task.logissue type=error;sourcepath=" $1 ";linenumber=" $2 ";] Tailing whitespaces found: " $3}' + # we exclude all c/cpp files as the c/cpp files of pandas code base are tested when Linting .c and .h files + ! grep -n '--exclude=*.'{svg,c,cpp,html} -RI "\s$" * | awk -F ":" '{print "##vso[task.logissue type=error;sourcepath=" $1 ";linenumber=" $2 ";] Tailing whitespaces found: " $3}' else - ! grep -n --exclude="*.svg" -RI "\s$" * | awk -F ":" '{print $1 ":" $2 ":Tailing whitespaces found: " $3}' + ! grep -n '--exclude=*.'{svg,c,cpp,html} -RI "\s$" * | awk -F ":" '{print $1 ":" $2 ":Tailing whitespaces found: " $3}' fi RET=$(($RET + $?)) ; echo $MSG "DONE" fi diff --git a/pandas/util/move.c b/pandas/util/move.c index 62860adb1c1f6..9bb662d50cb3f 100644 --- a/pandas/util/move.c +++ b/pandas/util/move.c @@ -1,3 +1,12 @@ +/* +Copyright (c) 2019, PyData Development Team +All rights reserved. + +Distributed under the terms of the BSD Simplified License. + +The full license is in the LICENSE file, distributed with this software. +*/ + #include #define COMPILING_IN_PY2 (PY_VERSION_HEX <= 0x03000000) @@ -31,15 +40,13 @@ typedef struct { static PyTypeObject stolenbuf_type; /* forward declare type */ static void -stolenbuf_dealloc(stolenbufobject *self) -{ +stolenbuf_dealloc(stolenbufobject *self) { Py_DECREF(self->invalid_bytes); PyObject_Del(self); } static int -stolenbuf_getbuffer(stolenbufobject *self, Py_buffer *view, int flags) -{ +stolenbuf_getbuffer(stolenbufobject *self, Py_buffer *view, int flags) { return PyBuffer_FillInfo(view, (PyObject*) self, (void*) PyString_AS_STRING(self->invalid_bytes), @@ -51,8 +58,8 @@ stolenbuf_getbuffer(stolenbufobject *self, Py_buffer *view, int flags) #if COMPILING_IN_PY2 static Py_ssize_t -stolenbuf_getreadwritebuf(stolenbufobject *self, Py_ssize_t segment, void **out) -{ +stolenbuf_getreadwritebuf(stolenbufobject *self, + Py_ssize_t segment, void **out) { if (segment != 0) { PyErr_SetString(PyExc_SystemError, "accessing non-existent string segment"); @@ -63,8 +70,7 @@ stolenbuf_getreadwritebuf(stolenbufobject *self, Py_ssize_t segment, void **out) } static Py_ssize_t -stolenbuf_getsegcount(stolenbufobject *self, Py_ssize_t *len) -{ +stolenbuf_getsegcount(stolenbufobject *self, Py_ssize_t *len) { if (len) { *len = PyString_GET_SIZE(self->invalid_bytes); } @@ -157,8 +163,7 @@ PyDoc_STRVAR( however, if called through *unpacking like ``stolenbuf(*(a,))`` it would only have the one reference (the tuple). */ static PyObject* -move_into_mutable_buffer(PyObject *self, PyObject *bytes_rvalue) -{ +move_into_mutable_buffer(PyObject *self, PyObject *bytes_rvalue) { stolenbufobject *ret; if (!PyString_CheckExact(bytes_rvalue)) { From 0eddba88332e9b7f41b838e503c155f7ebe4d9a1 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Fri, 8 Feb 2019 02:52:21 +0000 Subject: [PATCH 080/215] STY: use pytest.raises context manager (indexes/period) (#25199) --- pandas/tests/indexes/period/test_asfreq.py | 4 +- .../tests/indexes/period/test_construction.py | 88 +++++++++++++------ pandas/tests/indexes/period/test_indexing.py | 29 ++++-- pandas/tests/indexes/period/test_period.py | 44 ++++++---- 4 files changed, 109 insertions(+), 56 deletions(-) diff --git a/pandas/tests/indexes/period/test_asfreq.py b/pandas/tests/indexes/period/test_asfreq.py index 2dd49e7e0845e..30b416e3fe9dd 100644 --- a/pandas/tests/indexes/period/test_asfreq.py +++ b/pandas/tests/indexes/period/test_asfreq.py @@ -67,7 +67,9 @@ def test_asfreq(self): assert pi7.asfreq('H', 'S') == pi5 assert pi7.asfreq('Min', 'S') == pi6 - pytest.raises(ValueError, pi7.asfreq, 'T', 'foo') + msg = "How must be one of S or E" + with pytest.raises(ValueError, match=msg): + pi7.asfreq('T', 'foo') result1 = pi1.asfreq('3M') result2 = pi1.asfreq('M') expected = period_range(freq='M', start='2001-12', end='2001-12') diff --git a/pandas/tests/indexes/period/test_construction.py b/pandas/tests/indexes/period/test_construction.py index 916260c4cee7e..f1adeca7245f6 100644 --- a/pandas/tests/indexes/period/test_construction.py +++ b/pandas/tests/indexes/period/test_construction.py @@ -1,6 +1,7 @@ import numpy as np import pytest +from pandas._libs.tslibs.period import IncompatibleFrequency from pandas.compat import PY3, lmap, lrange, text_type from pandas.core.dtypes.dtypes import PeriodDtype @@ -66,12 +67,17 @@ def test_constructor_field_arrays(self): years = [2007, 2007, 2007] months = [1, 2] - pytest.raises(ValueError, PeriodIndex, year=years, month=months, - freq='M') - pytest.raises(ValueError, PeriodIndex, year=years, month=months, - freq='2M') - pytest.raises(ValueError, PeriodIndex, year=years, month=months, - freq='M', start=Period('2007-01', freq='M')) + + msg = "Mismatched Period array lengths" + with pytest.raises(ValueError, match=msg): + PeriodIndex(year=years, month=months, freq='M') + with pytest.raises(ValueError, match=msg): + PeriodIndex(year=years, month=months, freq='2M') + + msg = "Can either instantiate from fields or endpoints, but not both" + with pytest.raises(ValueError, match=msg): + PeriodIndex(year=years, month=months, freq='M', + start=Period('2007-01', freq='M')) years = [2007, 2007, 2007] months = [1, 2, 3] @@ -81,8 +87,8 @@ def test_constructor_field_arrays(self): def test_constructor_U(self): # U was used as undefined period - pytest.raises(ValueError, period_range, '2007-1-1', periods=500, - freq='X') + with pytest.raises(ValueError, match="Invalid frequency: X"): + period_range('2007-1-1', periods=500, freq='X') def test_constructor_nano(self): idx = period_range(start=Period(ordinal=1, freq='N'), @@ -103,17 +109,29 @@ def test_constructor_arrays_negative_year(self): tm.assert_index_equal(pindex.quarter, pd.Index(quarters)) def test_constructor_invalid_quarters(self): - pytest.raises(ValueError, PeriodIndex, year=lrange(2000, 2004), - quarter=lrange(4), freq='Q-DEC') + msg = "Quarter must be 1 <= q <= 4" + with pytest.raises(ValueError, match=msg): + PeriodIndex(year=lrange(2000, 2004), quarter=lrange(4), + freq='Q-DEC') def test_constructor_corner(self): - pytest.raises(ValueError, PeriodIndex, periods=10, freq='A') + msg = "Not enough parameters to construct Period range" + with pytest.raises(ValueError, match=msg): + PeriodIndex(periods=10, freq='A') start = Period('2007', freq='A-JUN') end = Period('2010', freq='A-DEC') - pytest.raises(ValueError, PeriodIndex, start=start, end=end) - pytest.raises(ValueError, PeriodIndex, start=start) - pytest.raises(ValueError, PeriodIndex, end=end) + + msg = "start and end must have same freq" + with pytest.raises(ValueError, match=msg): + PeriodIndex(start=start, end=end) + + msg = ("Of the three parameters: start, end, and periods, exactly two" + " must be specified") + with pytest.raises(ValueError, match=msg): + PeriodIndex(start=start) + with pytest.raises(ValueError, match=msg): + PeriodIndex(end=end) result = period_range('2007-01', periods=10.5, freq='M') exp = period_range('2007-01', periods=10, freq='M') @@ -126,10 +144,15 @@ def test_constructor_fromarraylike(self): tm.assert_index_equal(PeriodIndex(idx.values), idx) tm.assert_index_equal(PeriodIndex(list(idx.values)), idx) - pytest.raises(ValueError, PeriodIndex, idx._ndarray_values) - pytest.raises(ValueError, PeriodIndex, list(idx._ndarray_values)) - pytest.raises(TypeError, PeriodIndex, - data=Period('2007', freq='A')) + msg = "freq not specified and cannot be inferred" + with pytest.raises(ValueError, match=msg): + PeriodIndex(idx._ndarray_values) + with pytest.raises(ValueError, match=msg): + PeriodIndex(list(idx._ndarray_values)) + + msg = "'Period' object is not iterable" + with pytest.raises(TypeError, match=msg): + PeriodIndex(data=Period('2007', freq='A')) result = PeriodIndex(iter(idx)) tm.assert_index_equal(result, idx) @@ -160,7 +183,9 @@ def test_constructor_datetime64arr(self): vals = np.arange(100000, 100000 + 10000, 100, dtype=np.int64) vals = vals.view(np.dtype('M8[us]')) - pytest.raises(ValueError, PeriodIndex, vals, freq='D') + msg = r"Wrong dtype: datetime64\[us\]" + with pytest.raises(ValueError, match=msg): + PeriodIndex(vals, freq='D') @pytest.mark.parametrize('box', [None, 'series', 'index']) def test_constructor_datetime64arr_ok(self, box): @@ -300,17 +325,20 @@ def test_constructor_simple_new_empty(self): @pytest.mark.parametrize('floats', [[1.1, 2.1], np.array([1.1, 2.1])]) def test_constructor_floats(self, floats): - with pytest.raises(TypeError): + msg = r"PeriodIndex\._simple_new does not accept floats" + with pytest.raises(TypeError, match=msg): pd.PeriodIndex._simple_new(floats, freq='M') - with pytest.raises(TypeError): + msg = "PeriodIndex does not allow floating point in construction" + with pytest.raises(TypeError, match=msg): pd.PeriodIndex(floats, freq='M') def test_constructor_nat(self): - pytest.raises(ValueError, period_range, start='NaT', - end='2011-01-01', freq='M') - pytest.raises(ValueError, period_range, start='2011-01-01', - end='NaT', freq='M') + msg = "start and end must not be NaT" + with pytest.raises(ValueError, match=msg): + period_range(start='NaT', end='2011-01-01', freq='M') + with pytest.raises(ValueError, match=msg): + period_range(start='2011-01-01', end='NaT', freq='M') def test_constructor_year_and_quarter(self): year = pd.Series([2001, 2002, 2003]) @@ -455,9 +483,12 @@ def test_constructor(self): # Mixed freq should fail vals = [end_intv, Period('2006-12-31', 'w')] - pytest.raises(ValueError, PeriodIndex, vals) + msg = r"Input has different freq=W-SUN from PeriodIndex\(freq=B\)" + with pytest.raises(IncompatibleFrequency, match=msg): + PeriodIndex(vals) vals = np.array(vals) - pytest.raises(ValueError, PeriodIndex, vals) + with pytest.raises(IncompatibleFrequency, match=msg): + PeriodIndex(vals) def test_constructor_error(self): start = Period('02-Apr-2005', 'B') @@ -508,7 +539,8 @@ def setup_method(self, method): self.series = Series(period_range('2000-01-01', periods=10, freq='D')) def test_constructor_cant_cast_period(self): - with pytest.raises(TypeError): + msg = "Cannot cast PeriodArray to dtype float64" + with pytest.raises(TypeError, match=msg): Series(period_range('2000-01-01', periods=10, freq='D'), dtype=float) diff --git a/pandas/tests/indexes/period/test_indexing.py b/pandas/tests/indexes/period/test_indexing.py index d6ce4d5e3576e..fa8199b4e6163 100644 --- a/pandas/tests/indexes/period/test_indexing.py +++ b/pandas/tests/indexes/period/test_indexing.py @@ -84,7 +84,8 @@ def test_getitem_partial(self): rng = period_range('2007-01', periods=50, freq='M') ts = Series(np.random.randn(len(rng)), rng) - pytest.raises(KeyError, ts.__getitem__, '2006') + with pytest.raises(KeyError, match=r"^'2006'$"): + ts['2006'] result = ts['2008'] assert (result.index.year == 2008).all() @@ -326,7 +327,8 @@ def test_take_fill_value(self): with pytest.raises(ValueError, match=msg): idx.take(np.array([1, 0, -5]), fill_value=True) - with pytest.raises(IndexError): + msg = "index -5 is out of bounds for size 3" + with pytest.raises(IndexError, match=msg): idx.take(np.array([1, -5])) @@ -335,7 +337,8 @@ class TestIndexing(object): def test_get_loc_msg(self): idx = period_range('2000-1-1', freq='A', periods=10) bad_period = Period('2012', 'A') - pytest.raises(KeyError, idx.get_loc, bad_period) + with pytest.raises(KeyError, match=r"^Period\('2012', 'A-DEC'\)$"): + idx.get_loc(bad_period) try: idx.get_loc(bad_period) @@ -373,8 +376,13 @@ def test_get_loc(self): msg = "Cannot interpret 'foo' as period" with pytest.raises(KeyError, match=msg): idx0.get_loc('foo') - pytest.raises(KeyError, idx0.get_loc, 1.1) - pytest.raises(TypeError, idx0.get_loc, idx0) + with pytest.raises(KeyError, match=r"^1\.1$"): + idx0.get_loc(1.1) + + msg = (r"'PeriodIndex\(\['2017-09-01', '2017-09-02', '2017-09-03'\]," + r" dtype='period\[D\]', freq='D'\)' is an invalid key") + with pytest.raises(TypeError, match=msg): + idx0.get_loc(idx0) # get the location of p1/p2 from # monotonic increasing PeriodIndex with duplicate @@ -391,8 +399,13 @@ def test_get_loc(self): with pytest.raises(KeyError, match=msg): idx1.get_loc('foo') - pytest.raises(KeyError, idx1.get_loc, 1.1) - pytest.raises(TypeError, idx1.get_loc, idx1) + with pytest.raises(KeyError, match=r"^1\.1$"): + idx1.get_loc(1.1) + + msg = (r"'PeriodIndex\(\['2017-09-02', '2017-09-02', '2017-09-03'\]," + r" dtype='period\[D\]', freq='D'\)' is an invalid key") + with pytest.raises(TypeError, match=msg): + idx1.get_loc(idx1) # get the location of p1/p2 from # non-monotonic increasing/decreasing PeriodIndex with duplicate @@ -569,7 +582,7 @@ def test_get_loc2(self): msg = 'Input has different freq=None from PeriodArray\\(freq=D\\)' with pytest.raises(ValueError, match=msg): idx.get_loc('2000-01-10', method='nearest', tolerance='1 hour') - with pytest.raises(KeyError): + with pytest.raises(KeyError, match=r"^Period\('2000-01-10', 'D'\)$"): idx.get_loc('2000-01-10', method='nearest', tolerance='1 day') with pytest.raises( ValueError, diff --git a/pandas/tests/indexes/period/test_period.py b/pandas/tests/indexes/period/test_period.py index dc9a32d75d272..89bcf56dbda71 100644 --- a/pandas/tests/indexes/period/test_period.py +++ b/pandas/tests/indexes/period/test_period.py @@ -71,10 +71,12 @@ def test_fillna_period(self): pd.Period('2011-01-01', freq='D')), exp) def test_no_millisecond_field(self): - with pytest.raises(AttributeError): + msg = "type object 'DatetimeIndex' has no attribute 'millisecond'" + with pytest.raises(AttributeError, match=msg): DatetimeIndex.millisecond - with pytest.raises(AttributeError): + msg = "'DatetimeIndex' object has no attribute 'millisecond'" + with pytest.raises(AttributeError, match=msg): DatetimeIndex([]).millisecond @pytest.mark.parametrize("sort", [None, False]) @@ -98,8 +100,8 @@ def test_difference_freq(self, sort): def test_hash_error(self): index = period_range('20010101', periods=10) - with pytest.raises(TypeError, match=("unhashable type: %r" % - type(index).__name__)): + msg = "unhashable type: '{}'".format(type(index).__name__) + with pytest.raises(TypeError, match=msg): hash(index) def test_make_time_series(self): @@ -124,7 +126,8 @@ def test_shallow_copy_i8(self): def test_shallow_copy_changing_freq_raises(self): pi = period_range("2018-01-01", periods=3, freq="2D") - with pytest.raises(IncompatibleFrequency, match="are different"): + msg = "specified freq and dtype are different" + with pytest.raises(IncompatibleFrequency, match=msg): pi._shallow_copy(pi, freq="H") def test_dtype_str(self): @@ -214,21 +217,17 @@ def test_period_index_length(self): assert (i1 == i2).all() assert i1.freq == i2.freq - try: + msg = "start and end must have same freq" + with pytest.raises(ValueError, match=msg): period_range(start=start, end=end_intv) - raise AssertionError('Cannot allow mixed freq for start and end') - except ValueError: - pass end_intv = Period('2005-05-01', 'B') i1 = period_range(start=start, end=end_intv) - try: + msg = ("Of the three parameters: start, end, and periods, exactly two" + " must be specified") + with pytest.raises(ValueError, match=msg): period_range(start=start) - raise AssertionError( - 'Must specify periods if missing start or end') - except ValueError: - pass # infer freq from first element i2 = PeriodIndex([end_intv, Period('2005-05-05', 'B')]) @@ -241,9 +240,12 @@ def test_period_index_length(self): # Mixed freq should fail vals = [end_intv, Period('2006-12-31', 'w')] - pytest.raises(ValueError, PeriodIndex, vals) + msg = r"Input has different freq=W-SUN from PeriodIndex\(freq=B\)" + with pytest.raises(IncompatibleFrequency, match=msg): + PeriodIndex(vals) vals = np.array(vals) - pytest.raises(ValueError, PeriodIndex, vals) + with pytest.raises(ValueError, match=msg): + PeriodIndex(vals) def test_fields(self): # year, month, day, hour, minute @@ -381,7 +383,9 @@ def test_contains_nat(self): assert np.nan in idx def test_periods_number_check(self): - with pytest.raises(ValueError): + msg = ("Of the three parameters: start, end, and periods, exactly two" + " must be specified") + with pytest.raises(ValueError, match=msg): period_range('2011-1-1', '2012-1-1', 'B') def test_start_time(self): @@ -500,7 +504,8 @@ def test_is_full(self): assert index.is_full index = PeriodIndex([2006, 2005, 2005], freq='A') - pytest.raises(ValueError, getattr, index, 'is_full') + with pytest.raises(ValueError, match="Index is not monotonic"): + index.is_full assert index[:0].is_full @@ -574,5 +579,6 @@ def test_maybe_convert_timedelta(): assert pi._maybe_convert_timedelta(2) == 2 offset = offsets.BusinessDay() - with pytest.raises(ValueError, match='freq'): + msg = r"Input has different freq=B from PeriodIndex\(freq=D\)" + with pytest.raises(ValueError, match=msg): pi._maybe_convert_timedelta(offset) From 1d1b14c7ea7c3e497b66453cacbb8230b26cb6a6 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Fri, 8 Feb 2019 11:29:47 +0000 Subject: [PATCH 081/215] fix ci failures (#25225) --- pandas/tests/series/test_dtypes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/series/test_dtypes.py b/pandas/tests/series/test_dtypes.py index e29974f56967f..d8046c4944afc 100644 --- a/pandas/tests/series/test_dtypes.py +++ b/pandas/tests/series/test_dtypes.py @@ -291,8 +291,8 @@ def test_astype_categorical_to_other(self): expected = s tm.assert_series_equal(s.astype('category'), expected) tm.assert_series_equal(s.astype(CategoricalDtype()), expected) - msg = (r"could not convert string to float: '(0 - 499|9500 - 9999)'|" - r"invalid literal for float\(\): (0 - 499|9500 - 9999)") + msg = (r"could not convert string to float|" + r"invalid literal for float\(\)") with pytest.raises(ValueError, match=msg): s.astype('float64') From b08a58403cce2a728cd22f0770b4ad59a0903469 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Fri, 8 Feb 2019 15:33:31 -0800 Subject: [PATCH 082/215] DEPR: remove tm.makePanel and all usages (#25231) --- pandas/tests/frame/test_reshape.py | 9 - pandas/tests/generic/test_generic.py | 52 +- pandas/tests/generic/test_panel.py | 23 +- pandas/tests/indexing/test_panel.py | 22 - pandas/tests/io/test_sql.py | 6 - pandas/tests/reshape/test_reshape.py | 19 +- pandas/tests/test_panel.py | 1500 +------------------------- pandas/util/testing.py | 19 +- 8 files changed, 20 insertions(+), 1630 deletions(-) diff --git a/pandas/tests/frame/test_reshape.py b/pandas/tests/frame/test_reshape.py index 28222a82945be..daac084f657af 100644 --- a/pandas/tests/frame/test_reshape.py +++ b/pandas/tests/frame/test_reshape.py @@ -4,7 +4,6 @@ from datetime import datetime import itertools -from warnings import catch_warnings, simplefilter import numpy as np import pytest @@ -49,14 +48,6 @@ def test_pivot(self): assert pivoted.index.name == 'index' assert pivoted.columns.names == (None, 'columns') - with catch_warnings(record=True): - # pivot multiple columns - simplefilter("ignore", FutureWarning) - wp = tm.makePanel() - lp = wp.to_frame() - df = lp.reset_index() - tm.assert_frame_equal(df.pivot('major', 'minor'), lp.unstack()) - def test_pivot_duplicates(self): data = DataFrame({'a': ['bar', 'bar', 'foo', 'foo', 'foo'], 'b': ['one', 'two', 'one', 'one', 'two'], diff --git a/pandas/tests/generic/test_generic.py b/pandas/tests/generic/test_generic.py index 7183fea85a069..fb17b47948336 100644 --- a/pandas/tests/generic/test_generic.py +++ b/pandas/tests/generic/test_generic.py @@ -740,23 +740,11 @@ def test_squeeze(self): tm.assert_series_equal(s.squeeze(), s) for df in [tm.makeTimeDataFrame()]: tm.assert_frame_equal(df.squeeze(), df) - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - for p in [tm.makePanel()]: - tm.assert_panel_equal(p.squeeze(), p) # squeezing df = tm.makeTimeDataFrame().reindex(columns=['A']) tm.assert_series_equal(df.squeeze(), df['A']) - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - p = tm.makePanel().reindex(items=['ItemA']) - tm.assert_frame_equal(p.squeeze(), p['ItemA']) - - p = tm.makePanel().reindex(items=['ItemA'], minor_axis=['A']) - tm.assert_series_equal(p.squeeze(), p.loc['ItemA', :, 'A']) - # don't fail with 0 length dimensions GH11229 & GH8999 empty_series = Series([], name='five') empty_frame = DataFrame([empty_series]) @@ -789,8 +777,6 @@ def test_numpy_squeeze(self): tm.assert_series_equal(np.squeeze(df), df['A']) def test_transpose(self): - msg = (r"transpose\(\) got multiple values for " - r"keyword argument 'axes'") for s in [tm.makeFloatSeries(), tm.makeStringSeries(), tm.makeObjectSeries()]: # calls implementation in pandas/core/base.py @@ -798,14 +784,6 @@ def test_transpose(self): for df in [tm.makeTimeDataFrame()]: tm.assert_frame_equal(df.transpose().transpose(), df) - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - for p in [tm.makePanel()]: - tm.assert_panel_equal(p.transpose(2, 0, 1) - .transpose(1, 2, 0), p) - with pytest.raises(TypeError, match=msg): - p.transpose(2, 0, 1, axes=(2, 0, 1)) - def test_numpy_transpose(self): msg = "the 'axes' parameter is not supported" @@ -821,13 +799,6 @@ def test_numpy_transpose(self): with pytest.raises(ValueError, match=msg): np.transpose(df, axes=1) - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - p = tm.makePanel() - tm.assert_panel_equal(np.transpose( - np.transpose(p, axes=(2, 0, 1)), - axes=(1, 2, 0)), p) - def test_take(self): indices = [1, 5, -2, 6, 3, -1] for s in [tm.makeFloatSeries(), tm.makeStringSeries(), @@ -843,27 +814,12 @@ def test_take(self): columns=df.columns) tm.assert_frame_equal(out, expected) - indices = [-3, 2, 0, 1] - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - for p in [tm.makePanel()]: - out = p.take(indices) - expected = Panel(data=p.values.take(indices, axis=0), - items=p.items.take(indices), - major_axis=p.major_axis, - minor_axis=p.minor_axis) - tm.assert_panel_equal(out, expected) - def test_take_invalid_kwargs(self): indices = [-3, 2, 0, 1] s = tm.makeFloatSeries() df = tm.makeTimeDataFrame() - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - p = tm.makePanel() - - for obj in (s, df, p): + for obj in (s, df): msg = r"take\(\) got an unexpected keyword argument 'foo'" with pytest.raises(TypeError, match=msg): obj.take(indices, foo=2) @@ -966,12 +922,6 @@ def test_equals(self): assert a.equals(e) assert e.equals(f) - def test_describe_raises(self): - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - with pytest.raises(NotImplementedError): - tm.makePanel().describe() - def test_pipe(self): df = DataFrame({'A': [1, 2, 3]}) f = lambda x, y: x ** y diff --git a/pandas/tests/generic/test_panel.py b/pandas/tests/generic/test_panel.py index 8b090d951957e..73b8798661011 100644 --- a/pandas/tests/generic/test_panel.py +++ b/pandas/tests/generic/test_panel.py @@ -3,11 +3,8 @@ from warnings import catch_warnings, simplefilter -import pandas.util._test_decorators as td - from pandas import Panel -import pandas.util.testing as tm -from pandas.util.testing import assert_almost_equal, assert_panel_equal +from pandas.util.testing import assert_panel_equal from .test_generic import Generic @@ -16,24 +13,6 @@ class TestPanel(Generic): _typ = Panel _comparator = lambda self, x, y: assert_panel_equal(x, y, by_blocks=True) - @td.skip_if_no('xarray', min_version='0.7.0') - def test_to_xarray(self): - from xarray import DataArray - - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - p = tm.makePanel() - - result = p.to_xarray() - assert isinstance(result, DataArray) - assert len(result.coords) == 3 - assert_almost_equal(list(result.coords.keys()), - ['items', 'major_axis', 'minor_axis']) - assert len(result.dims) == 3 - - # idempotency - assert_panel_equal(result.to_pandas(), p) - # run all the tests, but wrap each in a warning catcher for t in ['test_rename', 'test_get_numeric_data', diff --git a/pandas/tests/indexing/test_panel.py b/pandas/tests/indexing/test_panel.py index 34708e1148c90..8530adec011be 100644 --- a/pandas/tests/indexing/test_panel.py +++ b/pandas/tests/indexing/test_panel.py @@ -122,28 +122,6 @@ def test_panel_getitem(self): test1 = panel.loc[:, "2002"] tm.assert_panel_equal(test1, test2) - # GH8710 - # multi-element getting with a list - panel = tm.makePanel() - - expected = panel.iloc[[0, 1]] - - result = panel.loc[['ItemA', 'ItemB']] - tm.assert_panel_equal(result, expected) - - result = panel.loc[['ItemA', 'ItemB'], :, :] - tm.assert_panel_equal(result, expected) - - result = panel[['ItemA', 'ItemB']] - tm.assert_panel_equal(result, expected) - - result = panel.loc['ItemA':'ItemB'] - tm.assert_panel_equal(result, expected) - - with catch_warnings(record=True): - result = panel.ix[['ItemA', 'ItemB']] - tm.assert_panel_equal(result, expected) - # with an object-like # GH 9140 class TestObject(object): diff --git a/pandas/tests/io/test_sql.py b/pandas/tests/io/test_sql.py index 75a6d8d009083..9d0bce3b342b4 100644 --- a/pandas/tests/io/test_sql.py +++ b/pandas/tests/io/test_sql.py @@ -605,12 +605,6 @@ def test_to_sql_series(self): s2 = sql.read_sql_query("SELECT * FROM test_series", self.conn) tm.assert_frame_equal(s.to_frame(), s2) - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") - def test_to_sql_panel(self): - panel = tm.makePanel() - pytest.raises(NotImplementedError, sql.to_sql, panel, - 'test_panel', self.conn) - def test_roundtrip(self): sql.to_sql(self.test_frame1, 'test_frame_roundtrip', con=self.conn) diff --git a/pandas/tests/reshape/test_reshape.py b/pandas/tests/reshape/test_reshape.py index 7b544b7981c1f..a5b6cffd1d86c 100644 --- a/pandas/tests/reshape/test_reshape.py +++ b/pandas/tests/reshape/test_reshape.py @@ -580,23 +580,28 @@ def test_get_dummies_duplicate_columns(self, df): class TestCategoricalReshape(object): - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") - def test_reshaping_panel_categorical(self): + def test_reshaping_multi_index_categorical(self): - p = tm.makePanel() - p['str'] = 'foo' - df = p.to_frame() + # construct a MultiIndexed DataFrame formerly created + # via `tm.makePanel().to_frame()` + cols = ['ItemA', 'ItemB', 'ItemC'] + data = {c: tm.makeTimeDataFrame() for c in cols} + df = pd.concat({c: data[c].stack() for c in data}, axis='columns') + df.index.names = ['major', 'minor'] + df['str'] = 'foo' + + dti = df.index.levels[0] df['category'] = df['str'].astype('category') result = df['category'].unstack() - c = Categorical(['foo'] * len(p.major_axis)) + c = Categorical(['foo'] * len(dti)) expected = DataFrame({'A': c.copy(), 'B': c.copy(), 'C': c.copy(), 'D': c.copy()}, columns=Index(list('ABCD'), name='minor'), - index=p.major_axis.set_names('major')) + index=dti) tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index 5d8de3e1f87d5..bfcafda1dc783 100644 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -2,56 +2,28 @@ # pylint: disable=W0612,E1101 from collections import OrderedDict from datetime import datetime -import operator -from warnings import catch_warnings, simplefilter import numpy as np import pytest -from pandas.compat import StringIO, lrange, range, signature -import pandas.util._test_decorators as td +from pandas.compat import lrange -from pandas.core.dtypes.common import is_float_dtype - -from pandas import ( - DataFrame, Index, MultiIndex, Series, compat, date_range, isna, notna) -from pandas.core.nanops import nanall, nanany +from pandas import DataFrame, MultiIndex, Series, date_range, notna import pandas.core.panel as panelm from pandas.core.panel import Panel import pandas.util.testing as tm from pandas.util.testing import ( assert_almost_equal, assert_frame_equal, assert_panel_equal, - assert_series_equal, ensure_clean, makeCustomDataframe as mkdf, - makeMixedDataFrame) + assert_series_equal, makeCustomDataframe as mkdf, makeMixedDataFrame) from pandas.io.formats.printing import pprint_thing -from pandas.tseries.offsets import BDay, MonthEnd - - -def make_test_panel(): - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - _panel = tm.makePanel() - tm.add_nans(_panel) - _panel = _panel.copy() - return _panel +from pandas.tseries.offsets import MonthEnd @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") class PanelTests(object): panel = None - def test_pickle(self): - unpickled = tm.round_trip_pickle(self.panel) - assert_frame_equal(unpickled['ItemA'], self.panel['ItemA']) - - def test_rank(self): - pytest.raises(NotImplementedError, lambda: self.panel.rank()) - - def test_cumsum(self): - cumsum = self.panel.cumsum() - assert_frame_equal(cumsum['ItemA'], self.panel['ItemA'].cumsum()) - def not_hashable(self): c_empty = Panel() c = Panel(Panel([[[1]]])) @@ -59,298 +31,9 @@ def not_hashable(self): pytest.raises(TypeError, hash, c) -@pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") -class SafeForLongAndSparse(object): - - def test_repr(self): - repr(self.panel) - - def test_copy_names(self): - for attr in ('major_axis', 'minor_axis'): - getattr(self.panel, attr).name = None - cp = self.panel.copy() - getattr(cp, attr).name = 'foo' - assert getattr(self.panel, attr).name is None - - def test_iter(self): - tm.equalContents(list(self.panel), self.panel.items) - - def test_count(self): - f = lambda s: notna(s).sum() - self._check_stat_op('count', f, obj=self.panel, has_skipna=False) - - def test_sum(self): - self._check_stat_op('sum', np.sum, skipna_alternative=np.nansum) - - def test_mean(self): - self._check_stat_op('mean', np.mean) - - def test_prod(self): - self._check_stat_op('prod', np.prod, skipna_alternative=np.nanprod) - - @pytest.mark.filterwarnings("ignore:Invalid value:RuntimeWarning") - @pytest.mark.filterwarnings("ignore:All-NaN:RuntimeWarning") - def test_median(self): - def wrapper(x): - if isna(x).any(): - return np.nan - return np.median(x) - - self._check_stat_op('median', wrapper) - - @pytest.mark.filterwarnings("ignore:Invalid value:RuntimeWarning") - def test_min(self): - self._check_stat_op('min', np.min) - - @pytest.mark.filterwarnings("ignore:Invalid value:RuntimeWarning") - def test_max(self): - self._check_stat_op('max', np.max) - - @td.skip_if_no_scipy - def test_skew(self): - from scipy.stats import skew - - def this_skew(x): - if len(x) < 3: - return np.nan - return skew(x, bias=False) - - self._check_stat_op('skew', this_skew) - - def test_var(self): - def alt(x): - if len(x) < 2: - return np.nan - return np.var(x, ddof=1) - - self._check_stat_op('var', alt) - - def test_std(self): - def alt(x): - if len(x) < 2: - return np.nan - return np.std(x, ddof=1) - - self._check_stat_op('std', alt) - - def test_sem(self): - def alt(x): - if len(x) < 2: - return np.nan - return np.std(x, ddof=1) / np.sqrt(len(x)) - - self._check_stat_op('sem', alt) - - def _check_stat_op(self, name, alternative, obj=None, has_skipna=True, - skipna_alternative=None): - if obj is None: - obj = self.panel - - # # set some NAs - # obj.loc[5:10] = np.nan - # obj.loc[15:20, -2:] = np.nan - - f = getattr(obj, name) - - if has_skipna: - - skipna_wrapper = tm._make_skipna_wrapper(alternative, - skipna_alternative) - - def wrapper(x): - return alternative(np.asarray(x)) - - for i in range(obj.ndim): - result = f(axis=i, skipna=False) - assert_frame_equal(result, obj.apply(wrapper, axis=i)) - else: - skipna_wrapper = alternative - wrapper = alternative - - for i in range(obj.ndim): - result = f(axis=i) - if name in ['sum', 'prod']: - assert_frame_equal(result, obj.apply(skipna_wrapper, axis=i)) - - pytest.raises(Exception, f, axis=obj.ndim) - - # Unimplemented numeric_only parameter. - if 'numeric_only' in signature(f).args: - with pytest.raises(NotImplementedError, match=name): - f(numeric_only=True) - - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") class SafeForSparse(object): - def test_get_axis(self): - assert (self.panel._get_axis(0) is self.panel.items) - assert (self.panel._get_axis(1) is self.panel.major_axis) - assert (self.panel._get_axis(2) is self.panel.minor_axis) - - def test_set_axis(self): - new_items = Index(np.arange(len(self.panel.items))) - new_major = Index(np.arange(len(self.panel.major_axis))) - new_minor = Index(np.arange(len(self.panel.minor_axis))) - - # ensure propagate to potentially prior-cached items too - item = self.panel['ItemA'] - self.panel.items = new_items - - if hasattr(self.panel, '_item_cache'): - assert 'ItemA' not in self.panel._item_cache - assert self.panel.items is new_items - - # TODO: unused? - item = self.panel[0] # noqa - - self.panel.major_axis = new_major - assert self.panel[0].index is new_major - assert self.panel.major_axis is new_major - - # TODO: unused? - item = self.panel[0] # noqa - - self.panel.minor_axis = new_minor - assert self.panel[0].columns is new_minor - assert self.panel.minor_axis is new_minor - - def test_get_axis_number(self): - assert self.panel._get_axis_number('items') == 0 - assert self.panel._get_axis_number('major') == 1 - assert self.panel._get_axis_number('minor') == 2 - - with pytest.raises(ValueError, match="No axis named foo"): - self.panel._get_axis_number('foo') - - with pytest.raises(ValueError, match="No axis named foo"): - self.panel.__ge__(self.panel, axis='foo') - - def test_get_axis_name(self): - assert self.panel._get_axis_name(0) == 'items' - assert self.panel._get_axis_name(1) == 'major_axis' - assert self.panel._get_axis_name(2) == 'minor_axis' - - def test_get_plane_axes(self): - # what to do here? - - index, columns = self.panel._get_plane_axes('items') - index, columns = self.panel._get_plane_axes('major_axis') - index, columns = self.panel._get_plane_axes('minor_axis') - index, columns = self.panel._get_plane_axes(0) - - def test_truncate(self): - dates = self.panel.major_axis - start, end = dates[1], dates[5] - - trunced = self.panel.truncate(start, end, axis='major') - expected = self.panel['ItemA'].truncate(start, end) - - assert_frame_equal(trunced['ItemA'], expected) - - trunced = self.panel.truncate(before=start, axis='major') - expected = self.panel['ItemA'].truncate(before=start) - - assert_frame_equal(trunced['ItemA'], expected) - - trunced = self.panel.truncate(after=end, axis='major') - expected = self.panel['ItemA'].truncate(after=end) - - assert_frame_equal(trunced['ItemA'], expected) - - def test_arith(self): - self._test_op(self.panel, operator.add) - self._test_op(self.panel, operator.sub) - self._test_op(self.panel, operator.mul) - self._test_op(self.panel, operator.truediv) - self._test_op(self.panel, operator.floordiv) - self._test_op(self.panel, operator.pow) - - self._test_op(self.panel, lambda x, y: y + x) - self._test_op(self.panel, lambda x, y: y - x) - self._test_op(self.panel, lambda x, y: y * x) - self._test_op(self.panel, lambda x, y: y / x) - self._test_op(self.panel, lambda x, y: y ** x) - - self._test_op(self.panel, lambda x, y: x + y) # panel + 1 - self._test_op(self.panel, lambda x, y: x - y) # panel - 1 - self._test_op(self.panel, lambda x, y: x * y) # panel * 1 - self._test_op(self.panel, lambda x, y: x / y) # panel / 1 - self._test_op(self.panel, lambda x, y: x ** y) # panel ** 1 - - pytest.raises(Exception, self.panel.__add__, - self.panel['ItemA']) - - @staticmethod - def _test_op(panel, op): - result = op(panel, 1) - assert_frame_equal(result['ItemA'], op(panel['ItemA'], 1)) - - def test_keys(self): - tm.equalContents(list(self.panel.keys()), self.panel.items) - - def test_iteritems(self): - # Test panel.iteritems(), aka panel.iteritems() - # just test that it works - for k, v in self.panel.iteritems(): - pass - - assert len(list(self.panel.iteritems())) == len(self.panel.items) - - def test_combineFrame(self): - def check_op(op, name): - # items - df = self.panel['ItemA'] - - func = getattr(self.panel, name) - - result = func(df, axis='items') - - assert_frame_equal( - result['ItemB'], op(self.panel['ItemB'], df)) - - # major - xs = self.panel.major_xs(self.panel.major_axis[0]) - result = func(xs, axis='major') - - idx = self.panel.major_axis[1] - - assert_frame_equal(result.major_xs(idx), - op(self.panel.major_xs(idx), xs)) - - # minor - xs = self.panel.minor_xs(self.panel.minor_axis[0]) - result = func(xs, axis='minor') - - idx = self.panel.minor_axis[1] - - assert_frame_equal(result.minor_xs(idx), - op(self.panel.minor_xs(idx), xs)) - - ops = ['add', 'sub', 'mul', 'truediv', 'floordiv', 'pow', 'mod'] - if not compat.PY3: - ops.append('div') - - for op in ops: - try: - check_op(getattr(operator, op), op) - except AttributeError: - pprint_thing("Failing operation: %r" % op) - raise - if compat.PY3: - try: - check_op(operator.truediv, 'div') - except AttributeError: - pprint_thing("Failing operation: %r" % 'div') - raise - - def test_combinePanel(self): - result = self.panel.add(self.panel) - assert_panel_equal(result, self.panel * 2) - - def test_neg(self): - assert_panel_equal(-self.panel, self.panel * -1) - # issue 7692 def test_raise_when_not_implemented(self): p = Panel(np.arange(3 * 4 * 5).reshape(3, 4, 5), @@ -364,84 +47,11 @@ def test_raise_when_not_implemented(self): with pytest.raises(NotImplementedError): getattr(p, op)(d, axis=0) - def test_select(self): - p = self.panel - - # select items - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = p.select(lambda x: x in ('ItemA', 'ItemC'), axis='items') - expected = p.reindex(items=['ItemA', 'ItemC']) - assert_panel_equal(result, expected) - - # select major_axis - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = p.select(lambda x: x >= datetime( - 2000, 1, 15), axis='major') - new_major = p.major_axis[p.major_axis >= datetime(2000, 1, 15)] - expected = p.reindex(major=new_major) - assert_panel_equal(result, expected) - - # select minor_axis - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = p.select(lambda x: x in ('D', 'A'), axis=2) - expected = p.reindex(minor=['A', 'D']) - assert_panel_equal(result, expected) - - # corner case, empty thing - with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - result = p.select(lambda x: x in ('foo', ), axis='items') - assert_panel_equal(result, p.reindex(items=[])) - - def test_get_value(self): - for item in self.panel.items: - for mjr in self.panel.major_axis[::2]: - for mnr in self.panel.minor_axis: - with tm.assert_produces_warning(FutureWarning, - check_stacklevel=False): - result = self.panel.get_value(item, mjr, mnr) - expected = self.panel[item][mnr][mjr] - assert_almost_equal(result, expected) - - def test_abs(self): - - result = self.panel.abs() - result2 = abs(self.panel) - expected = np.abs(self.panel) - assert_panel_equal(result, expected) - assert_panel_equal(result2, expected) - - df = self.panel['ItemA'] - result = df.abs() - result2 = abs(df) - expected = np.abs(df) - assert_frame_equal(result, expected) - assert_frame_equal(result2, expected) - - s = df['A'] - result = s.abs() - result2 = abs(s) - expected = np.abs(s) - assert_series_equal(result, expected) - assert_series_equal(result2, expected) - assert result.name == 'A' - assert result2.name == 'A' - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") class CheckIndexing(object): - def test_getitem(self): - pytest.raises(Exception, self.panel.__getitem__, 'ItemQ') - def test_delitem_and_pop(self): - expected = self.panel['ItemA'] - result = self.panel.pop('ItemA') - assert_frame_equal(expected, result) - assert 'ItemA' not in self.panel.items - - del self.panel['ItemB'] - assert 'ItemB' not in self.panel.items - pytest.raises(Exception, self.panel.__delitem__, 'ItemB') values = np.empty((3, 3, 3)) values[0] = 0 @@ -468,38 +78,6 @@ def test_delitem_and_pop(self): tm.assert_frame_equal(panelc[0], panel[0]) def test_setitem(self): - lp = self.panel.filter(['ItemA', 'ItemB']).to_frame() - - with pytest.raises(TypeError): - self.panel['ItemE'] = lp - - # DataFrame - df = self.panel['ItemA'][2:].filter(items=['A', 'B']) - self.panel['ItemF'] = df - self.panel['ItemE'] = df - - df2 = self.panel['ItemF'] - - assert_frame_equal(df, df2.reindex( - index=df.index, columns=df.columns)) - - # scalar - self.panel['ItemG'] = 1 - self.panel['ItemE'] = True - assert self.panel['ItemG'].values.dtype == np.int64 - assert self.panel['ItemE'].values.dtype == np.bool_ - - # object dtype - self.panel['ItemQ'] = 'foo' - assert self.panel['ItemQ'].values.dtype == np.object_ - - # boolean dtype - self.panel['ItemP'] = self.panel['ItemA'] > 0 - assert self.panel['ItemP'].values.dtype == np.bool_ - - pytest.raises(TypeError, self.panel.__setitem__, 'foo', - self.panel.loc[['ItemP']]) - # bad shape p = Panel(np.random.randn(4, 3, 2)) msg = (r"shape of value must be \(3, 2\), " @@ -537,159 +115,9 @@ def test_set_minor_major(self): assert_frame_equal(panel.loc[:, 'NewMajor', :], newmajor.astype(object)) - def test_major_xs(self): - ref = self.panel['ItemA'] - - idx = self.panel.major_axis[5] - xs = self.panel.major_xs(idx) - - result = xs['ItemA'] - assert_series_equal(result, ref.xs(idx), check_names=False) - assert result.name == 'ItemA' - - # not contained - idx = self.panel.major_axis[0] - BDay() - pytest.raises(Exception, self.panel.major_xs, idx) - - def test_major_xs_mixed(self): - self.panel['ItemD'] = 'foo' - xs = self.panel.major_xs(self.panel.major_axis[0]) - assert xs['ItemA'].dtype == np.float64 - assert xs['ItemD'].dtype == np.object_ - - def test_minor_xs(self): - ref = self.panel['ItemA'] - - idx = self.panel.minor_axis[1] - xs = self.panel.minor_xs(idx) - - assert_series_equal(xs['ItemA'], ref[idx], check_names=False) - - # not contained - pytest.raises(Exception, self.panel.minor_xs, 'E') - - def test_minor_xs_mixed(self): - self.panel['ItemD'] = 'foo' - - xs = self.panel.minor_xs('D') - assert xs['ItemA'].dtype == np.float64 - assert xs['ItemD'].dtype == np.object_ - - def test_xs(self): - itemA = self.panel.xs('ItemA', axis=0) - expected = self.panel['ItemA'] - tm.assert_frame_equal(itemA, expected) - - # Get a view by default. - itemA_view = self.panel.xs('ItemA', axis=0) - itemA_view.values[:] = np.nan - - assert np.isnan(self.panel['ItemA'].values).all() - - # Mixed-type yields a copy. - self.panel['strings'] = 'foo' - result = self.panel.xs('D', axis=2) - assert result._is_copy is not None - - def test_getitem_fancy_labels(self): - p = self.panel - - items = p.items[[1, 0]] - dates = p.major_axis[::2] - cols = ['D', 'C', 'F'] - - # all 3 specified - with catch_warnings(): - simplefilter("ignore", FutureWarning) - # XXX: warning in _validate_read_indexer - assert_panel_equal(p.loc[items, dates, cols], - p.reindex(items=items, major=dates, minor=cols)) - - # 2 specified - assert_panel_equal(p.loc[:, dates, cols], - p.reindex(major=dates, minor=cols)) - - assert_panel_equal(p.loc[items, :, cols], - p.reindex(items=items, minor=cols)) - - assert_panel_equal(p.loc[items, dates, :], - p.reindex(items=items, major=dates)) - - # only 1 - assert_panel_equal(p.loc[items, :, :], p.reindex(items=items)) - - assert_panel_equal(p.loc[:, dates, :], p.reindex(major=dates)) - - assert_panel_equal(p.loc[:, :, cols], p.reindex(minor=cols)) - def test_getitem_fancy_slice(self): pass - def test_getitem_fancy_ints(self): - p = self.panel - - # #1603 - result = p.iloc[:, -1, :] - expected = p.loc[:, p.major_axis[-1], :] - assert_frame_equal(result, expected) - - def test_getitem_fancy_xs(self): - p = self.panel - item = 'ItemB' - - date = p.major_axis[5] - col = 'C' - - # get DataFrame - # item - assert_frame_equal(p.loc[item], p[item]) - assert_frame_equal(p.loc[item, :], p[item]) - assert_frame_equal(p.loc[item, :, :], p[item]) - - # major axis, axis=1 - assert_frame_equal(p.loc[:, date], p.major_xs(date)) - assert_frame_equal(p.loc[:, date, :], p.major_xs(date)) - - # minor axis, axis=2 - assert_frame_equal(p.loc[:, :, 'C'], p.minor_xs('C')) - - # get Series - assert_series_equal(p.loc[item, date], p[item].loc[date]) - assert_series_equal(p.loc[item, date, :], p[item].loc[date]) - assert_series_equal(p.loc[item, :, col], p[item][col]) - assert_series_equal(p.loc[:, date, col], p.major_xs(date).loc[col]) - - def test_getitem_fancy_xs_check_view(self): - item = 'ItemB' - date = self.panel.major_axis[5] - - # make sure it's always a view - NS = slice(None, None) - - # DataFrames - comp = assert_frame_equal - self._check_view(item, comp) - self._check_view((item, NS), comp) - self._check_view((item, NS, NS), comp) - self._check_view((NS, date), comp) - self._check_view((NS, date, NS), comp) - self._check_view((NS, NS, 'C'), comp) - - # Series - comp = assert_series_equal - self._check_view((item, date), comp) - self._check_view((item, date, NS), comp) - self._check_view((item, NS, 'C'), comp) - self._check_view((NS, date, 'C'), comp) - - def test_getitem_callable(self): - p = self.panel - # GH 12533 - - assert_frame_equal(p[lambda x: 'ItemB'], p.loc['ItemB']) - assert_panel_equal(p[lambda x: ['ItemB', 'ItemC']], - p.loc[['ItemB', 'ItemC']]) - def test_ix_setitem_slice_dataframe(self): a = Panel(items=[1, 2, 3], major_axis=[11, 22, 33], minor_axis=[111, 222, 333]) @@ -719,43 +147,6 @@ def test_ix_align(self): assert_series_equal(df.loc[0, 0, :].reindex(b.index), b) def test_ix_frame_align(self): - p_orig = tm.makePanel() - df = p_orig.iloc[0].copy() - assert_frame_equal(p_orig['ItemA'], df) - - p = p_orig.copy() - p.iloc[0, :, :] = df - assert_panel_equal(p, p_orig) - - p = p_orig.copy() - p.iloc[0] = df - assert_panel_equal(p, p_orig) - - p = p_orig.copy() - p.iloc[0, :, :] = df - assert_panel_equal(p, p_orig) - - p = p_orig.copy() - p.iloc[0] = df - assert_panel_equal(p, p_orig) - - p = p_orig.copy() - p.loc['ItemA'] = df - assert_panel_equal(p, p_orig) - - p = p_orig.copy() - p.loc['ItemA', :, :] = df - assert_panel_equal(p, p_orig) - - p = p_orig.copy() - p['ItemA'] = df - assert_panel_equal(p, p_orig) - - p = p_orig.copy() - p.iloc[0, [0, 1, 3, 5], -2:] = df - out = p.iloc[0, [0, 1, 3, 5], -2:] - assert_frame_equal(out, df.iloc[[0, 1, 3, 5], [2, 3]]) - # GH3830, panel assignent by values/frame for dtype in ['float64', 'int64']: @@ -782,13 +173,6 @@ def test_ix_frame_align(self): tm.assert_frame_equal(panel.loc['a1'], df1) tm.assert_frame_equal(panel.loc['a2'], df2) - def _check_view(self, indexer, comp): - cp = self.panel.copy() - obj = cp.loc[indexer] - obj.values[:] = 0 - assert (obj.values == 0).all() - comp(cp.loc[indexer].reindex_like(obj), obj) - def test_logical_with_nas(self): d = Panel({'ItemA': {'a': [np.nan, False]}, 'ItemB': {'a': [True, True]}}) @@ -802,157 +186,11 @@ def test_logical_with_nas(self): expected = DataFrame({'a': [True, True]}) assert_frame_equal(result, expected) - def test_neg(self): - assert_panel_equal(-self.panel, -1 * self.panel) - - def test_invert(self): - assert_panel_equal(-(self.panel < 0), ~(self.panel < 0)) - - def test_comparisons(self): - p1 = tm.makePanel() - p2 = tm.makePanel() - - tp = p1.reindex(items=p1.items + ['foo']) - df = p1[p1.items[0]] - - def test_comp(func): - - # versus same index - result = func(p1, p2) - tm.assert_numpy_array_equal(result.values, - func(p1.values, p2.values)) - - # versus non-indexed same objs - pytest.raises(Exception, func, p1, tp) - - # versus different objs - pytest.raises(Exception, func, p1, df) - - # versus scalar - result3 = func(self.panel, 0) - tm.assert_numpy_array_equal(result3.values, - func(self.panel.values, 0)) - - with np.errstate(invalid='ignore'): - test_comp(operator.eq) - test_comp(operator.ne) - test_comp(operator.lt) - test_comp(operator.gt) - test_comp(operator.ge) - test_comp(operator.le) - - def test_get_value(self): - for item in self.panel.items: - for mjr in self.panel.major_axis[::2]: - for mnr in self.panel.minor_axis: - with tm.assert_produces_warning(FutureWarning, - check_stacklevel=False): - result = self.panel.get_value(item, mjr, mnr) - expected = self.panel[item][mnr][mjr] - assert_almost_equal(result, expected) - with catch_warnings(): - simplefilter("ignore", FutureWarning) - msg = "There must be an argument for each axis" - with pytest.raises(TypeError, match=msg): - self.panel.get_value('a') - - def test_set_value(self): - for item in self.panel.items: - for mjr in self.panel.major_axis[::2]: - for mnr in self.panel.minor_axis: - with tm.assert_produces_warning(FutureWarning, - check_stacklevel=False): - self.panel.set_value(item, mjr, mnr, 1.) - tm.assert_almost_equal(self.panel[item][mnr][mjr], 1.) - - # resize - with catch_warnings(): - simplefilter("ignore", FutureWarning) - res = self.panel.set_value('ItemE', 'foo', 'bar', 1.5) - assert isinstance(res, Panel) - assert res is not self.panel - assert res.get_value('ItemE', 'foo', 'bar') == 1.5 - - res3 = self.panel.set_value('ItemE', 'foobar', 'baz', 5) - assert is_float_dtype(res3['ItemE'].values) - - msg = ("There must be an argument for each " - "axis plus the value provided") - with pytest.raises(TypeError, match=msg): - self.panel.set_value('a') - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") -class TestPanel(PanelTests, CheckIndexing, SafeForLongAndSparse, - SafeForSparse): - - def setup_method(self, method): - self.panel = make_test_panel() - self.panel.major_axis.name = None - self.panel.minor_axis.name = None - self.panel.items.name = None - - def test_constructor(self): - # with BlockManager - wp = Panel(self.panel._data) - assert wp._data is self.panel._data - - wp = Panel(self.panel._data, copy=True) - assert wp._data is not self.panel._data - tm.assert_panel_equal(wp, self.panel) - - # strings handled prop - wp = Panel([[['foo', 'foo', 'foo', ], ['foo', 'foo', 'foo']]]) - assert wp.values.dtype == np.object_ - - vals = self.panel.values - - # no copy - wp = Panel(vals) - assert wp.values is vals - - # copy - wp = Panel(vals, copy=True) - assert wp.values is not vals - - # GH #8285, test when scalar data is used to construct a Panel - # if dtype is not passed, it should be inferred - value_and_dtype = [(1, 'int64'), (3.14, 'float64'), - ('foo', np.object_)] - for (val, dtype) in value_and_dtype: - wp = Panel(val, items=range(2), major_axis=range(3), - minor_axis=range(4)) - vals = np.empty((2, 3, 4), dtype=dtype) - vals.fill(val) - - tm.assert_panel_equal(wp, Panel(vals, dtype=dtype)) - - # test the case when dtype is passed - wp = Panel(1, items=range(2), major_axis=range(3), - minor_axis=range(4), - dtype='float32') - vals = np.empty((2, 3, 4), dtype='float32') - vals.fill(1) - - tm.assert_panel_equal(wp, Panel(vals, dtype='float32')) +class TestPanel(PanelTests, CheckIndexing, SafeForSparse): def test_constructor_cast(self): - zero_filled = self.panel.fillna(0) - - casted = Panel(zero_filled._data, dtype=int) - casted2 = Panel(zero_filled.values, dtype=int) - - exp_values = zero_filled.values.astype(int) - assert_almost_equal(casted.values, exp_values) - assert_almost_equal(casted2.values, exp_values) - - casted = Panel(zero_filled._data, dtype=np.int32) - casted2 = Panel(zero_filled.values, dtype=np.int32) - - exp_values = zero_filled.values.astype(np.int32) - assert_almost_equal(casted.values, exp_values) - assert_almost_equal(casted2.values, exp_values) - # can't cast data = [[['foo', 'bar', 'baz']]] pytest.raises(ValueError, Panel, data, dtype=float) @@ -1017,86 +255,6 @@ def test_constructor_fails_with_not_3d_input(self): with pytest.raises(ValueError, match=msg): Panel(np.random.randn(10, 2)) - def test_consolidate(self): - assert self.panel._data.is_consolidated() - - self.panel['foo'] = 1. - assert not self.panel._data.is_consolidated() - - panel = self.panel._consolidate() - assert panel._data.is_consolidated() - - def test_ctor_dict(self): - itema = self.panel['ItemA'] - itemb = self.panel['ItemB'] - - d = {'A': itema, 'B': itemb[5:]} - d2 = {'A': itema._series, 'B': itemb[5:]._series} - d3 = {'A': None, - 'B': DataFrame(itemb[5:]._series), - 'C': DataFrame(itema._series)} - - wp = Panel.from_dict(d) - wp2 = Panel.from_dict(d2) # nested Dict - - # TODO: unused? - wp3 = Panel.from_dict(d3) # noqa - - tm.assert_index_equal(wp.major_axis, self.panel.major_axis) - assert_panel_equal(wp, wp2) - - # intersect - wp = Panel.from_dict(d, intersect=True) - tm.assert_index_equal(wp.major_axis, itemb.index[5:]) - - # use constructor - assert_panel_equal(Panel(d), Panel.from_dict(d)) - assert_panel_equal(Panel(d2), Panel.from_dict(d2)) - assert_panel_equal(Panel(d3), Panel.from_dict(d3)) - - # a pathological case - d4 = {'A': None, 'B': None} - - # TODO: unused? - wp4 = Panel.from_dict(d4) # noqa - - assert_panel_equal(Panel(d4), Panel(items=['A', 'B'])) - - # cast - dcasted = {k: v.reindex(wp.major_axis).fillna(0) - for k, v in compat.iteritems(d)} - result = Panel(dcasted, dtype=int) - expected = Panel({k: v.astype(int) - for k, v in compat.iteritems(dcasted)}) - assert_panel_equal(result, expected) - - result = Panel(dcasted, dtype=np.int32) - expected = Panel({k: v.astype(np.int32) - for k, v in compat.iteritems(dcasted)}) - assert_panel_equal(result, expected) - - def test_constructor_dict_mixed(self): - data = {k: v.values for k, v in self.panel.iteritems()} - result = Panel(data) - exp_major = Index(np.arange(len(self.panel.major_axis))) - tm.assert_index_equal(result.major_axis, exp_major) - - result = Panel(data, items=self.panel.items, - major_axis=self.panel.major_axis, - minor_axis=self.panel.minor_axis) - assert_panel_equal(result, self.panel) - - data['ItemC'] = self.panel['ItemC'] - result = Panel(data) - assert_panel_equal(result, self.panel) - - # corner, blow up - data['ItemB'] = data['ItemB'][:-1] - pytest.raises(Exception, Panel, data) - - data['ItemB'] = self.panel['ItemB'].values[:, :-1] - pytest.raises(Exception, Panel, data) - def test_ctor_orderedDict(self): keys = list(set(np.random.randint(0, 5000, 100)))[ :50] # unique random int keys @@ -1107,30 +265,6 @@ def test_ctor_orderedDict(self): p = Panel.from_dict(d) assert list(p.items) == keys - def test_constructor_resize(self): - data = self.panel._data - items = self.panel.items[:-1] - major = self.panel.major_axis[:-1] - minor = self.panel.minor_axis[:-1] - - result = Panel(data, items=items, - major_axis=major, minor_axis=minor) - expected = self.panel.reindex( - items=items, major=major, minor=minor) - assert_panel_equal(result, expected) - - result = Panel(data, items=items, major_axis=major) - expected = self.panel.reindex(items=items, major=major) - assert_panel_equal(result, expected) - - result = Panel(data, items=items) - expected = self.panel.reindex(items=items) - assert_panel_equal(result, expected) - - result = Panel(data, minor_axis=minor) - expected = self.panel.reindex(minor=minor) - assert_panel_equal(result, expected) - def test_from_dict_mixed_orient(self): df = tm.makeDataFrame() df['foo'] = 'bar' @@ -1161,13 +295,6 @@ def test_constructor_error_msgs(self): Panel(np.random.randn(3, 4, 5), lrange(5), lrange(5), lrange(4)) - def test_conform(self): - df = self.panel['ItemA'][:-5].filter(items=['A', 'B']) - conformed = self.panel.conform(df) - - tm.assert_index_equal(conformed.index, self.panel.major_axis) - tm.assert_index_equal(conformed.columns, self.panel.minor_axis) - def test_convert_objects(self): # GH 4937 p = Panel(dict(A=dict(a=['1', '1.0']))) @@ -1175,12 +302,6 @@ def test_convert_objects(self): result = p._convert(numeric=True, coerce=True) assert_panel_equal(result, expected) - def test_dtypes(self): - - result = self.panel.dtypes - expected = Series(np.dtype('float64'), index=self.panel.items) - assert_series_equal(result, expected) - def test_astype(self): # GH7271 data = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) @@ -1193,121 +314,7 @@ def test_astype(self): pytest.raises(NotImplementedError, panel.astype, {0: str}) - def test_apply(self): - # GH1148 - - # ufunc - applied = self.panel.apply(np.sqrt) - with np.errstate(invalid='ignore'): - expected = np.sqrt(self.panel.values) - assert_almost_equal(applied.values, expected) - - # ufunc same shape - result = self.panel.apply(lambda x: x * 2, axis='items') - expected = self.panel * 2 - assert_panel_equal(result, expected) - result = self.panel.apply(lambda x: x * 2, axis='major_axis') - expected = self.panel * 2 - assert_panel_equal(result, expected) - result = self.panel.apply(lambda x: x * 2, axis='minor_axis') - expected = self.panel * 2 - assert_panel_equal(result, expected) - - # reduction to DataFrame - result = self.panel.apply(lambda x: x.dtype, axis='items') - expected = DataFrame(np.dtype('float64'), - index=self.panel.major_axis, - columns=self.panel.minor_axis) - assert_frame_equal(result, expected) - result = self.panel.apply(lambda x: x.dtype, axis='major_axis') - expected = DataFrame(np.dtype('float64'), - index=self.panel.minor_axis, - columns=self.panel.items) - assert_frame_equal(result, expected) - result = self.panel.apply(lambda x: x.dtype, axis='minor_axis') - expected = DataFrame(np.dtype('float64'), - index=self.panel.major_axis, - columns=self.panel.items) - assert_frame_equal(result, expected) - - # reductions via other dims - expected = self.panel.sum(0) - result = self.panel.apply(lambda x: x.sum(), axis='items') - assert_frame_equal(result, expected) - expected = self.panel.sum(1) - result = self.panel.apply(lambda x: x.sum(), axis='major_axis') - assert_frame_equal(result, expected) - expected = self.panel.sum(2) - result = self.panel.apply(lambda x: x.sum(), axis='minor_axis') - assert_frame_equal(result, expected) - - # pass kwargs - result = self.panel.apply( - lambda x, y: x.sum() + y, axis='items', y=5) - expected = self.panel.sum(0) + 5 - assert_frame_equal(result, expected) - def test_apply_slabs(self): - - # same shape as original - result = self.panel.apply(lambda x: x * 2, - axis=['items', 'major_axis']) - expected = (self.panel * 2).transpose('minor_axis', 'major_axis', - 'items') - assert_panel_equal(result, expected) - result = self.panel.apply(lambda x: x * 2, - axis=['major_axis', 'items']) - assert_panel_equal(result, expected) - - result = self.panel.apply(lambda x: x * 2, - axis=['items', 'minor_axis']) - expected = (self.panel * 2).transpose('major_axis', 'minor_axis', - 'items') - assert_panel_equal(result, expected) - result = self.panel.apply(lambda x: x * 2, - axis=['minor_axis', 'items']) - assert_panel_equal(result, expected) - - result = self.panel.apply(lambda x: x * 2, - axis=['major_axis', 'minor_axis']) - expected = self.panel * 2 - assert_panel_equal(result, expected) - result = self.panel.apply(lambda x: x * 2, - axis=['minor_axis', 'major_axis']) - assert_panel_equal(result, expected) - - # reductions - result = self.panel.apply(lambda x: x.sum(0), axis=[ - 'items', 'major_axis' - ]) - expected = self.panel.sum(1).T - assert_frame_equal(result, expected) - - result = self.panel.apply(lambda x: x.sum(1), axis=[ - 'items', 'major_axis' - ]) - expected = self.panel.sum(0) - assert_frame_equal(result, expected) - - # transforms - f = lambda x: ((x.T - x.mean(1)) / x.std(1)).T - - # make sure that we don't trigger any warnings - result = self.panel.apply(f, axis=['items', 'major_axis']) - expected = Panel({ax: f(self.panel.loc[:, :, ax]) - for ax in self.panel.minor_axis}) - assert_panel_equal(result, expected) - - result = self.panel.apply(f, axis=['major_axis', 'minor_axis']) - expected = Panel({ax: f(self.panel.loc[ax]) - for ax in self.panel.items}) - assert_panel_equal(result, expected) - - result = self.panel.apply(f, axis=['minor_axis', 'items']) - expected = Panel({ax: f(self.panel.loc[:, ax]) - for ax in self.panel.major_axis}) - assert_panel_equal(result, expected) - # with multi-indexes # GH7469 index = MultiIndex.from_tuples([('one', 'a'), ('one', 'b'), ( @@ -1343,53 +350,6 @@ def test_apply_no_or_zero_ndim(self): assert_series_equal(result_float, expected_float) assert_series_equal(result_float64, expected_float64) - def test_reindex(self): - ref = self.panel['ItemB'] - - # items - result = self.panel.reindex(items=['ItemA', 'ItemB']) - assert_frame_equal(result['ItemB'], ref) - - # major - new_major = list(self.panel.major_axis[:10]) - result = self.panel.reindex(major=new_major) - assert_frame_equal(result['ItemB'], ref.reindex(index=new_major)) - - # raise exception put both major and major_axis - pytest.raises(Exception, self.panel.reindex, - major_axis=new_major, - major=new_major) - - # minor - new_minor = list(self.panel.minor_axis[:2]) - result = self.panel.reindex(minor=new_minor) - assert_frame_equal(result['ItemB'], ref.reindex(columns=new_minor)) - - # raise exception put both major and major_axis - pytest.raises(Exception, self.panel.reindex, - minor_axis=new_minor, - minor=new_minor) - - # this ok - result = self.panel.reindex() - assert_panel_equal(result, self.panel) - assert result is not self.panel - - # with filling - smaller_major = self.panel.major_axis[::5] - smaller = self.panel.reindex(major=smaller_major) - - larger = smaller.reindex(major=self.panel.major_axis, method='pad') - - assert_frame_equal(larger.major_xs(self.panel.major_axis[1]), - smaller.major_xs(smaller_major[0])) - - # don't necessarily copy - result = self.panel.reindex( - major=self.panel.major_axis, copy=False) - assert_panel_equal(result, self.panel) - assert result is self.panel - def test_reindex_axis_style(self): panel = Panel(np.random.rand(5, 5, 5)) expected0 = Panel(panel.values).iloc[[0, 1]] @@ -1410,22 +370,6 @@ def test_reindex_axis_style(self): def test_reindex_multi(self): - # with and without copy full reindexing - result = self.panel.reindex( - items=self.panel.items, - major=self.panel.major_axis, - minor=self.panel.minor_axis, copy=False) - - assert result.items is self.panel.items - assert result.major_axis is self.panel.major_axis - assert result.minor_axis is self.panel.minor_axis - - result = self.panel.reindex( - items=self.panel.items, - major=self.panel.major_axis, - minor=self.panel.minor_axis, copy=False) - assert_panel_equal(result, self.panel) - # multi-axis indexing consistency # GH 5900 df = DataFrame(np.random.randn(4, 3)) @@ -1454,86 +398,7 @@ def test_reindex_multi(self): for i, r in enumerate(results): assert_panel_equal(expected, r) - def test_reindex_like(self): - # reindex_like - smaller = self.panel.reindex(items=self.panel.items[:-1], - major=self.panel.major_axis[:-1], - minor=self.panel.minor_axis[:-1]) - smaller_like = self.panel.reindex_like(smaller) - assert_panel_equal(smaller, smaller_like) - - def test_take(self): - # axis == 0 - result = self.panel.take([2, 0, 1], axis=0) - expected = self.panel.reindex(items=['ItemC', 'ItemA', 'ItemB']) - assert_panel_equal(result, expected) - - # axis >= 1 - result = self.panel.take([3, 0, 1, 2], axis=2) - expected = self.panel.reindex(minor=['D', 'A', 'B', 'C']) - assert_panel_equal(result, expected) - - # neg indices ok - expected = self.panel.reindex(minor=['D', 'D', 'B', 'C']) - result = self.panel.take([3, -1, 1, 2], axis=2) - assert_panel_equal(result, expected) - - pytest.raises(Exception, self.panel.take, [4, 0, 1, 2], axis=2) - - def test_sort_index(self): - import random - - ritems = list(self.panel.items) - rmajor = list(self.panel.major_axis) - rminor = list(self.panel.minor_axis) - random.shuffle(ritems) - random.shuffle(rmajor) - random.shuffle(rminor) - - random_order = self.panel.reindex(items=ritems) - sorted_panel = random_order.sort_index(axis=0) - assert_panel_equal(sorted_panel, self.panel) - - # descending - random_order = self.panel.reindex(items=ritems) - sorted_panel = random_order.sort_index(axis=0, ascending=False) - assert_panel_equal( - sorted_panel, - self.panel.reindex(items=self.panel.items[::-1])) - - random_order = self.panel.reindex(major=rmajor) - sorted_panel = random_order.sort_index(axis=1) - assert_panel_equal(sorted_panel, self.panel) - - random_order = self.panel.reindex(minor=rminor) - sorted_panel = random_order.sort_index(axis=2) - assert_panel_equal(sorted_panel, self.panel) - def test_fillna(self): - filled = self.panel.fillna(0) - assert np.isfinite(filled.values).all() - - filled = self.panel.fillna(method='backfill') - assert_frame_equal(filled['ItemA'], - self.panel['ItemA'].fillna(method='backfill')) - - panel = self.panel.copy() - panel['str'] = 'foo' - - filled = panel.fillna(method='backfill') - assert_frame_equal(filled['ItemA'], - panel['ItemA'].fillna(method='backfill')) - - empty = self.panel.reindex(items=[]) - filled = empty.fillna(0) - assert_panel_equal(filled, empty) - - pytest.raises(ValueError, self.panel.fillna) - pytest.raises(ValueError, self.panel.fillna, 5, method='ffill') - - pytest.raises(TypeError, self.panel.fillna, [1, 2]) - pytest.raises(TypeError, self.panel.fillna, (1, 2)) - # limit not implemented when only value is specified p = Panel(np.random.randn(3, 4, 5)) p.iloc[0:2, 0:2, 0:2] = np.nan @@ -1559,100 +424,6 @@ def test_fillna(self): p2.fillna(method='bfill', inplace=True) assert_panel_equal(p2, expected) - def test_ffill_bfill(self): - assert_panel_equal(self.panel.ffill(), - self.panel.fillna(method='ffill')) - assert_panel_equal(self.panel.bfill(), - self.panel.fillna(method='bfill')) - - def test_truncate_fillna_bug(self): - # #1823 - result = self.panel.truncate(before=None, after=None, axis='items') - - # it works! - result.fillna(value=0.0) - - def test_swapaxes(self): - result = self.panel.swapaxes('items', 'minor') - assert result.items is self.panel.minor_axis - - result = self.panel.swapaxes('items', 'major') - assert result.items is self.panel.major_axis - - result = self.panel.swapaxes('major', 'minor') - assert result.major_axis is self.panel.minor_axis - - panel = self.panel.copy() - result = panel.swapaxes('major', 'minor') - panel.values[0, 0, 1] = np.nan - expected = panel.swapaxes('major', 'minor') - assert_panel_equal(result, expected) - - # this should also work - result = self.panel.swapaxes(0, 1) - assert result.items is self.panel.major_axis - - # this works, but return a copy - result = self.panel.swapaxes('items', 'items') - assert_panel_equal(self.panel, result) - assert id(self.panel) != id(result) - - def test_transpose(self): - result = self.panel.transpose('minor', 'major', 'items') - expected = self.panel.swapaxes('items', 'minor') - assert_panel_equal(result, expected) - - # test kwargs - result = self.panel.transpose(items='minor', major='major', - minor='items') - expected = self.panel.swapaxes('items', 'minor') - assert_panel_equal(result, expected) - - # text mixture of args - result = self.panel.transpose( - 'minor', major='major', minor='items') - expected = self.panel.swapaxes('items', 'minor') - assert_panel_equal(result, expected) - - result = self.panel.transpose('minor', - 'major', - minor='items') - expected = self.panel.swapaxes('items', 'minor') - assert_panel_equal(result, expected) - - # duplicate axes - with pytest.raises(TypeError, - match='not enough/duplicate arguments'): - self.panel.transpose('minor', maj='major', minor='items') - - with pytest.raises(ValueError, - match='repeated axis in transpose'): - self.panel.transpose('minor', 'major', major='minor', - minor='items') - - result = self.panel.transpose(2, 1, 0) - assert_panel_equal(result, expected) - - result = self.panel.transpose('minor', 'items', 'major') - expected = self.panel.swapaxes('items', 'minor') - expected = expected.swapaxes('major', 'minor') - assert_panel_equal(result, expected) - - result = self.panel.transpose(2, 0, 1) - assert_panel_equal(result, expected) - - pytest.raises(ValueError, self.panel.transpose, 0, 0, 1) - - def test_transpose_copy(self): - panel = self.panel.copy() - result = panel.transpose(2, 0, 1, copy=True) - expected = panel.swapaxes('items', 'minor') - expected = expected.swapaxes('major', 'minor') - assert_panel_equal(result, expected) - - panel.values[0, 1, 1] = np.nan - assert notna(result.values[1, 0, 1]) - def test_to_frame_multi_major(self): idx = MultiIndex.from_tuples( [(1, 'one'), (1, 'two'), (2, 'one'), (2, 'two')]) @@ -1815,40 +586,7 @@ def test_panel_dups(self): def test_filter(self): pass - def test_compound(self): - compounded = self.panel.compound() - - assert_series_equal(compounded['ItemA'], - (1 + self.panel['ItemA']).product(0) - 1, - check_names=False) - def test_shift(self): - # major - idx = self.panel.major_axis[0] - idx_lag = self.panel.major_axis[1] - shifted = self.panel.shift(1) - assert_frame_equal(self.panel.major_xs(idx), - shifted.major_xs(idx_lag)) - - # minor - idx = self.panel.minor_axis[0] - idx_lag = self.panel.minor_axis[1] - shifted = self.panel.shift(1, axis='minor') - assert_frame_equal(self.panel.minor_xs(idx), - shifted.minor_xs(idx_lag)) - - # items - idx = self.panel.items[0] - idx_lag = self.panel.items[1] - shifted = self.panel.shift(1, axis='items') - assert_frame_equal(self.panel[idx], shifted[idx_lag]) - - # negative numbers, #2164 - result = self.panel.shift(-1) - expected = Panel({i: f.shift(-1)[:-1] - for i, f in self.panel.iteritems()}) - assert_panel_equal(result, expected) - # mixed dtypes #6959 data = [('item ' + ch, makeMixedDataFrame()) for ch in list('abcde')] @@ -1857,29 +595,6 @@ def test_shift(self): shifted = mixed_panel.shift(1) assert_series_equal(mixed_panel.dtypes, shifted.dtypes) - def test_tshift(self): - - # DatetimeIndex - panel = make_test_panel() - shifted = panel.tshift(1) - unshifted = shifted.tshift(-1) - - assert_panel_equal(panel, unshifted) - - shifted2 = panel.tshift(freq=panel.major_axis.freq) - assert_panel_equal(shifted, shifted2) - - inferred_ts = Panel(panel.values, items=panel.items, - major_axis=Index(np.asarray(panel.major_axis)), - minor_axis=panel.minor_axis) - shifted = inferred_ts.tshift(1) - unshifted = shifted.tshift(-1) - assert_panel_equal(shifted, panel.tshift(1)) - assert_panel_equal(unshifted, inferred_ts) - - no_freq = panel.iloc[:, [0, 5, 7], :] - pytest.raises(ValueError, no_freq.tshift) - def test_pct_change(self): df1 = DataFrame({'c1': [1, 2, 5], 'c2': [3, 4, 6]}) df2 = df1 + 1 @@ -1992,89 +707,10 @@ def test_multiindex_get(self): MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1)], names=['first', 'second']) - @pytest.mark.filterwarnings("ignore:Using a non-tuple:FutureWarning") - def test_multiindex_blocks(self): - ind = MultiIndex.from_tuples([('a', 1), ('a', 2), ('b', 1)], - names=['first', 'second']) - wp = Panel(self.panel._data) - wp.items = ind - f1 = wp['a'] - assert (f1.items == [1, 2]).all() - - f1 = wp[('b', 1)] - assert (f1.columns == ['A', 'B', 'C', 'D']).all() - def test_repr_empty(self): empty = Panel() repr(empty) - # ignore warning from us, because removing panel - @pytest.mark.filterwarnings("ignore:Using:FutureWarning") - def test_rename(self): - mapper = {'ItemA': 'foo', 'ItemB': 'bar', 'ItemC': 'baz'} - - renamed = self.panel.rename(items=mapper) - exp = Index(['foo', 'bar', 'baz']) - tm.assert_index_equal(renamed.items, exp) - - renamed = self.panel.rename(minor_axis=str.lower) - exp = Index(['a', 'b', 'c', 'd']) - tm.assert_index_equal(renamed.minor_axis, exp) - - # don't copy - renamed_nocopy = self.panel.rename(items=mapper, copy=False) - renamed_nocopy['foo'] = 3. - assert (self.panel['ItemA'].values == 3).all() - - def test_get_attr(self): - assert_frame_equal(self.panel['ItemA'], self.panel.ItemA) - - # specific cases from #3440 - self.panel['a'] = self.panel['ItemA'] - assert_frame_equal(self.panel['a'], self.panel.a) - self.panel['i'] = self.panel['ItemA'] - assert_frame_equal(self.panel['i'], self.panel.i) - - def test_to_excel(self): - try: - import xlwt # noqa - import xlrd # noqa - import openpyxl # noqa - from pandas.io.excel import ExcelFile - except ImportError: - pytest.skip("need xlwt xlrd openpyxl") - - for ext in ['xls', 'xlsx']: - with ensure_clean('__tmp__.' + ext) as path: - self.panel.to_excel(path) - try: - reader = ExcelFile(path) - except ImportError: - pytest.skip("need xlwt xlrd openpyxl") - - for item, df in self.panel.iteritems(): - recdf = reader.parse(str(item), index_col=0) - assert_frame_equal(df, recdf) - - def test_to_excel_xlsxwriter(self): - try: - import xlrd # noqa - import xlsxwriter # noqa - from pandas.io.excel import ExcelFile - except ImportError: - pytest.skip("Requires xlrd and xlsxwriter. Skipping test.") - - with ensure_clean('__tmp__.xlsx') as path: - self.panel.to_excel(path, engine='xlsxwriter') - try: - reader = ExcelFile(path) - except ImportError as e: - pytest.skip("cannot write excel file: %s" % e) - - for item, df in self.panel.iteritems(): - recdf = reader.parse(str(item), index_col=0) - assert_frame_equal(df, recdf) - @pytest.mark.filterwarnings("ignore:'.reindex:FutureWarning") def test_dropna(self): p = Panel(np.random.randn(4, 5, 6), major_axis=list('abcde')) @@ -2275,132 +911,6 @@ def test_update_deprecation(self, raise_conflict): with tm.assert_produces_warning(FutureWarning): pan.update(other, raise_conflict=raise_conflict) - def test_all_any(self): - assert (self.panel.all(axis=0).values == nanall( - self.panel, axis=0)).all() - assert (self.panel.all(axis=1).values == nanall( - self.panel, axis=1).T).all() - assert (self.panel.all(axis=2).values == nanall( - self.panel, axis=2).T).all() - assert (self.panel.any(axis=0).values == nanany( - self.panel, axis=0)).all() - assert (self.panel.any(axis=1).values == nanany( - self.panel, axis=1).T).all() - assert (self.panel.any(axis=2).values == nanany( - self.panel, axis=2).T).all() - - def test_all_any_unhandled(self): - pytest.raises(NotImplementedError, self.panel.all, bool_only=True) - pytest.raises(NotImplementedError, self.panel.any, bool_only=True) - - # GH issue 15960 - def test_sort_values(self): - pytest.raises(NotImplementedError, self.panel.sort_values) - pytest.raises(NotImplementedError, self.panel.sort_values, 'ItemA') - - -@pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") -class TestPanelFrame(object): - """ - Check that conversions to and from Panel to DataFrame work. - """ - - def setup_method(self, method): - panel = make_test_panel() - self.panel = panel.to_frame() - self.unfiltered_panel = panel.to_frame(filter_observations=False) - - def test_ops_scalar(self): - result = self.panel.mul(2) - expected = DataFrame.__mul__(self.panel, 2) - assert_frame_equal(result, expected) - - def test_combine_scalar(self): - result = self.panel.mul(2) - expected = DataFrame(self.panel._data) * 2 - assert_frame_equal(result, expected) - - def test_combine_series(self): - s = self.panel['ItemA'][:10] - result = self.panel.add(s, axis=0) - expected = DataFrame.add(self.panel, s, axis=0) - assert_frame_equal(result, expected) - - s = self.panel.iloc[5] - result = self.panel + s - expected = DataFrame.add(self.panel, s, axis=1) - assert_frame_equal(result, expected) - - def test_sort(self): - def is_sorted(arr): - return (arr[1:] > arr[:-1]).any() - - sorted_minor = self.panel.sort_index(level=1) - assert is_sorted(sorted_minor.index.codes[1]) - - sorted_major = sorted_minor.sort_index(level=0) - assert is_sorted(sorted_major.index.codes[0]) - - def test_to_string(self): - buf = StringIO() - self.panel.to_string(buf) - - def test_to_sparse(self): - if isinstance(self.panel, Panel): - msg = 'sparsifying is not supported' - with pytest.raises(NotImplementedError, match=msg): - self.panel.to_sparse - - def test_axis_dummies(self): - from pandas.core.reshape.reshape import make_axis_dummies - - minor_dummies = make_axis_dummies(self.panel, 'minor').astype(np.uint8) - assert len(minor_dummies.columns) == len(self.panel.index.levels[1]) - - major_dummies = make_axis_dummies(self.panel, 'major').astype(np.uint8) - assert len(major_dummies.columns) == len(self.panel.index.levels[0]) - - mapping = {'A': 'one', 'B': 'one', 'C': 'two', 'D': 'two'} - - transformed = make_axis_dummies(self.panel, 'minor', - transform=mapping.get).astype(np.uint8) - assert len(transformed.columns) == 2 - tm.assert_index_equal(transformed.columns, Index(['one', 'two'])) - - # TODO: test correctness - - def test_get_dummies(self): - from pandas.core.reshape.reshape import get_dummies, make_axis_dummies - - self.panel['Label'] = self.panel.index.codes[1] - minor_dummies = make_axis_dummies(self.panel, 'minor').astype(np.uint8) - dummies = get_dummies(self.panel['Label']) - tm.assert_numpy_array_equal(dummies.values, minor_dummies.values) - - def test_count(self): - index = self.panel.index - - major_count = self.panel.count(level=0)['ItemA'] - level_codes = index.codes[0] - for i, idx in enumerate(index.levels[0]): - assert major_count[i] == (level_codes == i).sum() - - minor_count = self.panel.count(level=1)['ItemA'] - level_codes = index.codes[1] - for i, idx in enumerate(index.levels[1]): - assert minor_count[i] == (level_codes == i).sum() - - def test_join(self): - lp1 = self.panel.filter(['ItemA', 'ItemB']) - lp2 = self.panel.filter(['ItemC']) - - joined = lp1.join(lp2) - - assert len(joined.columns) == 3 - - pytest.raises(Exception, lp1.join, - self.panel.filter(['ItemB', 'ItemC'])) - def test_panel_index(): index = panelm.panel_index([1, 2, 3, 4], [1, 2, 3]) diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 053927a77c612..387f402348513 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -34,7 +34,7 @@ import pandas as pd from pandas import ( Categorical, CategoricalIndex, DataFrame, DatetimeIndex, Index, - IntervalIndex, MultiIndex, Panel, RangeIndex, Series, bdate_range) + IntervalIndex, MultiIndex, RangeIndex, Series, bdate_range) from pandas.core.algorithms import take_1d from pandas.core.arrays import ( DatetimeArray, ExtensionArray, IntervalArray, PeriodArray, TimedeltaArray, @@ -2052,14 +2052,6 @@ def makePeriodFrame(nper=None): return DataFrame(data) -def makePanel(nper=None): - with warnings.catch_warnings(record=True): - warnings.filterwarnings("ignore", "\\nPanel", FutureWarning) - cols = ['Item' + c for c in string.ascii_uppercase[:K - 1]] - data = {c: makeTimeDataFrame(nper) for c in cols} - return Panel.fromDict(data) - - def makeCustomIndex(nentries, nlevels, prefix='#', names=False, ndupe_l=None, idx_type=None): """Create an index/multindex with given dimensions, levels, names, etc' @@ -2307,15 +2299,6 @@ def makeMissingDataframe(density=.9, random_state=None): return df -def add_nans(panel): - I, J, N = panel.shape - for i, item in enumerate(panel.items): - dm = panel[item] - for j, col in enumerate(dm.columns): - dm[col][:i + j] = np.NaN - return panel - - class TestSubDict(dict): def __init__(self, *args, **kwargs): From eb2ab3a4d20e06debb670a9891aed8be58b1111c Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Fri, 8 Feb 2019 15:34:56 -0800 Subject: [PATCH 083/215] DEPR: Remove Panel-specific parts of io.pytables (#25233) --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/internals/__init__.py | 2 +- pandas/core/internals/blocks.py | 35 ----- pandas/io/pytables.py | 121 ++---------------- .../tests/io/data/legacy_hdf/legacy_table.h5 | Bin 211111 -> 0 bytes pandas/tests/io/test_pytables.py | 25 ---- 6 files changed, 15 insertions(+), 170 deletions(-) delete mode 100644 pandas/tests/io/data/legacy_hdf/legacy_table.h5 diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index cbefae07b07f1..6b6010fa9c605 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -51,7 +51,7 @@ Deprecations Removal of prior version deprecations/changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Removed (parts of) :class:`Panel` (:issue:`25047`) +- Removed (parts of) :class:`Panel` (:issue:`25047`,:issue:`25191`,:issue:`25231`) - - - diff --git a/pandas/core/internals/__init__.py b/pandas/core/internals/__init__.py index 7878613a8b1b1..a662e1d3ae197 100644 --- a/pandas/core/internals/__init__.py +++ b/pandas/core/internals/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from .blocks import ( # noqa:F401 - _block2d_to_blocknd, _factor_indexer, _block_shape, # io.pytables + _block_shape, # io.pytables _safe_reshape, # io.packers make_block, # io.pytables, io.packers FloatBlock, IntBlock, ComplexBlock, BoolBlock, ObjectBlock, diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index d966b31a22932..ac7d21de442db 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -3134,31 +3134,6 @@ def _merge_blocks(blocks, dtype=None, _can_consolidate=True): return blocks -def _block2d_to_blocknd(values, placement, shape, labels, ref_items): - """ pivot to the labels shape """ - panel_shape = (len(placement),) + shape - - # TODO: lexsort depth needs to be 2!! - - # Create observation selection vector using major and minor - # labels, for converting to panel format. - selector = _factor_indexer(shape[1:], labels) - mask = np.zeros(np.prod(shape), dtype=bool) - mask.put(selector, True) - - if mask.all(): - pvalues = np.empty(panel_shape, dtype=values.dtype) - else: - dtype, fill_value = maybe_promote(values.dtype) - pvalues = np.empty(panel_shape, dtype=dtype) - pvalues.fill(fill_value) - - for i in range(len(placement)): - pvalues[i].flat[mask] = values[:, i] - - return make_block(pvalues, placement=placement) - - def _safe_reshape(arr, new_shape): """ If possible, reshape `arr` to have shape `new_shape`, @@ -3181,16 +3156,6 @@ def _safe_reshape(arr, new_shape): return arr -def _factor_indexer(shape, labels): - """ - given a tuple of shape and a list of Categorical labels, return the - expanded label indexer - """ - mult = np.array(shape)[::-1].cumprod()[::-1] - return ensure_platform_int( - np.sum(np.array(labels).T * np.append(mult, [1]), axis=1).T) - - def _putmask_smart(v, m, n): """ Return a new ndarray, try to preserve dtype if possible. diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index 5a96b3e2db563..2ee8759b9bdd8 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -15,34 +15,29 @@ import numpy as np -from pandas._libs import algos, lib, writers as libwriters +from pandas._libs import lib, writers as libwriters from pandas._libs.tslibs import timezones from pandas.compat import PY3, filter, lrange, range, string_types from pandas.errors import PerformanceWarning from pandas.core.dtypes.common import ( - ensure_int64, ensure_object, ensure_platform_int, is_categorical_dtype, - is_datetime64_dtype, is_datetime64tz_dtype, is_list_like, - is_timedelta64_dtype) + ensure_object, is_categorical_dtype, is_datetime64_dtype, + is_datetime64tz_dtype, is_list_like, is_timedelta64_dtype) from pandas.core.dtypes.missing import array_equivalent from pandas import ( - DataFrame, DatetimeIndex, Index, Int64Index, MultiIndex, Panel, - PeriodIndex, Series, SparseDataFrame, SparseSeries, TimedeltaIndex, compat, - concat, isna, to_datetime) + DataFrame, DatetimeIndex, Index, Int64Index, MultiIndex, PeriodIndex, + Series, SparseDataFrame, SparseSeries, TimedeltaIndex, compat, concat, + isna, to_datetime) from pandas.core import config -from pandas.core.algorithms import unique -from pandas.core.arrays.categorical import ( - Categorical, _factorize_from_iterables) +from pandas.core.arrays.categorical import Categorical from pandas.core.arrays.sparse import BlockIndex, IntIndex from pandas.core.base import StringMixin import pandas.core.common as com from pandas.core.computation.pytables import Expr, maybe_expression from pandas.core.config import get_option from pandas.core.index import ensure_index -from pandas.core.internals import ( - BlockManager, _block2d_to_blocknd, _block_shape, _factor_indexer, - make_block) +from pandas.core.internals import BlockManager, _block_shape, make_block from pandas.io.common import _stringify_path from pandas.io.formats.printing import adjoin, pprint_thing @@ -175,7 +170,6 @@ class DuplicateWarning(Warning): SparseSeries: u'sparse_series', DataFrame: u'frame', SparseDataFrame: u'sparse_frame', - Panel: u'wide', } # storer class map @@ -187,7 +181,6 @@ class DuplicateWarning(Warning): u'sparse_series': 'SparseSeriesFixed', u'frame': 'FrameFixed', u'sparse_frame': 'SparseFrameFixed', - u'wide': 'PanelFixed', } # table class map @@ -198,14 +191,11 @@ class DuplicateWarning(Warning): u'appendable_frame': 'AppendableFrameTable', u'appendable_multiframe': 'AppendableMultiFrameTable', u'worm': 'WORMTable', - u'legacy_frame': 'LegacyFrameTable', - u'legacy_panel': 'LegacyPanelTable', } # axes map _AXES_MAP = { DataFrame: [0], - Panel: [1, 2] } # register our configuration options @@ -864,7 +854,7 @@ def put(self, key, value, format=None, append=False, **kwargs): Parameters ---------- key : object - value : {Series, DataFrame, Panel} + value : {Series, DataFrame} format : 'fixed(f)|table(t)', default is 'fixed' fixed(f) : Fixed format Fast writing/reading. Not-appendable, nor searchable @@ -946,7 +936,7 @@ def append(self, key, value, format=None, append=True, columns=None, Parameters ---------- key : object - value : {Series, DataFrame, Panel} + value : {Series, DataFrame} format : 'table' is the default table(t) : table format Write as a PyTables Table structure which may perform @@ -3027,16 +3017,6 @@ class FrameFixed(BlockManagerFixed): obj_type = DataFrame -class PanelFixed(BlockManagerFixed): - pandas_kind = u'wide' - obj_type = Panel - is_shape_reversed = True - - def write(self, obj, **kwargs): - obj._consolidate_inplace() - return super(PanelFixed, self).write(obj, **kwargs) - - class Table(Fixed): """ represent a table: @@ -3899,85 +3879,11 @@ def read(self, where=None, columns=None, **kwargs): if not self.read_axes(where=where, **kwargs): return None - lst_vals = [a.values for a in self.index_axes] - labels, levels = _factorize_from_iterables(lst_vals) - # labels and levels are tuples but lists are expected - labels = list(labels) - levels = list(levels) - N = [len(lvl) for lvl in levels] - - # compute the key - key = _factor_indexer(N[1:], labels) - - objs = [] - if len(unique(key)) == len(key): - - sorter, _ = algos.groupsort_indexer( - ensure_int64(key), np.prod(N)) - sorter = ensure_platform_int(sorter) - - # create the objs - for c in self.values_axes: - - # the data need to be sorted - sorted_values = c.take_data().take(sorter, axis=0) - if sorted_values.ndim == 1: - sorted_values = sorted_values.reshape( - (sorted_values.shape[0], 1)) - - take_labels = [l.take(sorter) for l in labels] - items = Index(c.values) - block = _block2d_to_blocknd( - values=sorted_values, placement=np.arange(len(items)), - shape=tuple(N), labels=take_labels, ref_items=items) - - # create the object - mgr = BlockManager([block], [items] + levels) - obj = self.obj_type(mgr) - - # permute if needed - if self.is_transposed: - obj = obj.transpose( - *tuple(Series(self.data_orientation).argsort())) - - objs.append(obj) - - else: - raise NotImplementedError("Panel is removed in pandas 0.25.0") - - # create the composite object - if len(objs) == 1: - wp = objs[0] - else: - wp = concat(objs, axis=0, verify_integrity=False)._consolidate() - - # apply the selection filters & axis orderings - wp = self.process_axes(wp, columns=columns) - - return wp - - -class LegacyFrameTable(LegacyTable): - - """ support the legacy frame table """ - pandas_kind = u'frame_table' - table_type = u'legacy_frame' - obj_type = Panel - - def read(self, *args, **kwargs): - return super(LegacyFrameTable, self).read(*args, **kwargs)['value'] - - -class LegacyPanelTable(LegacyTable): - - """ support the legacy panel table """ - table_type = u'legacy_panel' - obj_type = Panel + raise NotImplementedError("Panel is removed in pandas 0.25.0") class AppendableTable(LegacyTable): - - """ suppor the new appendable table formats """ + """ support the new appendable table formats """ _indexables = None table_type = u'appendable' @@ -4209,8 +4115,7 @@ def delete(self, where=None, start=None, stop=None, **kwargs): class AppendableFrameTable(AppendableTable): - - """ suppor the new appendable table formats """ + """ support the new appendable table formats """ pandas_kind = u'frame_table' table_type = u'appendable_frame' ndim = 2 diff --git a/pandas/tests/io/data/legacy_hdf/legacy_table.h5 b/pandas/tests/io/data/legacy_hdf/legacy_table.h5 deleted file mode 100644 index 1c90382d9125c039e40b821f41451b989d1c2b36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211111 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 diff --git a/pandas/tests/io/test_pytables.py b/pandas/tests/io/test_pytables.py index 4a806b178c6ee..b464903d8b4e0 100644 --- a/pandas/tests/io/test_pytables.py +++ b/pandas/tests/io/test_pytables.py @@ -141,7 +141,6 @@ def teardown_method(self, method): @pytest.mark.single -@pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") class TestHDFStore(Base): def test_format_kwarg_in_constructor(self): @@ -3984,30 +3983,6 @@ def test_legacy_table_read_py2(self, datapath): }) assert_frame_equal(expected, result) - def test_legacy_table_read(self, datapath): - # legacy table types - with ensure_clean_store( - datapath('io', 'data', 'legacy_hdf', 'legacy_table.h5'), - mode='r') as store: - - with catch_warnings(): - simplefilter("ignore", pd.io.pytables.IncompatibilityWarning) - store.select('df1') - store.select('df2') - store.select('wp1') - - # force the frame - store.select('df2', typ='legacy_frame') - - # old version warning - pytest.raises( - Exception, store.select, 'wp1', 'minor_axis=B') - - df2 = store.select('df2') - result = store.select('df2', 'index>df2.index[2]') - expected = df2[df2.index > df2.index[2]] - assert_frame_equal(expected, result) - def test_copy(self): with catch_warnings(record=True): From 04df22fbeec782f474bcff20480eded04e0d1d82 Mon Sep 17 00:00:00 2001 From: Ryan Nazareth Date: Fri, 8 Feb 2019 23:37:37 +0000 Subject: [PATCH 084/215] DEPR: Add Deprecated warning for timedelta with passed units M and Y (#23264) --- doc/source/whatsnew/v0.25.0.rst | 5 +---- pandas/_libs/tslibs/timedeltas.pyx | 5 +++++ pandas/core/indexes/timedeltas.py | 5 +++++ pandas/core/tools/timedeltas.py | 7 +++++++ .../indexes/timedeltas/test_timedelta.py | 8 ++++++++ .../tests/scalar/timedelta/test_timedelta.py | 20 +++++++++++++++++++ 6 files changed, 46 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 6b6010fa9c605..4032dc20b2e19 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -42,10 +42,7 @@ Other API Changes Deprecations ~~~~~~~~~~~~ -- -- -- - +- Deprecated the `M (months)` and `Y (year)` `units` parameter of :func: `pandas.to_timedelta`, :func: `pandas.Timedelta` and :func: `pandas.TimedeltaIndex` (:issue:`16344`) .. _whatsnew_0250.prior_deprecations: diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 0a19d8749fc7c..f08a57375a301 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -1158,6 +1158,11 @@ class Timedelta(_Timedelta): "[weeks, days, hours, minutes, seconds, " "milliseconds, microseconds, nanoseconds]") + if unit in {'Y', 'y', 'M'}: + warnings.warn("M and Y units are deprecated and " + "will be removed in a future version.", + FutureWarning, stacklevel=1) + if isinstance(value, Timedelta): value = value.value elif is_string_object(value): diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index cbe5ae198838f..830925535dab1 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -207,6 +207,11 @@ def __new__(cls, data=None, unit=None, freq=None, start=None, end=None, 'collection of some kind, {data} was passed' .format(cls=cls.__name__, data=repr(data))) + if unit in {'Y', 'y', 'M'}: + warnings.warn("M and Y units are deprecated and " + "will be removed in a future version.", + FutureWarning, stacklevel=2) + if isinstance(data, TimedeltaArray): if copy: data = data.copy() diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index ddd21d0f62d08..30cb15f311b9f 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -2,6 +2,8 @@ timedelta support tools """ +import warnings + import numpy as np from pandas._libs.tslibs.timedeltas import Timedelta, parse_timedelta_unit @@ -90,6 +92,11 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'): raise ValueError("errors must be one of 'ignore', " "'raise', or 'coerce'}") + if unit in {'Y', 'y', 'M'}: + warnings.warn("M and Y units are deprecated and " + "will be removed in a future version.", + FutureWarning, stacklevel=2) + if arg is None: return arg elif isinstance(arg, ABCSeries): diff --git a/pandas/tests/indexes/timedeltas/test_timedelta.py b/pandas/tests/indexes/timedeltas/test_timedelta.py index 79210705103ab..3cbd9942f9d84 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta.py @@ -1,4 +1,5 @@ from datetime import timedelta +import re import numpy as np import pytest @@ -325,6 +326,13 @@ def test_freq_conversion(self): result = td.astype('timedelta64[s]') assert_index_equal(result, expected) + @pytest.mark.parametrize('unit', ['Y', 'y', 'M']) + def test_unit_m_y_deprecated(self, unit): + with tm.assert_produces_warning(FutureWarning) as w: + TimedeltaIndex([1, 3, 7], unit) + msg = r'.* units are deprecated .*' + assert re.match(msg, str(w[0].message)) + class TestTimeSeries(object): diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index e1838e0160fec..7d5b479810205 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -1,5 +1,6 @@ """ test the scalar Timedelta """ from datetime import timedelta +import re import numpy as np import pytest @@ -317,6 +318,7 @@ def test_nat_converters(self): assert result.dtype.kind == 'm' assert result.astype('int64') == iNaT + @pytest.mark.filterwarnings("ignore:M and Y units are deprecated") @pytest.mark.parametrize('units, np_unit', [(['Y', 'y'], 'Y'), (['M'], 'M'), @@ -376,6 +378,24 @@ def test_unit_parser(self, units, np_unit, wrapper): result = Timedelta('2{}'.format(unit)) assert result == expected + @pytest.mark.skipif(compat.PY2, reason="requires python3.5 or higher") + @pytest.mark.parametrize('unit', ['Y', 'y', 'M']) + def test_unit_m_y_deprecated(self, unit): + with tm.assert_produces_warning(FutureWarning) as w1: + Timedelta(10, unit) + msg = r'.* units are deprecated .*' + assert re.match(msg, str(w1[0].message)) + with tm.assert_produces_warning(FutureWarning, + check_stacklevel=False) as w2: + to_timedelta(10, unit) + msg = r'.* units are deprecated .*' + assert re.match(msg, str(w2[0].message)) + with tm.assert_produces_warning(FutureWarning, + check_stacklevel=False) as w3: + to_timedelta([1, 2], unit) + msg = r'.* units are deprecated .*' + assert re.match(msg, str(w3[0].message)) + def test_numeric_conversions(self): assert Timedelta(0) == np.timedelta64(0, 'ns') assert Timedelta(10) == np.timedelta64(10, 'ns') From 977bcf873dc7fc39cd2ba7c826636cfef9d25167 Mon Sep 17 00:00:00 2001 From: Justin Zheng Date: Sat, 9 Feb 2019 08:52:50 -0800 Subject: [PATCH 085/215] BUG-25061 fix printing indices with NaNs (#25202) --- doc/source/whatsnew/v0.24.2.rst | 2 +- pandas/io/formats/format.py | 11 +++++++++-- pandas/tests/series/test_repr.py | 8 ++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 73df504c89d5b..30338f6fb876c 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -52,7 +52,7 @@ Bug Fixes **I/O** - Bug in reading a HDF5 table-format ``DataFrame`` created in Python 2, in Python 3 (:issue:`24925`) -- +- Bug where float indexes could have misaligned values when printing (:issue:`25061`) - **Categorical** diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 62fa04e784072..f68ef2cc39006 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1060,19 +1060,26 @@ def get_result_as_array(self): def format_values_with(float_format): formatter = self._value_formatter(float_format, threshold) + # default formatter leaves a space to the left when formatting + # floats, must be consistent for left-justifying NaNs (GH #25061) + if self.justify == 'left': + na_rep = ' ' + self.na_rep + else: + na_rep = self.na_rep + # separate the wheat from the chaff values = self.values mask = isna(values) if hasattr(values, 'to_dense'): # sparse numpy ndarray values = values.to_dense() values = np.array(values, dtype='object') - values[mask] = self.na_rep + values[mask] = na_rep imask = (~mask).ravel() values.flat[imask] = np.array([formatter(val) for val in values.ravel()[imask]]) if self.fixed_width: - return _trim_zeros(values, self.na_rep) + return _trim_zeros(values, na_rep) return values diff --git a/pandas/tests/series/test_repr.py b/pandas/tests/series/test_repr.py index b4e7708e2456e..842207f2a572f 100644 --- a/pandas/tests/series/test_repr.py +++ b/pandas/tests/series/test_repr.py @@ -198,6 +198,14 @@ def test_latex_repr(self): assert s._repr_latex_() is None + def test_index_repr_in_frame_with_nan(self): + # see gh-25061 + i = Index([1, np.nan]) + s = Series([1, 2], index=i) + exp = """1.0 1\nNaN 2\ndtype: int64""" + + assert repr(s) == exp + class TestCategoricalRepr(object): From d7a0964b359d37ef92045f24a570aa92a78568dd Mon Sep 17 00:00:00 2001 From: Gioia Ballin Date: Sat, 9 Feb 2019 16:54:09 +0000 Subject: [PATCH 086/215] BUG: Fix regression in DataFrame.apply causing RecursionError (#25230) * BUG: Fix regression in DataFrame.apply causing RecursionError * Add feedback from PR * Add feedback after further code review * Add feedback after further code review 2 --- doc/source/whatsnew/v0.24.2.rst | 2 +- pandas/core/dtypes/inference.py | 13 ++++++++----- pandas/tests/dtypes/test_inference.py | 8 +++++--- pandas/tests/frame/test_apply.py | 7 +++++++ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 30338f6fb876c..c95ed818c9da0 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -21,8 +21,8 @@ Fixed Regressions ^^^^^^^^^^^^^^^^^ - Fixed regression in :meth:`DataFrame.all` and :meth:`DataFrame.any` where ``bool_only=True`` was ignored (:issue:`25101`) - - Fixed issue in ``DataFrame`` construction with passing a mixed list of mixed types could segfault. (:issue:`25075`) +- Fixed regression in :meth:`DataFrame.apply` causing ``RecursionError`` when ``dict``-like classes were passed as argument. (:issue:`25196`) .. _whatsnew_0242.enhancements: diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 92972c83cea53..1a02623fa6072 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -397,12 +397,15 @@ def is_dict_like(obj): True >>> is_dict_like([1, 2, 3]) False + >>> is_dict_like(dict) + False + >>> is_dict_like(dict()) + True """ - for attr in ("__getitem__", "keys", "__contains__"): - if not hasattr(obj, attr): - return False - - return True + dict_like_attrs = ("__getitem__", "keys", "__contains__") + return (all(hasattr(obj, attr) for attr in dict_like_attrs) + # [GH 25196] exclude classes + and not isinstance(obj, type)) def is_named_tuple(obj): diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 89662b70a39ad..49a66efaffc11 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -159,13 +159,15 @@ def test_is_nested_list_like_fails(obj): @pytest.mark.parametrize( - "ll", [{}, {'A': 1}, Series([1])]) + "ll", [{}, {'A': 1}, Series([1]), collections.defaultdict()]) def test_is_dict_like_passes(ll): assert inference.is_dict_like(ll) -@pytest.mark.parametrize( - "ll", ['1', 1, [1, 2], (1, 2), range(2), Index([1])]) +@pytest.mark.parametrize("ll", [ + '1', 1, [1, 2], (1, 2), range(2), Index([1]), + dict, collections.defaultdict, Series +]) def test_is_dict_like_fails(ll): assert not inference.is_dict_like(ll) diff --git a/pandas/tests/frame/test_apply.py b/pandas/tests/frame/test_apply.py index ade527a16c902..a4cd1aa3bacb6 100644 --- a/pandas/tests/frame/test_apply.py +++ b/pandas/tests/frame/test_apply.py @@ -318,6 +318,13 @@ def test_apply_reduce_Series(self, float_frame): result = float_frame.apply(np.mean, axis=1) assert_series_equal(result, expected) + def test_apply_reduce_rows_to_dict(self): + # GH 25196 + data = pd.DataFrame([[1, 2], [3, 4]]) + expected = pd.Series([{0: 1, 1: 3}, {0: 2, 1: 4}]) + result = data.apply(dict) + assert_series_equal(result, expected) + def test_apply_differently_indexed(self): df = DataFrame(np.random.randn(20, 10)) From 7ee5b92ce4c7fb37337b07ea23493de60d2e63cb Mon Sep 17 00:00:00 2001 From: Albert Villanova del Moral Date: Sat, 9 Feb 2019 17:56:52 +0100 Subject: [PATCH 087/215] BUG: Fix read_json orient='table' without index (#25170) (#25171) --- doc/source/whatsnew/v0.24.2.rst | 1 + pandas/io/json/table_schema.py | 15 ++++++++------- pandas/tests/io/json/test_pandas.py | 10 ++++++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index c95ed818c9da0..b0f287cf0b9f6 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -52,6 +52,7 @@ Bug Fixes **I/O** - Bug in reading a HDF5 table-format ``DataFrame`` created in Python 2, in Python 3 (:issue:`24925`) +- Bug in reading a JSON with ``orient='table'`` generated by :meth:`DataFrame.to_json` with ``index=False`` (:issue:`25170`) - Bug where float indexes could have misaligned values when printing (:issue:`25061`) - diff --git a/pandas/io/json/table_schema.py b/pandas/io/json/table_schema.py index 2bd93b19d4225..971386c91944e 100644 --- a/pandas/io/json/table_schema.py +++ b/pandas/io/json/table_schema.py @@ -314,12 +314,13 @@ def parse_table_schema(json, precise_float): df = df.astype(dtypes) - df = df.set_index(table['schema']['primaryKey']) - if len(df.index.names) == 1: - if df.index.name == 'index': - df.index.name = None - else: - df.index.names = [None if x.startswith('level_') else x for x in - df.index.names] + if 'primaryKey' in table['schema']: + df = df.set_index(table['schema']['primaryKey']) + if len(df.index.names) == 1: + if df.index.name == 'index': + df.index.name = None + else: + df.index.names = [None if x.startswith('level_') else x for x in + df.index.names] return df diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py index c5fcb9fb0f672..0ffc8c978a228 100644 --- a/pandas/tests/io/json/test_pandas.py +++ b/pandas/tests/io/json/test_pandas.py @@ -1262,3 +1262,13 @@ def test_index_false_error_to_json(self, orient): "'orient' is 'split' or 'table'") with pytest.raises(ValueError, match=msg): df.to_json(orient=orient, index=False) + + @pytest.mark.parametrize('orient', ['split', 'table']) + @pytest.mark.parametrize('index', [True, False]) + def test_index_false_from_json_to_json(self, orient, index): + # GH25170 + # Test index=False in from_json to_json + expected = DataFrame({'a': [1, 2], 'b': [3, 4]}) + dfjson = expected.to_json(orient=orient, index=index) + result = read_json(dfjson, orient=orient) + assert_frame_equal(result, expected) From f67b7fd2c75db951e63513003b1c6d3d1161a4b2 Mon Sep 17 00:00:00 2001 From: Christopher Whelan Date: Sat, 9 Feb 2019 09:25:58 -0800 Subject: [PATCH 088/215] BLD: prevent asv from calling sys.stdin.close() by using different launch method (#25237) --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f0567d76659b6..c86d5c50705a8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -104,7 +104,7 @@ jobs: if git diff upstream/master --name-only | grep -q "^asv_bench/"; then cd asv_bench asv machine --yes - ASV_OUTPUT="$(asv dev)" + ASV_OUTPUT="$(asv run --quick --show-stderr --python=same --launch-method=spawn)" if [[ $(echo "$ASV_OUTPUT" | grep "failed") ]]; then echo "##vso[task.logissue type=error]Benchmarks run with errors" echo "$ASV_OUTPUT" From a4f598778058fe4ad8ca5486f64b8a5078c318c0 Mon Sep 17 00:00:00 2001 From: MusTheDataGuy Date: Sat, 9 Feb 2019 17:26:38 +0000 Subject: [PATCH 089/215] (Closes #25029) Removed extra bracket from cheatsheet code example. (#25032) --- doc/cheatsheet/Pandas_Cheat_Sheet.pdf | Bin 339134 -> 345999 bytes doc/cheatsheet/Pandas_Cheat_Sheet.pptx | Bin 105265 -> 105004 bytes doc/cheatsheet/Pandas_Cheat_Sheet_JA.pdf | Bin 206153 -> 420841 bytes doc/cheatsheet/Pandas_Cheat_Sheet_JA.pptx | Bin 76495 -> 82359 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/cheatsheet/Pandas_Cheat_Sheet.pdf b/doc/cheatsheet/Pandas_Cheat_Sheet.pdf index 696ed288cf7a6067e73e819213b014e6d4ee6b32..d50896dc5ccc52afb31899a9ed704149d3e1688e 100644 GIT binary patch delta 262099 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 _n%!uxt;3y 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%0DbU7`_nH;AP=zz|w9Ob+M;s2M1( zpdKKEITss@ku(U#d73|Mc%FZ@cNju7F(xk7@y)i z+&8^rP?G1NK(57Xey_?2Y{?!KC>7={(I16d(Hk*IWMCgv?y6dyD((ujsu1 zA4lPsO`z?Zis{}u#h&@oY{b;*PR|?EFwOE^JHfoRUa+aT2B0h4CHgZsqc8PXk##oY zN5nObysE8tRe2+g$A^lQ@ewt=;2ZCQ*CcyJ;g4gZ5E%R#5d256AJX_Q@5#GU;yMTV zz`f$okzaA%`-WdJ)-(i|%5DY&0Ky!8MTkRjhZTg;qBJ^spA@jQA!H{bJeOt5g`OXuN7&OsQ8kp!EL+55-&h^66p@n9IHxaKw|*x)u*dp za}>)45^ZbnUKId8J+`SZHBYbd4ufd!BE(bweX>pNf@5V-p0A@w|emfYPK3KUW& zk1llKPSqkRiY^gxoF|rj)c14`Xb6n%lI+&%)-x{1DQLg#Z>uz`=z(cT*6xv4d}=}wfEs{5nWbz4;5x8n&k}Hqj17!NGRqN zvum_4{K2(}z*e4Ai25Xu4-STVr)UQXzVS)rRXhT7l4QD_R;GZbp*P0ec|aiA($dht z4`m*zx93%)%;WO`pm~(?Gx@tfadZ5w?Cj*alI76=HS_u2Ast?0(Bn({U5;?h(6=r} zhu4JuNPJeXSoK*&tK0YSRDTN?VxSteEG{1Y&OW-=E{2f#L0r!dR|Jf~v7=e8dVLvR z0$D44b^jv>$Yc{!PdY?MuHO5{p zF-5WaJyA&Q<7uGC*niYiQu?m3@?9byDr(TNt#=p?+2*SkB<`;xU{u z%S*!>`(-QZv8x%YT`g5W+X<{Gv+3j;I6OV0zwk7^w83U=9mqJOr@s*-lhYEuJU|Ya z&vbNI$O!!6zUFfA)la2p9Vf~0F}^S<)!QAcTFss3bnv-~jYa8hcMG53pH$DX&empK z)_1*tonhA$pGFkjdWG39EtX0JwtMJ{9jRE>QLOd( zuOvnNp;hsyoYyj~9JmJR+TH6+cT{nr8K?DXXwaS~^lqJYVU{RxjafJRX(S`44Bkyf zED2i>N&m){=!J^mhlLT-kyl!?;YlRzBYPdXB=QHqh-P&uD>uuEiQ?<~CZ}68Qv66= zX=7<)-#tz9yIWjuo3G$^R)69tLRKGw#1o@*lY4$!QcDbB$tq-W9|zZp=x2T5AvFW_Gmf*S zcLoLr?X4$LRv!lR+49`oZ6an@y;%pIVd|)>$*sZda@jK5uoGJgA7HhQzT|DP$-GjFzb!awgm@#}^M}lX`^v z)@XfFe-X|Zh0|KYRAI~E60IPev4Lu8OfBnmEgP0QKxIcx)gd=nYGFq!0I6cHXX?W{ zikP6{K`V2A-0(rF%?oal{v~JXATt&o+Aht_8`f4K6s?H!8Xx^DscWD>^u{2xQ9k7Q z@@IRjVNm-Du%0D2Zk?ZsN`*N4q=$`nGsd9KqS@t4fvq%|Aq&KQvz3 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
  • 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%0D@siwb`Zu?H`V%-Y=d#VR2%fAYT2wUY7Pxp}s zMpJg(ftPt*ZRzXbkGPVzpm6F(?+1gg3FA=Wod^m}EIaJ7rj(qpvoq@`KMZys<=!W* z3~k6C`RPmc8BN|cUes_=6Yf6IIP@?YsW*z|Ry zudPG^^+|}mA2wMV2(;jEI-NIifP`pgR!_8q$&m@z}NWHan?F5DVeB9w>9KR4B^~GhJ zj@Fu{LjnND3%)wmF~R%9wLwo*IYR6qF0mqk%ea_Rr-ce%CA*u8g9@XRT~3s3iwCKi z1rPg!%UzYE9vVH;KPXx8>KBeGzikT6iL2MCw2WG`DEYE8bw>6so~w`KQq=E-sK4Be zJW7GP3j&@rs19#8xVBXvNVf8%aGcwp^k5fVV#$El;B}>l-xp>L#kl71C%yk!7y8aC zoT)pzc&1ei*Ynrl>0g(U;6lqrmv|?Ia~jP?4o>_s99#QlEweQQ;(;g=m0OjC#Ab2r zblQsbRcs-_D8+zV)@ng^{1ZeD;*Ff35tAR3oAw(5A?#WTzcn((;0%)sSWXyh;nOS{ zG>ZVnGAe0Jt8P`4Uz$AVQZ|c@o_YhE)?9XX&c*`@P9a3ayZgUz7dj6RL*h(+fEAl+ zmf5DHNe3JZ5Qp_j&24{2<0)$sJA{MQd8V4(nA!%woeEMZd(W)8J^h0vI?glZOY;kL zw4j!q=8V2DE4b~EE3@Y9_2W;{AyB1%DT4qD!Z0Td-_YSBI4&N;;L~t>j+vC;n{o3V ze1)sW_DrWO-;CW0^2iAjmA#Mh)EFT%yqFuacDs`ScA+jsLIvN>3t|cWO9mUj5Jkx?4R{v<5xd^2vob_&-Q;F@FTSm zv6udFWF%Yh$l;hjIB#zaL|0b(BF6<5mE_EGxA87yb}zp9*|rqzlk-+^>R;puvXyl1 z6a^VMKkbeR1lXp;*&bgP|2x}2Tf4$KSt+bBpP{j$y@(-9D$Ds&^CW7;^X7jAU6g$H?4)n zRxrtw*IEs&CD{e*pt+fe-lJ3dUhhLpRIXB1Cvy#hVya&1S6Pcen&LH3)EWVx3& z)CK2{*+YtFqSb}xRjrU4u)*Udamg1Ts#bJK6}GLUub}*chOR^>zlFW5Y;}q+Jt*bJ zw-a^!ODb!DO&@SW)5Vdfo|tn;w5w2KVxW;t&Rv%MKEo z`liBkT4!1SoNFyDWIbmS?X5bNPS*$V9zm7>=T_4hI)wh(B8}wx0utj|SKUGzKR2(B zW$SoK@^63pX%m9ImW$!o2Mj96I@sWkAM=XIe#01;#tkW*$SiBNw%l)OgMe z+*KJJL*24fX{mLkR@aOgvDi12jBW*9yB5YO&d4OoMW{lhE#!s=T|7?+W%`WH1%Rah zlobeJr!+QWDvq$r#(^$~WHz0rCKbV3b}Gh}@mo15SJo)O$|FpxiLNrF+ayHzrDs$zx72`?}BmFBQmH7C(>0=`13;xZ@WWnrkDA? zi3;s+Z1!)y73c(DqeQu%{8>$O8N>3F3@?md5Aj9?dzXs!p%!7pi95ZkwCK%Aa6{?| zul!|>pd|LF=w(jfj^YcErOUdgOzNF4W4~#&J=Ux}4OyX(G6P$ufXy?tJp|G|tA~eA zENTl;OtAL~&~G2xnwbl7~qg zHH~t0;hnuZFWG_q;Odxp+$lPFcWx`c1J&)S*wy~M7JO83H?==EAf;b3RWO>>X0AJw zngR_$=BF%JKyZwCVWH1TvY%4p(VZ3!8ME3H6H%W+t<*~>!x19{8xVi^9kZMceg%-mkzkN`XcR^0=07-yb@xdY zKa>~`V?#FZDegcwK0KYU;JqMOzBipo?XcJ|6lep84ZL)seEq4P7m_yi_>XlZ9!%te zo|X?bR>Aao)2By&e_w$&Rlz6``13tQIAUW)tSrNye!FW#c4Sm)Qf zn0Kr*^(|A<>i&p^w9_o}w(hm|Y~ob4nK%fJ-9~H@*2D&=GeZ>Ge6WH(ikRJ0#@rjs zpfyx=846OaGeTOtEZE22%RC&aq1xFd?M~3)xuh$OahuRxaTF!mT^~MnC*=&e%|a9( zf+_-n@SRS#yvudeF(h4PNDnAspN2sTlwo=doRPJqMIkTMUx(eFus=C-iz-i0wowg9 zg{q(K=>wKb1HUZp4{5E%!kQG?ki28?P;u3mcn9>g9T@mU4*f z44S)6jr3GWW=GcAP5Sj{$#!3l+&b8cA zIwzVuQCz*)Vsu)7%}aS->PQl3-<>$B?n1%v+s~C((1CULKoYK)dZ!tzE<`Yv3Oj(^ zrm>^K0`+mLeYH@+(_rwb2Da(p!g9_m^5UWugdaGnux!0+ln!~)cOH6uHsB;amS7D4 z0SR`xW59v}QLgc#tTDU71uPdT&VuOm=6`PE+xI?NW~4Dor|c}Itc^5cvFo;3N$L$j zL$s}#9OcAgt}f}$MGIJa&P=8RyaCr7%bom9KP)fpht@SLtG}@Z#;?Em{r~p=qvbYZ z0v+g=Hj6^?9KxI?*ueDol1u2dtcGU&jr+*jR+HL zjBS4IcHX8l@HPl@F7CblrrDu@x#f6>GZHmN!%B3Z%T^?0xLwWc)_OxuEZG)nzkA90 zIXQHy!hB^wxoFI~YtPr*2@e1hH|Y$V=iSN<567uR@BpwiF%+)(Fjv+rx9KCCG#TOw1h~ zB89v*YCpr>az!8NxEs+5XfLY< ziml9$Q?D4dm$^)X$*~SfNy<_?)OEzif%ZK+BHj|Ax5E!gQmxz8bmEts#q2oHH{Tr! zeSXezLWvc%)Buj_+SZFAHb2lil*X*WF#3%LcCgdKwZgDP&7^EAAfWj%Hlu;BpuL#D6T{1@Touk)2t!>(*uB% zOj@a#Gxxd0nk?@o>2{n`ugV@`ZtPq~>4G{`t$oeA%JAfHBY(*>L$=jgn z9a1*oDdtiSs!hsGgSjC%#+BWO{T*^a;J~MRAA^`Z>QX}0sZj0%z|0fn>%8vnV7EIl z0IQq1_{9y*8a2}j6l@DIYO&LGGiq&pUQBju#TnOgEj|Q&d!qFQ(h?#+e>Ibx&rt$> zE~8thxk^wZfCUW8zZYUNYZN0{Mk2Suk)X*;Lgn9n3~}o(sBUHQh6WE*^HkT*2&3T0 zLYl54DxyVCJqRU8sKg@`$J=;VA3VI025N}-aW=*nC(O%b^2g9Vr6W|v|Lj2_1m8iI zfzaQOfm`L4CV=sv+&ILhfjM4&AU}M5_oNc50V>*q_o36)n%lrfTHMO%Q)SCf{H`I+ zI6SA?@FrI+9^NzvEey9oYp&Zt?k`{W?}GRItxr+W*Eij*Ui8}QMg%P{Bd1A#qpVl4 z+XLAW%7Z&|i0bU0fes`X4}oELA$lZd|9_x?=l>}pXaIt^Pf^G zsDctPfjQyB)jBSv!>LR`h-vKFMp0$7h|Z( zqzUayQ7|y7Xt!CCBzyn{HmySa$CdnJj}4W&NMzJ7{*m|+MYtV{l$Y>Q%$q8q#&cK- zTx0rowLw{E0BMkAJPL5$H+YY%QXEgfk^Lqd|iN}}Kv{2Czt@2^upr-p#leX>V*cZ#qe4up^T zrt9%fxYugkL)}vvs0TmIT0PAlIIDlLJ;8@943!V3>*M+=GK4EK0$_@A#jEWrWkH;% zL!Kx=%0!Sx#=(^4VO*kgy^BybPE>+G17k||k`KYak?@4;V2eU{iy}x7T$;eRqiNK) zmmC4`Ipg>r_~@h8|45klKrsjQDU-Jj7ebd|BV>1=gNh!gU<;(_K!aY(689Ihs#yq>jcFVGev*WPM7y>3AQY68 zWN1|pEK8k*3R@T!0=o^1Q2Q|{qhKMPFGd9**?{CwLWv42gA>ak_0O3vL-eX4)%*#; z5|R0@7mAXU2s==P@C3}Ao^{m8V$>*r0-a?*3zbn|NZjyBTj!+ym+NU<7|tTnbT!65 z8R!r$838E;ffKVp3W=DIpVS`Ys)FD_K^-u06g^|qs%%cSm=*TNq-cvvCVI5#-9sZl z4FAOzKZdn(Rra(*oQ$mJt$evbwqhsCwUgE@i^n@>^flTH&nF$0wf=c@#8-nH9t7e2 z^)l)HTu}t&1_nIx69qp^EWoouFD~9Gy#cOhjByjnQzWJ>P>;0}0rmH2UVNXbK{SP3 zTR{W`G+1?PuUgi)I?kw?C1}tfA}tBn`j+(<-P+D0|4-v--uP|bMnC}z_5%;*2Q{Kf3yuQ6jEEVVYQJUn zM*ubQpFPyCRf|6a%H{y};n)51xY=i2zq!8`!*kZ!nrK_3v1qWO$d6RpV4%uwOytiY z>9RTq3z420@%PCW+4uwAJ|C}F9W}4#u^68Qz&Gr zfKL*KMvnrqGy2AtRrnGBriE&eAR1%z&@WhLuhC7evTJr1X$%?_1#=Z3O@?{{p)L#E zt}`n~n>1l7LcZscDU~YFq>fpg=2lce?df6;tUd&FK8mzNY)?eldYbg4N6Qh zoiesPE-X@#i0xLK^fT0ChPM>^4zF|?xyn+nh8d#w42Ge4R9Jt>p0?XC3rG>Kw8GD- zQGhbB&B!y%_*g!TT7o2dsSzzlC~F0MIOzzf5hjh=pGgwM09e(Jy?us}6^<}FV&F|N zz3T&Nbn6qhi~AYZXTjoSEvxLd?PS|Y)xD(C8YZjLI@M^E48YR0e~2AWj}kGun{m*) zFN>1VSMKB|;;i!-@z$Sx-XG;|Y)oieZ!sIl&|XVxT5qw~dDL@r-uB0LcNT@9?|w;_ zH|;6dH}Dw(9*qMDaCw5@+l&RcPFvGEl~!u#aNH8`)@Ey{J6URs)A-cyHw(1#Pzp>J z*GI4(hWCm+(v1i$Sy$jo86%^Dj>U?I+#-KV@UG%pP0y|Qg@IA7p?UZ2C{OI8FBeYK z9=tOy=Mvz`zbO0W{t}xkK6Y7TH*QKAKj!LWj+wqlZ zrc5s+$T6Tr{7wra(k4tTWK`)xIJ@l?{QttU2z__>RQ6?$;>+t`Mbrgk@3g4vB6 zjCgRHo0&B||NE;tRqFGy`FsxExGvx-P$n)0h;$SJJGD`h3T~5#N%5qjb+QM35=*6W zPwSjDpSfe}t>t|~VYxv&6=LCIzkhOTtz%{VnT>KFSL4k~w@oaMov`_Z?OeXl-x5|A z3yK3C>v%+M4;CbEIhwJpdR?&;Ua%m%et!gp z6u@seteRzLwgt2CzvxFFBxbQ$?fWv}S9BSoykrJdi_U0Bi6|R%*>mZ#qf#>D$95@oQ?wJb6}JhIs0Zi6f`3P57{{ zH-+=QO5QY^Gau~E2}_tp5zUeKY=8=bM1khIj%aS%I|*9UXfRF%colgx{-OqSEK|+FXVE{TOpcmRNTR^ZEGuLwiNa` z;2b60Jb_37zWPxapacTA#)T1f9DGwcoExb+Gl~d%II7+!Iu1@SMkGs0@oKD}Rxo4+ zE-&sd`9pS$)$v+Y5P)BCB)Mw(%5M`nU)eI{KFO_YGB*!h`_-jc2%4yj@Hn@=&byzo zB9Jxei;2kBxtC&zhOB59S4gzk1!}0!P_c9Bp!p!i6*bL z5#@{QkN^I@IxisHf7M+k2eGFGK$aeEsJ!`6v(8jKRM^6tEvYQE4$K~FW$kKGTzwu^ zFw(yd6>t@VT-n(vmWxK-iWKT&cw|SVY)6Gn!Vj4Si>ARx zXH}=v^s>Sfu0rjNITezeaoBCs_QmbF3oADphhm#8FFur&us`m4)$SguJMcxRg3(*Ux4e<|-PVE@?&23{#5xWPV zsH#kZmJ3rnYUE`3Zx>?D*TTQP|)OL$#7VAPTdo)FI{^il;F5w>FdA{5z zU@JyIL}&kedFhSw5DBN7kT_vGMAii?;1WUNqnhl|afB1rR!2!kR!L`iO9Zn~V|}OH zHadBAuSX>wHf(u*qv);wdnb;L&Jt%D!w^L(# zyce_(HSM$C4+^5uJEs(t9rvA78V$MP1`l!6o%WOKLMRdsTouPGc@Zs8C8htV^vV}I zL(~~G-qnZ9HpWm6eE^dS3S2-O)YwC*%^$G;VEz*-#k0@mGVo$U2|ty1SP(~2k|ioG zErsIhfhi9*@`BeZjzUme%5I$48?+f8Lu5S5p)MCeLsT`)fF8&Gq)lEhoQ}3L@QB|{ zG^|8C;te`<&PH|VZa|Tp(W#$D38Xti-3y?z`jf^Suj3k`Nw#Ud@1hQXg`bdnu`ozL zxrUO*88FpOdh4%mZE#bt`ZHR=j7I&p<-&J!z-7j(#RxYJ4PPG&0U>mOx;y1ju}iY7 zL`82R_?V+-hucqZecL{AR^avIDcgNtO{|BhY#Qk1*8R1>+dbzR*R6jt!Kd;tNKm{8 z__6WoU~_Y?ln#C$m;x~+j)9BxP?P0oi}#x=IdfVR!y=h&vg zP^}k@nDqRKY{0!RjMzrr7CW@LTRLOZeU7E@#$A&32M88W$YC{o7`FP*=h>Gva-@&x z)a;tA0V^U>D-3Q<(PM;qZ%`&hqAVMERqOwGlZAp=*Dfx#j}fBA`TE944EZf&VEvn_ zafBI9i_??K=)|wn4yC|{Fj)1d_2(cb^aR?X8E@`iMUgvzwis=Wpth$zml#ZbSmtyH z8Yc6jF_8+QSuG4vUnA+|14Q%(ND}`i2o7Ml4B^T0?=pPgcMqiZQy*0;T0Nh8AzSkF zZOyrlsz%n+M)cwNBIML81j=F#bMA@+NwO@sS^3Ga_S(rKoF1d4l-N2gOo3ooS2M{{ z+P^<;wxv#jJ)X*ieQSHWyi`ftC;NKRbms^Z;;!(MExtZD6^59KNHXP8d5T_PAIM27 zpCA-K_Jt)xC=co*l|*7A7NXis1C>N$=8swjP^fp3{KU@?BhvUiAwtzZ_yB%E2E>pH z1iUCcOR`9wZ@TDQm(-AD4^{A*aULdff3@MVxyQ0OC3St%WJ%nAZ_p^nAa66Dh+XYp zBwt|en4*9ELDBN~zNvYNNFNw)dUlA%Emb)fpi}d?W;eQX3(pU)mIp&JzIM@+&hC=&vF)~lN(jxO=I3mkcdXA<@gP%4l&izLw>pzMG( z1)uymcpYQm2AiXxi(WY|_O_ zqo<@g*G7ZMLnCjJ1##LEB(cb$$RQA@A@3U&iF9g%f_K>Aqm)p1lNVnD#b zcSf?Shzb`U_Saj5Q)+ak6!&b2b*@vMU#_dNM&<)Q^YQJWOS~hM4!zDTJ31PsMTkCd z@|*N>z2yemqT{FYhfkhM;fKeD$VMDw-GDbAnB#yE0#7u3)HiXIAZOJrn;AH*3zoeD|AWs4DJn|Nh{dpO7{Mr`~h=89O(7=q;;djYrNDRw)-tY>jlPuH6y(I_HTz6 ziedE={#eTkMUcoV?UAHmyVvu(HaGwK^K2V-B$+1Ke;f zCX$QKaP}5$CPTGfXPAjv`%ZP(WvaB+@29B`Tg8pw{yN?JcTm_p82G^QDliAn6_7ns zhu%V`N50i(b3>{4)d>d>^w~JkShwN7x&HRjn*vDFEi!~*d3!Ze^ZUGPigtoDKwaD8syq*s49m@Vagz2{;BnpOpk-3?^ z-4`DX16K$UT_ch|#R0w}))r(dFijFb^b3veZf}%3olt)J(>M1MZ~6L(;>Io28`*jH zN?x|z3GsvC`NQJitxL&{C=up@`T33R`f0OgD{Ej&{U3i}`aRHnHAVFR2wnErJPM!h z=?GR9(HDJzs9%E{!J83SI)cmeqil;68$4_mii60~Gw6m30WTb82UdEd3jC_K3GHvE zZLHs=vNsG1D6IulDst;QY4keP9*h(nq4+Z-M44u_*c`; z^C7}vEmxj+*aNsI#_PXfe!s54I}L2O*LSk_ej8|_VH~Pp*5RxQ4x*VA2*6GplOps5RPZgD064_f8IS$jg^oki80PbeYJ)vY zo4*%z@vFsPCZ`2BQ8R0DS2U%9F*_D}wXl@cOIu7L4|Q{L9)G#i+jtZ$g({h9_3J6- zA-nsu9EW(~mSa-Evtx*->T2oDp3xQZMVYM^gplku^jS>^S6`@9U!ZR{df|`FDJ{D~ z)_8wn0!;;=dS8qmkj2VtrhMiZpizY+63ei2QkxABb%ZPdK`@wPR z!Tj^jduq`!IefFFD}Sa;_FU#+rxdX_4P9EXw1ht;jbhRwy;n_)shnMJF*(bR>QM() z$HBzt_!Ui$`Mb@1e?tPx_ z;d!)Ws?|h4lw)AL(cpDHJ|iF^hRmFxEf*qgnz$t@j%!!i;j;ZQ=R@V{21rgvMAHDV z&kPi2(rnh#ykKNz)B-g4{VEnNG|AHYQ?J1g-&%F)ypZ+AQMWqStYWMf4?jVRxyDgD}FEIT=B-{^^iFF(C^h)UjA z!mi`BuxA5Ls=OJ%4XS6H$89r+roj4F4D{txdiNXtv)+HQiLLvU?tm662;l)Kn8s;f z3|QWi4`w-%ZM@rYxZysO=F<#1CNIwo3WeVGD9}-{x_7P5ja7R#eu{eV*MZ%ew%9 z;GPFP=3R)=QuP^8c{AC2GaQb7j@n*pS6_c$kVUkYqj0mnv$1k&;#y03h>Uuq1?ap~ zwAlb3WqiIzWww&~o5Tb<90(%{0Oh#rzcZThu5C|Uy&;ZV34Lg;ERkc}h9qLbpNce7 zSQY#fmsHIP845Z^V0;wFei>j^@_gRTw>t@PrCkAE^|BR*lCR{>JSH1B>^Z~|zf7Mhyf z+*0Xr&>og@Md}J`iw-l}pfhXbh?Z0j#8#Z9%Cp|7E9~tzrBe&%H;#n`psxuX=p~lM zix&mz$qNpNZ!phO#1^PEmVqDNl4moPlvHr&LI%d#5yDtYT{f1AxvF%z!GyHiTqE#@ zosr{f@)-fu^;xz22LM^%}RUk?I{a(gpvO>7O1F0`!iA{<*!~i|8Uh z&o=}mf`ejeh9*EJ1W}VhJ{%ie7X%lTF*%YG$?2&d-DfB;w(N=gYG>R`tLuMPOHecZ z^($BHipjw&Ajr4lla>NVO#6=epK5B^If|rfrJ6M?7AGl~{TFfw6@0$MS^h)*&w;&< zr5_%YB?QmDVOx4PJOBzCjq@Mw_HDCR1&JJj5l<=FCKk=FITz5Aigx8OZrN)|i`U3F z?ww-P1jRSIQPAC^Zpv87F=WJGWmnRWo^t##?uaEI*VA{21kC92HuPIv*R^EeCX&11r=(3iBj42ixbL075DfxMSt8? z97JA#`ds;MffjO;cNynV{Nz3vLpWN_SuAGWALznpYcSkF@@t^K16OcH%9*bY^;J{1 z>$xJq7HiRCY5-XlsG1D-*ETOoD7icNoJme0xYf(3SWd~nt?|3oNMQS?PP7CI5LO;uAEi8<4 z>GT;mn18{hsT^^1=dL(PL!wBw?fT!I?b%ylW_52@l?ZY)>SR| zsmK)Cp-|1+a3JPw=BOvwE-mUs6=zZ4-gg(W?9xArKON$>CTge*t5>u?;S;802I?m? zy9&Lh5ZuDNB?oINa<2)_lF!xRl8Q{^0{@M0qw25 z&~bE?3+OhQdTjYzxJTkKIP9{9(p^QV?~`$d(vCI$)KntnY}E9Z5V9u}`14Q6U{e5^ zSwTlW3WO2cCAqsA6Xe*6jX^NUzW@UnDqzpXz2t_zEcBF~;y66+f?Om?uN+-&Iy7c#mczh-){KS=AJwW}y^W{Ub@=|ll3-fsQa7%$la#w_@>QKZK z5LJ!KPIM$`#+x%5!F@0cm-2oPKK{LtE#M|$b{kV$MIA|6gPpKN7A;aEuVDXjf2aMG zMzoU6$B(D*rgk0$H;8Joy^MNHvB7NEwJCPbe1iFdPrH|Nr!14In>vv7dc>KkAHY69 zdJFPC4P@d)GGZx_;Lm`jKTvZ1oByxMQTol*H5r$;pmaH+pQ9XNgbW`1FZ{rWl(Ck# z5QJ?s-?8%(#$lCi_0EE}!FTcpaHbiHH@4me#;EP$ke>)-U}R@{1o%#nTAZvWvXMKV5z+)uf=%%&5@MC!D9v+8!7|0OE)gHD{ zY(8ltKGt8_m*YSAMVaIsz&*iH&EOGWk)NgWnqud%OzeE#ZrQz(n6c40$FprY*2z{g zucBp2{fjh#ZO_XW?&);#O}ESn4^g9O<4Q}lZO;!c%??vTiLnKa>V0!`%|m8@hm(8_ z-FXXHElassWFHnQSy>4QS?T>oSurvb3W`!Umz+sC*XZ(E*% ze`5@=clv1|FtwFu4NiL;tFmzpFKX0vLejZUrB{w50vYVmeX}^fDt**SJ;npA?9b~rY zkF-7)Djpe(x>NUqQl(N8D_iVN_5p6VmX(%+7HucpmeF;c1L^=K!OY$zD$ZC-=~IbT z1L3AnKlToEFutTmz+ayviB>umC<#B)Vr#ataS*rZ)GCUY+gEE~X!CS_yU*UlEF;iZ zRZBolYpc!9ZlRXXTk2iieVyE0u~L%8xubb2V{VCGm6i3Fxq-Hn6&kF4j(`54EUxdZ zCFQMTC~Lb~yB?T7pWj@+PntnaMP$~iv|P(1-$&(Gx*AdinAMCm3;xBUDem6qO+hch z2szP$%u2~$DvO(+`sYE7=+Qq#{txo~p2C%;K++gA)4zFQ2SORLmWg=|UG5Yfn+~Uf zN~5*ho$!Iq*3Yy#-&A}gU?$IjbHzjKk#p<$?68}0*Z?4X#zQD7YSryGl8Bc{ZNE$F>zfhRzYokNGmh?|U z^TjD@Ot%98P8s=%B;@JmId*t7nwruCENsG{b!m$tpoxM1vXdHq)eilx@KJjOil;ft zxWKz_EfqOhifvz^yHFObX7x~-|DNCxd1f{D++K3nSm9ElXDlczxp7D)IgCDr9lqP# z%4Ovy`LU1Jr^;=8mIk{!k4^Hj?2u8oTZ#uS`SK!rwzSVqD?2K6>BeGiqRJAxB4M87 zC>*8;kf%uUOi~zFxxF@%2;M@@9H?5p2DDyx{mSfzw{U^|mic`1Ni&tJbz$>@ij#jo zGTexXdT819zDOFQek7z1F)Mw)sKZ+VG6qg96Wd9XsEjnp1+uFuHim{4R19}}Nn@NO zzwT7;DMrhWgYA2^gKg;_qoi+N(r0n#*$FrS@9oT*8h8>;I+P2WyvP(1oWUVm{k63v z3tA0bPwmlHo3qy-_9}wi`AS_Bl#R6{v{fzXHmf@3q$Z4p2nM>knlv?*$`U!v)GUmx zdi(OmCJrFm%te}6_=7O(Rgm#Ak2GmYYGTz<{WRwO-jeUnha6YGW=3(Y=8XH;cO68@kmS8rQ^3l~>s9X(lJNtedV@XIcTyW3lMc7(?(qOB+EH)p?7GtD3m0y!|U*#wkb3 z%^+5~4-J)D=Jn{P;BqT|!j%@}Gp$eaax2RYvmH4bmH5Z$4u=`bkG#_Tz;U4YjOCR% zbg&uBBp_Ps-hm3*p;Yp(P#!116V)%ue@923l4UfS+*<^v<0zNTVTtf`=s>4Vt;Qkw zH|KgF>l#*2bA>$-rnoER*FBR`bzPfx&oEdXWcs`qBIr*3ER5-)a5L#i5 z;K+|jFW0R{W3r}14~|tC-Q<*MpsB~P#HS}_HeV<5iB)hGA;W|_2F1$N0p3X)s`dhT za~24gOiFb|%i2cw%K$EX?GoL2UY5*|cZ&2jJ2RThOHD0H>VKo7@g4)jH$2CVj5nfc zwPW-qW1(l{`%T*F7;Ln%%B8A|r}6%)y`8cFiRY!L!ZPW8Hj*+r9BB$MOm6UA;uH45 ziELb9>Vp)pij0bMypiM$L@|HEF&KOj>*k(f2c$0cMso^Rq3r*PV^R))|dBU!dvOMqtf@| zM@H_blbT{qeQ}7HN9{@;iGhhkK4O%SF9Hrp3wK9x!uJ-b+>0orBi>$S91utaM%M z^l&zDpJ)Y%Bk8K4r8Bkgxr{F{NJf)VFQ=rlMN*@E?`(IFY%FOnZ4^s?sscOgc-Qch zweRti)2iA;T`Jp)N*?%lU`8*W?()%|ILL1t*Xx)!iI}mdFYQmSJwMG1Gog3N_gqj- z2u*><0ha3RD&^|Mpc@LnX`GBs+h|%_gub8MKWZ?k?9_79QA50>pOu+jB$VD=q+Kl|#GN`Ns#9yTj!NcO= z;g94OvaMPh;mN16B z0FhlSB8Gtu)GFv0Vdz(+8hs%>d3Rn1%A2EH(hJy_tI$}Tmg4moC8U2vN_!x++NiHP zbKz24NuLSXCf;;+IGPOR9qGIzVG11~>60D~p|&G59?^$giuM-+O2=&)VXNOqmENi% zlXfcQ$crfh3e}tGOK`0U53RH*UcwQ;5Q|8|uj5;4slA}ADqpcIbTE@$?%mwVE92YH zJdaz+sk5f{mP+X&4_WZJl*=+6R$FSS`Xze1cuKL@UVAtJTX2&mtBrx8Ea_w+AlJkK zzOPYS?^>T}Nn__#?ql)B?}B@(ag2NH-CCAAw)iljb+&5WcGmVKE-yBv+#wBk+MW4S zU0r?#eztsOeRh}%uq)uiguKJ=3nae_#g{P=U6x%I-GUCUC80Rgd$WT~^AuT|US+3ba<{bc8@ z^;kP3u&uFvxjt=B|5{U9T2u#`7VO>%O;>N*W~?eA7uyCqjn~Y)Ywsb1uo(jQH6LJ+?h^r9Gj`7$(}1KSMRBJC`)*ka!+07xGNqHlSQt+bH=yS z9a|3@OdtAg6BGC2a1*t4t1AQ(nJFJnD^KS9nabm$Q)CmSQ)m;aQc3{-64O)nIzeU2 ztJXk+(~__E#^;%@cgNdnwCGBQ>yu9#e{;@Dkoo)Chq-P2`ty|+_MeaXT(&2X5VRzN%_2qFD+VO=P16a3|eD9sS751d*b)5{ce|Q56pFtmVZ*5PALTndJ zms>SkZC|S&z$);02(;OCejNteea=lBk^GDa5g{c;e)&>8WEb}3{#pZdfyC#(ex_S! z;&B+Wv#SU`=Nl$ihkP07$y#4@UaPXz_jrrpj{UzALnTB$bIACKk-rDT|JxWqlG3_2 z!_CL%e3653Io|H{Icc|%;Iw3uMa19Ay<-c!ZP*d$Il4dXY^}$6PO(0{`wt^WNxA;t z`TxJEis>yn&B24%=sZGE`b7Ml6aBUD9M;TFv^LBE6r~jNThu8@Q&9;Vvbfx5_L6cK z&Pp>^s!!qs)^Vi++mdoqum}8-@;!XG`Xy$PQwGTcO0$wzAYCOYO3}iE*BDb=Ddyen zr-tmTVaYht={{sW(@?SUX3Qv0Tys!wf|vaC|4 zvRDJ^_vl$j>Y_p|&KO$KBzf?gHS3kE1h-EGHd+8B(Hk8XqsUp>G^S1ioiqG@x^IKX zoZ*0}>afHAyZC?ZeRl`P$*4PYCA5yd5y;60GT_Q1^V#!b%UCpfhB^@#Uqe^~Q4QJq zhU@ob;Ns_$oGDD{jEo2&-N_Et{we*O3>7Qa8$pY0*Di2$c`0)7Vh;U-rOd)|In8L94O|xvZzWCW2y5pY6Mv%b< z1Kio*gcYI&-SAb?sMxx>=_#CQbEu(j!Bym>I(to$L*6#8S4d z!j@)wPmim}^_X}{ZyLdI6O!({c@`FiZXTPvp&!4x&;LugS!iCNen@BtDZiyLPkgov z{6a$CsdyrLKksh)uTLH&cd;D)sQ>+}+(d zxNGo(2M_MeitIQn4ST#)6CF3rOMicT=0(c1E#? z)zALO$-r=arRb<&G_f1ClUygBw~e5Ym(7)H$`sQ@8h)J3OqyywTOwz7#&RafNz`W= z_egF*wjr`XFW{1^!>ri|^u~`_pIKoj?pBy3IrtWCO)D$z1>QJJ1`1>>Gfzc@dk3Et zaq%q;x~_k*l^~`m<}#SCP4Ir~M|2MJfUy`8^9zyaQ%l!(Ij9%IL?Yg#i!qdH1ViW0 zyBzHJssj@iC=w4xa}8q*q zzgq@*12WQhiKtjP6y3@EF*mA$PeeH2TjF1JC%$*jPp{8=Bn0^qXDpy3ccoh&e6vo7 zW@h>8V#~{{RkEf~*pAG*IS3N_IsGkXbh`K!qoxjVKdKHkqyE{ zP!%%nIEYTA0CW4MOrvYN6*Ipxq&2DC3Cg8w{(A1C^24sd0RQ1I7<}W9T4L=?)*bYh zmV=x8NPAFbjp8rKKzLyK9`TO~g;nM@M}J4Y!JA_;jcW|nE1XOf_3=c7j~z#c#B}d{ zA=K+)K>FLiKI3Jp+}r|wME)EPf>! zKK*T*uV#8P-{aQ8Z`W=6}1Jnr8yVF-Sga3#LR-N-_XM9Zqv_q08*x8p78%T z;D_E>`#TBR?{`*e?t;#!8+)&8;JWEShDNQ`noejq(V1Jal+(tv6q`8-oh#C7Gxb=2lGThakO7XCQM7#&;1o-rC=O$Kw@YwO55H1@sDS zN^mk0KVnt$TuJs9j50U$BhBo{2%=}}%qG63om&~BX!HA~jg6QbkQU*L(i`|`;k4#| z$DQPfy6nhti;36Fn0aOQ%F$vd3o+a;X&1|`QXab)TG9hkSP(5#~T z;Z!{P9bM;F?wHS8n}cqZGv6`WgXWiD`#1gGEt>0&OIkP6I*o$@iVCQOSF4Uoth`u_ z%gO$7nLL#_C?(h=6^pw2`j2gN?pk5qLiw+d zjX3{QR7;aG%IK(hVvPy^fTaYV_(8<1OHj`GFOXDkFi`#ar<3Fz#l{X~1IU(jz(<5onCG@mK)T7dL4wz}AtXAM=lbVk(2;u&ja9sNaO zo{mM%V=TV1kuIQFc7aT%rc=lR`^63X2x4yVo+`5gcw>?VGk_D!nYEer)>)-S(KpdheSKWfCC+6)Do+($}(8X11 zvlh{Va$HWdF?y-q5nxv<+u&}eJ`QO&mA2DVF}?nk8U}cIS3YfwDZmtM>!~P0%W=O_ z>Qv&23?ZPHHKI%H_8j7zHN<^h?*$WwqOt6U_xpqqyGs?vsaa?3TasL9DX~x|p*~+( znH@>f-KuXBRNdA?8w;U??%jn`n?;CBANHB5L(~Gza!ybjD9oY_nv0J$)m9I$S!pJA zDA2b>n-s{@atTU7NX!T|FdpB@#;pF6uA=N1G>tGyf@F8S+#OOr$wnpLImUA24Eeq5 z!FFctFi5mBLI&pxCJR-pWcCQ3^|c6VR@sDIR=S6lz?a_2wD4M*50DFV#3+_p8e<*2 z_~otEu$?N-G^z<)&Rl?{VnACKQA>wW&mV`|)O(=3ZWP0Y$wcAIwsX60l2Be}PnWNS zMX8-^Pc8B!*w58rDP&|PH=DloWmsP^Pt)cky+x<%4#FCjXoCOTga6El$fZ_(c`FmP zp4XUwX^&z`78MhqAzw4CK^MK!?Z5-#DR77*wyd!-`R$2RI(v}O@alWy;1;Vx)k*QA z-w~)s($1nN^{so^`h0N=FloV3zHaR<|NAj`wKMxF zncSxy4~J}W_V<1hb`g9}tk>gkq_Jd4kKE2d> zCvO~dYyqSLqZeu8)k7&9yhe0(nJk&C-^4&gEH8u}dM+AFlQL&EYtrUdY5z!)pz&QM z3`Pla1osLSn`<%6H1>GfJ(+KbzWoObS@oBoPJ2@ixePG7vZBqtRlE8)?NGYG2@VIW zGQ_Qtp~0hxfMVTm69uxUqAsP+4vV5pLc+KY#J=u87w4WZ4l5l zctWd!hK2B!2xp1coeS{cJ-I95;ZUiv8y;@pZ zF;8-$%mZ!|D}m&9H8yOC{Cl@aljQ? ztA-Z{i(U^(-g9~7d3GogS^L8>$`6!*!qJaYOxn6n3{Fhz8*st9XimU7clkRM?Jc7| z6fhERG9z7Ffx(_sTLE5{9xz(n4PoP2?N3MIO+IN@x40ie#kn)(`ApA&HRwLtNmX8A z(j5&tb$gZ`jTl>F3avk)ouj>^)JBuCH%_ocV4W8adwkiF_mEdM+HWUnug&s(Wfsj^|hX47_-MedPQK9J5K&#Y3AIw zWa#oKb8>p#yP?>B!(QzSdjU%4ogd9pBBxJ?>ofiqq;6zku_HMmwflWlbJS8-Rkt3k zyz2e;B@DFpu$?}`6%@&`Y<+TEAf6Fc(4BFzG$#z|Ut5^vrF46EY--syawX(o=X`AX zCEj12N}{UiSTSRB!s*^LTlF#(`O%E*e*cKht^<5|4Vx%W9gw~K zu{GzeA8cO|G`7Ym9nh5ZIxE=!i~hx$rmeP4;Vd*CXTSnlLE7&ph_PJGHmd^84<#{5 zoG?w&a129s34Fu)w+)6rjV)?PkkHEs-OK4(6BHxR_B8F|R8aIufyVs7N@@_%i0c)A z{(s_Z%oQg99E1l(kp1~3FdFecc>-RFhADm!!*>ZO7(cK_b@Lpz!s4~SfBGCblImTQ z{J@fwthf68T-)U5#$i33*ERP53uRpw-7)u2Utip9^PbYeO(5fQKTB^;-QX_5v2ngX|TYg2Z+fl0BIW$ejZ|7d$}gf75u4i^X? zuoqq%)9=h5QWy3g@Q@M^I7=1^#*uwxj%i4c1?N>Gz=pZz@JJ4zPL6BAf}G=-SC10P*R0q!G^*6 za1)dr_YJc~8L+AQM%rkpIprupB`^%cMqVgd8_21T0sCoAdw5zxXsLBU`&mZMNT4?g zE&mCUJq{y$v{Y>)ZOGIGBWp-Gb}ff5;BzBhlvJPo8x4>EWGY&~zB0%cks%KV15Pq} zCeX_L0&aXm&)x3rvlXGnVQB1YfM&4mZ$M#~G-}8Gw<9lH>TCav1IQP^&lXfD&v5eV|c0A%o)=Fmup8h0!yXRu6otW57N>$T!a28JT|)e_3CT z$wyFgm}Ki@MYo*^q=%MjYIF<>UN`bW(jxf+{%0^~tz1a(Qs0fG(X%e-4Zt7_ivV*O z*!%p1{8OC?2-O?(czga9bJa@mMyYiH4Yn0IfY9>)!a({3Ow)e@gaN%VY4xC__QHYV zp3tyuK9nLQKBgn7-gt-DJ}4wn#8$DUR6b4M!D=7|NH77&4i0P#qU847Te%MK>;QePiD2DHSb1QhI*4l7su)tKg{ss~TpMDo8t<}Ccxc@wa3hE+dAcX>B8a+d5`I9nC2kdtmJ+lf= zoQl#08crj^HbbDbN`d_}>Buur_E7E;W1rs6C7t29k#ZV}R@+zz(1_=v4kb7hx?S z=>K972INc3uxZ?(&Dj_`Y0d=#_y6bOAS#FL6~ew7A&|hA)Vjd^zedmBL2v1kJB7>S zOew?=tub3Ip{KCmnE&=U?Z5H(Um!q$9Rv5PjGno)K2RAxCvvyc&t!yhfqsFw%Z|77 zD``&_57>iyI)i&Gm#|=7kVMa(BNX^wOyC{)p)mX_O7iTxvHuqq!i7I>41Ww>J<=sU zmtp_QGOU_6QY`T|QtW?m0R{H^|E;ZRtw*mF8?cY3bpa{2CwCEL^vt7`3k~M(yGi@!CLh5%bF{0mvl?G?pZGdc zv}@uszc-fuoQstDFFVAIo;g9jFbqJ*)XRWWY?>dCL?VfBsj7CFX`io9wMS+ZF!durI9E@hnX1xc6Dz z6(wBuM)N^IwMeqRH0J-kXt;NIxv+5Y^xF;o;$QV&u5s)suDOsz&c;pkGaMQxK96nWnXtM$wfVGs?6TR?ril4Brn~ znkgHOYN^MasJN~BA?7($#T-8GyQ}SBIBRncQol)+_43p<=_*v-qC&12)$oJE3YWjA z&sibM;(s}0rHc4yQ0VDtJiY_Jo~`^@)d_h|lA5-{A30DzXUT?uJ;k%vhx1F0n}ho?gyqohc7L?&IG1`AUdgn7m1Mq8khL4%uK5ktWl&j>HA{j!(uJ_L<1TxtNvY{ z>EF8~zE`3s&e@SuQ;4_y!6Y$=8_H^K`m=rQfnQGOu)a!0jJLoYt7RumiSQTD{&2SC zP-%>e3=9-M+>)4IDi@ctw8<(5N2IvU^X#DR@LcrabLV6)vm=fJAv@@$s{?lM>t~=H z+uTM6Di$c;-u!9bK1j%_%sF3h-yC;KS-P`fbpARNic~ zH3<7&fY^PMj{?rXn;?kN<5KHucny8(AoYHD4MvRq0v$u4kmfw>0dG|OF|z2H5_jG( z=k!G=(KBJ%#VoPkTJ)jysUfU4y&7@@KksUzcJWBCqfOOZzc?Iwuf&!9pwv&rw*Hq-#cRUnnZd+ z_{pwa%&YJC#J!cr%BHN{tGP}n4bV6R=iP8qf&mh>8jl0wXzN~EeG1&?CC%!S+=;kh z&x`<%vR4yzf{CX(rx6{f4vIGaE+yn?meyS15LrcT(QB_gM znC9*UiQn?2F2I!a*DW@jl+$ErS|MVG(vjaY(l;v+Y4G`<;6rApQLP(?>hxEZ%x}?{ zCmysUR8V0u>Z*vo7OnRi1^pTngfy%%oqt&@T`Vo}Czm>_8T#{C*2at$Qu+GX>*>*` zoEmQqWJu}ePX(;}ij-KzZBS3843#!b=iHE|H9irj8s@os%LQ(-}*~NeSO< z_Z&|5_D;PdoxXI9-Oqk}U2E)0TYAm5y?{y2&DE{IPzXFXeMd6;!HxUFn8=-n1p+_d zYZyhokV2R-n-)zZCJ_D*kIKUsWi?UbGl&xJW=nv08jNR=RAnlM7@&&_~6E!NPHM6=8L? z7OJ+ETo-E_Rs(Yzw#snucd0G63Z@Pt$f}_*Hn!??@e88)GT|4G9}?XVSAaseMHdKN zAct1-&n|dYr3g|nhywHw2(bXGK^&G!5w)8X>jGI(HHfbifCls@=mU4Nbh=PpTtsZB zBbO4pv(TIR)_x7)zM(fxbSGs(61?Gu#&2HYX~?@0)zQ{)uG z7)_Q9>AWH({c5n&X@^Kg{GMpGXjcp8K!$nCdPQX(^c{pgWV%VIrsQoiv>#Aev>gm& zEi>i4c9uw@0`(9OU6fSL^_&Gg7xck4!LniHSgbvx5lfWNEb^5=DLllGsl1Fqf(;Yt z{OB1@gzLT(E6VgR@*v8IcuAJvYxv$_e1b~#0CtmnGAIMclQSaFY}9A{U!Vj5kN~~Z zI1yJA1trWH!}}!;1gmd@9JU?wNnmTm63Z3M`3Y%f1w`Y*ijc-P*)h(9Ue5m-oFFv= zYi>e`(Do&Og0h|x1CdwE3u?5$I9@y{$eIIb6v0drKF~t+Z~i0z*LMU07wVwAqJp>h zm}75mFGRcjNFr)HFQQ0A%LEz&=v=Q3I)fctz{^Kg7Er+o257{<$)#=S{QVUzt%dzN zKIi9JIwdQ&RIz1bKy>&K^ejUkYXT|&zFV3_PYMb-2649B&59$HQ>10rZZBZ_jK_n6 zO&*h%5>|!-vApUTA=Lh`ENrpjeN61f4)vqyLu^nYe2Ub`G8!tiqWfqX&=l^=y?AHn zyOS^P31FB&c{)C%(%%DW;@C_I0*Tlh8q`;goK8Kdx<`9(vUgJ4QQA>RX3TER9xB%_ zwe9R8stDsnPlI6gk1ueby+Y(e_-m42d6ft`t5GNSgmVRORt-BKy{#TF*p`lLlGw+G zj##NkfL|!nmp-9{M0z|0gdN}rKKbJ7%)zpr&sP{6U6X*MvBy9$!FOchYJO&RCgG_M zQTv4=KgpO6Hp-lPHfMRVfpjVh4;OCE3^)0dP1~(Oq8`QRSD!S4R38c_+&FxP^Dgit z8;lK$Q~v|+iL8>cul}bShWrSGa)^_R8(yj0Nve23Kh}Y{0Vll-kleIvc3ae+Cv#4U z2XDssetRJx)cCs!`kb>wr!PltfD>dP(%4k3VH*chwdOX22=Z2l?DIZ?JKhzj7DRz7 zB9Lk3x@GwL5P8cw+n+DGD5~{9IDiCZxZu0X5zTy&sXR4)VUly3@fv17i8J0q@ihx^ zs35;`Z~owbm6kOg@bQcXuVx<(Y=b^WnSd57^ulsCJNE7F3q{$V(3wN%V~DF;=!>AB zJPNbPBIN*d(=8ea;fbSAK70)%1;lH4?3Jgl8gVj^h-CQ?JSuST5x>8bi3El+(UhI< zC38UkK-)zk1B74Cb^xHhzmQ_dz(sr)=t;U78TI`^|Md=v2xu0)^>yOwFXtaxQqJ*; z!ABg_gCWBv8I#7YzFvg=Q*i5VGqfx@d_VN{{ao`irw=vJW=bGLi6gh3!~}kwA$>am zP->Z?5UfMrN1s-*GccHZ>m8jK_}FlnNy9i$`V-UXGDaC3glF3nw#1q`Tt&8tH+I~ zLYaKI2ud%a%hIBYc%*2F>MX-(En;yiMmq}&BCX(Rd`ct7#h!m1@I8eR-=7HsjnzkP zG5W9(gn?-NhwM%)!F{`d%WT)Y{bh1#Fs#5}(py7@Vo7qqdnMhPQ z8DZOFiAvz;ayP{F&jEfYQ7zulOs4+RpJAC;vVc0YYHWT)&K6{}zzy<<_Fr_7xfsC? z)-n{}=Dru47^slo4IGh6@fUHcT<8YGQ5E^CP+(?WuZfd|NU8O&W|3LVSzMyb;%{&H zUu`8%^S=tcFD=mpMT;y`!X8CN!p}TGK!ux1|AdLBKu6n+FcK0#g$JrgsI>v;3^{Nx zSh>VUNhHJ4%`ko`!uin_nn+a$q(flJK;m`Na1SPkvPwwfK3CK+2cg#|ek<^e{`NG> z2Llu%zY)g(3}sS4MitB#{1OeuM<$eRTl+dn(6AK>X(%B&oM(BCv?rQ|;eZhanhb&( z6(@o_<35NXPeHX!QEZN|CVI#viv}2?%p+(Z4RVQn)lPzF<|DaPqArG|g;0)zV|V(p z!8=GMYBj7wlTR0m+bo4(lm7G%l7tAdI|(S!fHh4EpOsz?qw&xxX*rALQ6c{ibDkO_ zU|wDYiB3A#s0}&fFZo+C_{!4At&f}OD~EuVnuaz71Al?}t8}k9EW^n1=ntsH`O!WA z)LALRIEe+CFOA!oyGCNQ+-yj!@%iOM%Dd$dF@;{#4`U~f z__Ogx(O=81_RX@T{w{M|j6qnMYV-^rt@(Q}X|e*gF`nRY4U?lr`gyXe5z==;RS5@L zLZV4Iv9qpVLdbF%m*qvIIk*(M&;o&!QMcC?VDh3FM9EaXCCAAd?CpN#s(}?YcucB9 z^D%?KFUC~^P|G$Eh_VIaK$M@JPtDk*65ej_N)cF%E=sQc)pek_q6ZhC$>+?hdf10UU1z zOV4Fwd@y?irndedw91*@aouy^hoDaV)ab1$|BQ=uBw?*$zR6*dUqWQ7bK$eDA3ATY zUHA~qo2%K@rWx7z1SQ;`8C(Gg6xOd$e5S8PI)$d({ALdb^+cbFI{XfGLR)k-`IpDo z*2!eoanKBNR1+;Pk1KUDQhGT}MkfEY8dl&;kjNf_42>AIXqPEfh}(Fbn~($WDBwmm*@As;!% za20`G3ZT3rPxX5?iqqX3)s-*V=Dq7R&)E$*Hf^Uj4NL0c8NYTF*ZvkidA-p2wT;re zDR0tLwEyD0e$cd0{6TMBt7xAZi!D5yexncvdVesG zo59HDeDlWgo|LF3P-Zldh1Rf!&ir~SjkT37L5?4Wz#eZMaq2|ePo?|059P275tsn{ ztX#@Leceh$0K8kj~Y$4M<}OC}xgEuyjFL~GXR zAzrt1AhnHqs$Z&9hoB6&(15PFdW=FeTd9lD z>7jFxRAY!#iQqMfOb5kG$^+yAREF)UZG!R_K8bxlFlUISkOcp}&2uU+3k^yin|qyB zRYEm32`B+Ky=spN7~d}pw!87miU5n{+Td(8uYLvcmo4!EGL*0R*=sBjC=#UTC6`^seOP0FZ zsfuv@0&CS^DTNsnzT8=lykws9(WT-6W!3%Sw(ulxUlZ7E26d%z3u##Ahxdg)LoWhI9|HhWB&vT=V>P|F1EYwiz}Ls^q>|0%DP_nxohI2=m7#r ztfF|02A{+nV%kcN5*q142r>*(ExlNlK{hzI_YIOQh#CA^Ej4E5yLIko)~Cy=cNEsG zhTrA!SLIXedqnU5QvKB9I*1vGjZwFFk&D&nYHtZV06g~btLzf1_;6A?&5Hd*R*zk| zl3=<`y5Ga}RZn+8>w%=%Z}ikJ1ca4t49b#y?bY=9)e zy+e+aBv-4;z*&@~rs7I!>(r(1&)l>6$Rt&mBF*>ub)LI5quc7gv^t!k`@EI9QgB

    fkD-4j%fjIKScf|-mXJpzW}+1r>i63a(2W`+P-mJ$JZppiAl$t z%?gWr!-Em)*Zf6%Ud!lnb4bTr)7)<|z$8;u5SSi+l_%1G%4TqsevnRX>^u@ z2a!CRMphq6sLPdQ?_d|LMbP+9H+9CAS-bl*HKn(S{fs*J#1lSg6#zQTD2-ZI8hmcS zf2R2A1bZsJq!ZN1Y+7hyjbV8%xh=*#9&BoY?Xv7ip@XXU}B(8QTBFMY7_*my$kEvp8gz+4ngP}ail%JlM{FV{`=;*P8>^0^flq< zwxSzR2PQV@s>Kf@R+nw69huql?}CSWgo+p5hD(?>*F#=9?+;ZGxZ@;}5P#==1Hi(k z<5;XxRTA2MrkK#igs05n6M#>dz=N=ZB>9!FtvhtHwu#O8>6!V#`5$pAB;;^rC1;<_fi+YqsxPl>AlW#q*vlp9o-gW1u+4$%n7nFnkg4#mIUIFAB07~ z;Gn)y4g{h>qBdY`_dS!s;l;uO*(Lsw>3z2)f_&fG(E8!D8phu|(0O?`4kO5jKHt}FKDKUufT2cOc}`dbd|13TZFu8;TFH&v=)Q_J9TomsQ1_%D?+ zzux`&d|d}pf5%{0Wc1pwTy&)a+i06~hpTki`Xbo3O6F`gho=V8%+X)hSaYUD5ge!} z3EdXrPi8nZak86U)Y{Sv+{%)exr2JovpQ^l z*K5Qr_YA+%D_{vwZ1C$%CZCX0$~|gJS4iLTDYgTjdrsfu*LUl*lUzR95nBPEjr8yWT$V`qdtY6nxcs0J`ycWSihy*yLqk+jqPV8flq4f0}|w% zD`|i@ag24Yq_1 z3x&f)w`c$!SxU_7zTSQmW|!=^ucNnq`fzuXHk!*H!d~kE$XDEbwBWVcS=3<(X?^jw zy?gkyHoGU^K6(2-lP@2KTJdf7!u>j?(CEORs*B#0l9&(}E*?28JPM1{fuupo>T#)= zlTQIVXJ;TPJP*g$Es=kp>Y9{KIJ8A3ZXK*|l$yiqcjMtvw;ZVa(F;h&23uYY(Pjnm zPMr-2N}4_nBr1X(3DlrA=KwXAzc*wIBOYzNG3bOKD&xF5536gk*5Gw;8V84^Pad#{ z;3_Ih@hDH5S^e$j=bMh9xrygY2a{>tbrFpRjnLWqaevsY;$zngV2V1v&ujL~JA<`5n%&oSUJ3r#cR2tJ(rv3^ zaMyHuop!`)G|{6kgJat55!g!aeMQsex#*t`F*DRtL@jzhvSLN%YjvYl?ukJv7mEjem_=bDk&0)3S0}){`@p@ALbdP1Ih}u0HS< z1)x@jdXBZZ*cB3UlkJFacj8Az-l>-KT*TQ4bx7`>3q*~fc(-6V}O*cPV_64oLVj>rrxMkf_&MFEH@%t0_6JSX_?o1EQ}~45UrRi zDLo;A@ksW|XHbl7AlQ%GgkOLNRfFZZY?%Nw4n)yfiDc;=)%uL!lmZtG0QjBZcm~xb z(@PL117?8ZK|%_>SU$i^gQtg3tslA+-pmsDPv>}23=4*mn_FmOFd2-ErYSKO+)^;M z7JnY%ypQud`iwur!nC|?%(ql1ptAh^_qss}!Atg?<;@z{|400yK}HhYtIA*Nl#yBf7rJxd)MaH7>o*V1KlgIw~^Q$TkT&)|E?bv zewy@E_N-NN`>tg?>>vobo8NVPHiGcZj7;6AVV0ZKYwz_`IUuL(%TIocS6ycMyE(-j zt?*iQUATL#TiHD24fb%r{oKPl@cVOAcPiDb`a6v|CAaYL>1Td?8t3-@B@F)kCC=;L zUL9$nwxKXAg^urhof`TV9eJBD{f#H}A*YiMt?7K12GEK?->hq1$Jr05BjsGV=eM3{ z$cp1yHaMP>`*??il`}})Lo3e4lQ4?!sC9;Q&y$?`-AAu#j8kRe@7QE%rIt-S+Jr@K z56#bkT0_U;ak9;+b?4tcxInuoF}L2K>emo+;_3BO?OyijpSiS;E(bp+s<$QP!_BzMfDjGl$s>71nu#C zursBv#fdMe57vuBCJOtRCUuK&m{1pAzhI=zthmMlSK7UaatwQ#Y#;JA8?(4`i{8_6 z1$E!%I2M)aGx|acYM3|H>oW3kHt#FeT;j8zGmeYCk(zrt8;;(7A33@={gd&KurmBh z(s7e?^B}8CZsG)wbFB78Q>yJFLC9uA$7 z8NFTg(K|+D-&-w!Ii*6W;y_@%o#0N#ikQd3{z86+x>w7HjL76bw6vM83?TmaphHHjPc*SjvurO z1J}Qc+nYN-4^i^^)G04b@LSqlRa4;no>EXMj*GRpv9Rkc_-SAZ{(IO+!C&pcf<3VW z+|+NKoqW5wUW(#cS*mRnq=~S!>0Rmk8RQoK@)R7H#B|a7=cs@FyvK92`gt&42Rsma zX&FIlocB)Zwmn^1mt(^A{8%nJ^e!IP4qWB6M}hL$q@v72ZA|FD6ds;Gdy6r+ng2Oa z)@tbA%O|{S{&Infdfu#`7X6+AZrgUQe(GGY4v)fJ_{`OPs%Aynyr*PZ>|4CJd(TZl zaAn};)^%G~&aKS!FSg3u4G2;Q8z5$;B(q0vtoUxdK*F?PWoSH|J}@sxv{GrVfP1C& z*)eG&$wFa|yh35COLq4O@#dpynzWUQ4b8^F&e>X{3>5+SpUTzBs|Ka}EnOYT z^goR!tzY6FUpOx-FB`MGxr3#f73)`ab}nu%At4k*S2q`PV|x_DpV{ZW2u7-sAD>;1 zi5%A6#khZl#Y6U3KWgOnyB z-7Cm`R~Y6lk0lrZ4Si8bn3SS-KMQJA#G{Lj-gWoSPvB&i|HdIZ`)0;t&gDdxj5Tcn zJlSlUb%ec?@-x<4+*j9qmSm}k>e2BGR@cT{fHmuldM#|3qvV|?DLPwIEWj(zw!l!3 z<$b>-BzYLKkg@i9foo!dfnR6 zbcJ|e0gw{e%Npw@_>|x+DynKAPyB;eY)-=`OK+-bv&Vy-Ofe;Xa1Qp+#Bi?QgYD!T zq)8~?e;92^pwVx*y0LL&cSR(dF(WZ*LQ1yX`kblrw!WEks$5t0&|?wjXm&%dgwD38 zBt56Yr&9P@`xYa2#((f#iANrJK01Cxm?bX%4QMeD^crjEx?^z;=||q+O^%s6qv&%q zNOmlyFnMj;0d7LlQu0&LqGMzg5^(`&W}2ChYnxF7dBfz&B^6dAXkaqV+Bz;B6oRp) z#%+tGsJ(;j=JCz6i$*j3B$k2$?yn9p*owr|f5pTHQmuu9+8gvFi<^BmrB0!A*okS+ zfjP^88_>jx{$fFX4@&iDC7;|F2hH20c8@#4f75;-mi)n7TnR1PHxytre80RuB9;5u zDSYGldi6*>c(;-FH~im}&!!MThEMydf9ij7l694_^Ec0eS@NUG7vX=G{=Xal8-b1k zhp*-TEqr;ToM;9fA3l&Ji_!j{91DM?6-9>sXjzus%Qq9A1u);dM<7t~l}oclM1O@b zgLO`_@cP}Cm`k!qo>-i#%ydsgE#dNTcC-?x5zMinbO`$zC%FElLY9ew?_UiW5#PJ# zVtiU~@AgBh{|W8=@#TD^?Z=eXRq=Cz%O9naeP9}}meZ{(F;k(5N5DU}NsvT2%d}JOEao-$ zI8skyMCEjfKgO50{a1iT$yQNeRe(dapr8SzlA?q4%|L6(K#dds-5u{Y5@TrYRkYh2 zxk_IsfNRoB`7atKk0bBTt$7SfkVKjVpL}_-_!$c3NhtLd3W@$$HzVgzUB&)_vW20F;;ZItrhOrGLE>W5ugr}D^;9)JbxHO9x=?qSz!n=$ z3gTHyL+dB8Zh{|SYU-UQ-}nX3Q^%6L)+|<_TE+lemQK}@#1xH2m(>GbPpt9A&jJ~u zm;sI%gD5}p=XlHOK#kP>7J*L#5EVcd7+si#NNUyI`ND_Fu{F`vw33OQ(7lJVQ@oN) z?!83iLD6^kJNm(5WLlfnPFqM?bqnrA%NQH(GFUs*kZI$3A0oF@hSI((zn^+^KIFEc z^*I)d(Z{2|=k6-?x#&^(bie(Ga&DB*3_!#zjWR!uR?N&_rD8|7_$fORRH+Jl5tWjm zXDCodwAaxxQFJf9xzp5RgY%m!g}L*4VJpaAqWQ=sb&t_i4G*i5%pLP15-3r%pXoPp z6h14HH%0el(K$MHOO2qTLgB|XXX`MQbi+xShoDtI$B7#5NRWK&rA^pMg448E zG%R7;f|23>QkWv(4j=%!=(a7Ew&L5NyA(&q&lM^r+lAJMk`s~_OOLGu3W2qq)8-eA zK^d)}-0|R4dr+~teYg2lcIN#t{PZqVod9ppIPU`rI`n%ZKLhn_hfx;mF(cY4P_T>q;AC~o|7;K%TWKI_xbHt+d)}k%Aq^7 zfm~@xFQRnkP4-%=YH3jX>Td8!6Ky%@% zr^I$}S&+qpE?hty;Y(Hi5TS42SrXeZ<2s77kEc6dZg$;ozK_k4hK{u*^rrJBwpufTs22j@ z8hC=OmC^uo5|m@iaU&=RICa$u<|mymnia<$s|wnpfVQ@0vq&K3t`&TymO3XuI zM@NZY=Gj)&Wp$FKptzhaj_6ncNaqpyf~vzW;KX?$giBO!lDYfu6S^SfUq$bCL47cb z(Qfa@B?;AS-TI`@zfpd6-6eESAntSE3exns_1E8*;>`a3nM)t?-dTDw*}*f4mwY3VH( ztSJgNb!7L0VDhl6nsVo;9zsKgf@6=IAni+RQpABZ1L5iw6SIqvDx^aqLl6xTJ3%khjldKm!rUnbY$9Ff z05%bwB_e){`BDrniTP3ul9Bab?I4i};qK%E;6k3{BYNSlD*$=$*YyA(&JG2c5Y`S5 zifjYdq!;iSvP?gi7xGgnLKyKHj?ABVuv4syV(_n67u8^v*cti`3Yh`!4kwub_6|0g z2U5LUL|W)m4uBcqx)8t&f87O`M!2p9@P{sw4YG^b;hJOtl#s5I0ZNG1A%Hlfdi4lK zF*k&3T{14DdYOnAF*`t^Y6L17q!_RocSo6w1bv5FOn_lfTuk8GAe7h|^Pr#D8vUT4 z*c#&?AK4<}wJq5N)($F}1FlI7pcTQn3eXDg+z8-Ca4rIH!#j5aszTcoBWy#ps3bZ8 znn?935!hmHDiQl)dQ^icVtQnQY-CKhJO2i3lL~-bh!(>jIT;S(Hg2ILFv5Z?GsFt3 zP!zrnfE3au9pO!O5>k#`C~M{i^*dbYDLJ1VZj??k{d?+5{t$qSBh{Ji^evI1{8(j zQAyMSc*V?+SXCkz$k@1nKa^QCKB%0*U}p#PHL7RXy;`vg^|fOJPi$+*MJPlD|G)<3 z6+xuO`+(?I>@$f$eFyPVb1VHwAHuJgm=|1qaNlmsb1s6$nVxT^f^w4?Z&aS=6g_3# zL_2(G6<6i1i{-A9@7pe6tp3W)-ugac>@kb__jEzqAk5?5n9NrLEgq>oxMw^6InXfV zFw04EU$RA`U7JqYS_M&sU_U7NCAwpLH9AvPDXeoYMOpuNqM~x5a=b3_@K8@HgYEE` zx506$hxg_zdVKs%)>=bK(%e6^sYXl(+%wKTPAA81q48oNtIb~0OqEzCv8qANnIheS zQNdGG?&2b4yk88A4wBVPTU)k{VA>$}*@wyDr5$B9dd{_XnS>RWxUPx=GyXrS-YK}U z=a1KpZ9Bi%wr$(CZR{8m+nCsz*v`b-vF&8f#6I)Ccu$?T)>U^`eXFawFV^b*tmkpv zpf>3x3vgtsVYwwr*59ULD6m16AF6~=RzDjw`)^qG@&Gt{0abaFpXi0s6qe}zR=Dhw z-+$s6v23Qi=b8IU)WqZM$1<6+iThjF@>xELJOAJXhce*cfv!>oz#BAkF`qAd_&`>P z%GBZ$&kv@EO~!1o!u9FYI@`vNsTc>cmj`OeipZ`mM!_qa8BnXuqkeF3ig?9rXH z+?W3qy)2Y}d)k_~tgr0j_Qq8yz_&AW;jQezyEA^Ft=!?-owKZz?|keDS$V}*f9gqG z>A+Wi_`smN=4yYZm=DpNyDYEF!26lH?5?ci)}6Y{!fP}Hs7_dBSI+Y2j$ba%&vfsO zSjJF}<6}B(P1azb5vjycp3P@IJw5`CR;Oq{(y&xQ=QAH_&Q~XDFjW#LlPEhYGb^v; z=jYe(&YpRaRr=+hx_5^x6Dgx8Kg|Va6yNjBo@!3o;Z!0hpX5*RX1M20UI;34@G6~g zCM*Y;MH(1@N=fBUUMklgXIK+-nHoqmn3cB5 zYWd`RQl8Q(kobI&9_+459tcNeM`b5v2W76Qd~*3Q^{|SI$oXK2kP{)LgU^FLgB^qD z`>Y^j5ElsB748M&1>%K>7mf%_81f4u5FrpE5a|Q|1?~my1kGa;Dn*bqBINWdIcC@tB^_HNf1e31F?)s zes~{fp#MN%t|G3at}GytL7~B&L4H#vR{$=^VPAP4rC7n(t~ORtsY|GY&I;L;(3Qaz z(Urp$)0M&%(p9L5pcAEYb-l0!;RV}NJ~MFAX|kmeU;yA_pcApPPEAeC&d4sRs#7v7 zqTRxnr5l^hprLxQ35ubtr~Xy9+&%0+RSEr2jtf$jho+uG&PZsBJ9|k6C)_DqkuLm4(Bjha;*yt|L+Ppv-BoE}Vro&MKb5^px_KPG zje2w2*xu%neOr5LYc_FeTlvb~65x<#_4|YJa~tKu0YiKbBRv~K#2Rw=&ZX^5tWOJ# zY3R6;Ph?vC2#4YOkA_%M-F^TPelnQ&wmRy<|8@YlOz2(cU5HC4#h}?f;l9H@!#)~_ z3Iq+}b4B9?3xxTAe!+Tydck|)0HK4(K;R(ZCen4-Yv?biUf2)x7f28wVBct;c;9KC zX(ISOA1J=?zUaQ-z9haVzBudf*IxXCU2@DAY2^0y~E$l7iE%Yt;EzByoL#PR) z4}=d`2c$#DR)|3;z#t?qG%tiOlrZEfbY<|LN@tM1n7ETb7rqV*)YsHX)QK?YGrWX7 z$iDBs&%VC|;ctS#2#y082Nt+S_Zo^*jBg7;5DGJh$Oy&^aT&^Aj8g?J7((a@z7m32 z1#t?A3vnOPH3%I7*#vn8`72c36h{>$9lU%{abM6B#+8{BfK3ZS4GtZG{2P`U;wV(m zl!z4v4>BG`8oWysB^fy$swG76@XOEpR)m2s*ctY#F}ANmzg{jPnYEFb@E;8aW5 zUKY`grX%@%GB4G5YK-5OYyqL&uE2@SX9hN1aXQ zQ|)b1_t_-|Ai#68Bj7LtvRAn$U($2!f#||`_0xIYbvE&~OGr{@nWOhiVMF?%1a?*( zBd+DHm22~46Sl3<%y8rabNKcRb~`r|Z-$0s^niR&A#lITl4Qu3{PDL@yzk4TcuRz# zcxLLfEQKCafBr|9pHu+u{hH+24VdxU}vAsJo;1yd>s)1_l=RbeW8G^1cCSw;t zAn~vAAVwLMR;hp@?zJ>RHj0iEu48X^SiOV~WZJIS?f4~wfayER0qe}l%)M8gn>%s` zwho`PfZu)De_$s^vTcmLTaI~-(OU{Vi$6%T|Kj;!t_3eCC8iu5Bf7&~Q5ycpNNv`q zmgfLH0YJ(8IOSb2`V@9lN-;W!_ZIkeBXAe%%oc3A3g@;KpX=$)aj z0K(ql;GGyY)$gvm?~$pOmpLNh`o6}Fujp}rO5>~qo>PctDzYWThGKABibzzm4|xat z8+iQJPg6+}DJ3wqgZ8}a$MB%b)grYmsrJ+fz$E6e+;tgq3-pg&sSOQF8`gP`tx1%0 zL4`t!`LwJ_X2}nyD%{_9za5Uf{=!TTDD2V{rIlK;yyD%1NKTI_{@VP7e~l*+eZ@Ui zaE~DxA|jqJB%YWnzfTX%xl9tukJ^}eKf68#D0H99kay|(>wVoy`sm&nAiaM}Tb0DC z0UiTmS9r1vw~QhW;dW#T)B-f$q1o(Z(p>BHc6U4^{Ux5s*v=2R;Duy&1s<5hke}Ag1e|m`2I#FJ@vA%f4Hk*)P&Y3Wwcj(|BXQ z{8K4UHWdl@{&J_;>3(xfb{k}b*<$ax0bGbGx}@kSUunKP&3T+*E$&-TI5^x@Un2b7&>QOm?i>6 z7~!X=7`$)O!{V#FqNeMXp;-7=3Kk8Ps&~dtPptJk&{{R)QfqX}wt1x%XM#?{ zFyBv|Q}_pR-gg6*25(VYPmg@2lZt3d23U#`oTYzh{r!({dGI$}-!2{rF@Ug?R&vR% zwDjn(Ew`+Ra>Kew%IC4y;)>KMd4%(aFr#=S$N0FUG*(13Ayq8i#d=u7F}n@M{rF-! zAtu9#(kLj^`}BCxj5;SQtl@{{P119gr*w2zprd@Yxmpzc4=QFJW;aK}2I~il$?@2& zEN>+srLb)PMKX`~bdP*yI-uiq)Mtr@#mj}SU&rA#ao{`n%>>DEY2m)Bh4}ry!m`vZ zParY6^xOP|zlV5M>ky6v%mqc8oQ%@Rcnj!9`pBkwDu7i)gcMOgg}tv0FhBR{2fXLi z%}Gu4?<(LvX~NCa9bg^XoDHvZ(@pK-`^2ztw_ud;;_sH97=NCg1>kdVTmMFz{?AC_ z4|ZOz^xU*Apwjtrq+vs+uxPid*ncU(YPEaYVNL#ht8lDe?<}E`5s-I zJ51P64Z!kCKJ02c-oGf1*BWUX^;i5q!GpVQ{K;6G{zU1Mz}+fz`VaN# zbh}^DFzJv6IGTMAA6jv5Nd-cGM%6DjA|q&J4svoB%L<+1lP2W0pgT%ZQ-{+qVJXK3 z$S&S8@VhBXXVFMDN*R=bGrE0Qzef##3fC~p1_C~ZvnkPQumCS$Hjw2DnZ*ffPG8Dz z#4AO}*oaOpcxC#d{nVhSwBgqZ3sqt1LMIIpJbz(sGCzR_D?#J@(6V(8O`<0<;YK!F zxA|MwMe5_IpUXf!QRjqyb`DkpfS13)K& z%<0#pyY6>O41k%=+{4mQMYmcqdz}^UrsvC`U5l=wv|+7gHAj*!YIAMu*V+O}G6v-X&o)NO>ua->N->P&uOunyXz1&iuEwAs zt%sYS5F6stbKd%26`ybA0^)rH?nPlig#;g8!1<4nYEOFx1E@#A*VZUcEHgW|Ox~M? zRzPdBK9!D~0+4=-#s`q^Ycz2C`Ks>x+(15=?`Z^>G~E7dQE#zzcph20uCMRd-F++L z?0G+^%-WScqGtdfn>{3urvBS$U#60xR;J>_%l-)Cq#IeLI)CrO=JKZt%-OkOj61R? zbASO)GhgpVtIuE`vT{3QXbYrJ>avRTFh<f*Ny;6XcNrqqD%}7efr7|fcv`S1}JB`QO!9gBxR9Utd&YfcYr2$c#1Auqu znNF-&^mVo{U3cjFJ-q6q7}g~YZG^j2b{>3-^mW}ODyt21%E_wp$3=dl2sUbJa&dO| zvp>K(yf?!c^2bk86zV-sRv3zqt07FT%b85MN^UD*1_`$qlyaWlP>hMhS*`0f-%?J+ zhPK}+w?jIeGjd+jT<}ZXZ5};I4*=lqo)N>ITV2avyn;tszi9P3!q^nu-x%I+cq13{ znZq+~#j}*df})F046IWOa_QUB!zWgw|FMwM{dTx$6l^-;q-Il@;_tLM6C=2rt&`In z2%}y1Rk16#y6LU60{Kd5@WXib!l0DQV6rsxdVvjN0syvg@|;}GSA{t{$5rNx5*U5hW1zBe4HwUGlEFGZ=X2ff zSgM_eZ`6i^tbccgSAV}t9KMrG+^x^wCf8<%t}uh?;=ZBRo&aOz_(a`b7R)l|2u9z{ zYpbOFt#m&bJR=(xu)Dt)8VJ7`8lNR}{tf=$&K?%=-*ABHJkUUdY)sxxb!JTLNmZsC z)++@%fXxl=RYBF^ZQWK5Ep`$88 z&rpsV#{XlXXeDkSCA&0;yo#!1Etmwz4!@iLXCFCw(Gsq|JRz+>?wJ;@zg8(2uXw)V zpIW?w4g|oMFU6h_#A@ka1JNnPe3VpW%MMInJF(b0AS^@DuD> zeP}-TbNsJF{{QVd{eQ+u$>}G_DCD^DA-TeVn7DS-N@dVhWWnj!;#`=dJpyX#s4KGU zY8s-woto)uY5MBX-2j}-`w*f1$j%-!z2Eu^Bz@CX%=_QmUKea|8lI5^JjZ~zY&I4k-%;H3V#Vd}V3 z4(q?{I8|=6($@z2bESgBTmT|iu`dQmR&NCJ@e6zs8T|&SZ3# zTc~j?_&Yq2eP#LMz}xPde>0g;alSq937-BW90dQtVFEbL<;~MpUl2>Y?X^V(GTvF=U)ng0)||{M-385EP*mA4SfIzHAwH8P zZj|h{?ZPpz6ds}6MsrxGZ29I8zyx&np9fc=jGsjNp9z1IHVbX^=BcGK&iR{l}Ww$2PAooVJbS zLp$ShH-HTeH&;8SUjdVSP~4^F=d5x^<#fxZ>4nGJSBuK*z3tdi0f|G=J)xAtm@R2(~@ z)UkV{T`g8|X=|@V zn6Cg_#X$|6@U?BebWt~&C8^v?Yv{f*2sA!R^<3FF*AAL}tnwNfg_H&hEIf_!A0XY) zVllILUhNS~Xl2oCtx}p52QJW*72%Vac%=2LoT%v7*)DPyw4JI&rr^_qw3oVsf1nX%6H&7pI?>WtS4`_Fund~zqW{VI!f9m(66^&gW|`^ zYA2XumGZ4; z@+DB>4Y;h5-mGTf?C)h;L=E6zVD%XJa0*!=~a1Lh9@hDd%ykjwFgjA3Vyt)X{c%NzdXMzd>4wvdO*tk<@W$ol{OFDYkWUgp(y9s}j!eRxi`bj|KDqu9duni7k?%$3h(wM7)TbYae~S@=&Zy)xuh z3buK2494loR1H-5U3~EQ^f-d42!J64=x4SyfRaKgn}uBB9++_FU1GcZVT=PFgu(93 z7S$K>t6qa{WS-}_6FzYsO7KGZNCBJCi2c@Gpn}5qU4fMj`ySSM6yD^Kz{^Je0rOv) zi2m$>E0+c*sW7}wVKHpl;vAb^5VG0JNOlVm+W+n!vO>%B2cJU;JTDD^db*ZKYX10z zhsDbtrP}jd@`p9gVn;IJuNU258ur8}gHHvAny@#Db9o^*zIVtoLE=uhOaSci5zX{# zD7BjY&4WLlkp&1grR}J^0JIDQ8#g==Sgj46+vIn^o|y=fl#^uJPwG+}S_|_Qi~%jV z4ke<}aoRs82vXfURa^h);y>$XiKnWGT)qPyUvS%QzU_&g+CB#s={^}e$N0+nAlfd8 zmg@OAX6nvTokc%d#SRL>hXGvS!_DSMUw!_c3`EAyv?=vcOj?Kjve&1}7rLV&ce&RW z9sawZRh3zdv0(c)zYx?WX?7cULyOr;rv}d`ScAJY;$N zL-aGaw6;w!GXiSY4UCGni>6`AaqSw#oz#~^SZO~!n~NlZ6g^0UB>==YS1f<=E+gD; z*F)a6!_StJi@pW!3rm%@V2I+LK6kuaet-J#6A|uxR9>+j&v~xXOsJ4@WDVmZM3+v% z=Hp)J&TmYcQK&Y{=a_suI0zj_Z6_1D|C=3h7a7n08t>I8&sSvO!#`>N6r_L8_-@WG zlaLl_9)3N2_JXnip?9imAJG7#n{OZ9*65QN(7+u&UgOP6Fwx7LcU=TvcWvPRDMsl2 zO-V!dqhwFk4LJ%VdkK#Hjzv)sP6$LxlnF(H9WjN9j|ij<5vL7h&xX@5C8)?jjysTF zhaU(xYKpdUMco?{D2*$nkyeRq_|XvCkW>gj4+?mgJMTmJRXWx@hIccH|#xGCcjp*-Bkr zll@+X6v0D}cJufdPHYDI*-@1lGf%@Y@Xg2l7(R#!0WJ)udZ!j#O&u)3hHNd4S%(L( zoTRFX1B9KhTwZ~%j@If4H<~+a@VBRMHR(B07tQ1T8T)_;CNmk%`WyGczB_DfdL0qOy$-W=fa}V!4bd^|@hn*+u5 z?Bv|ke)gDtbT(W3y?6I$k*Q2jRKhPr6PdxrbwF&(Rtl94H?CZ%(!#JwR==coaQDT? z6|8~M@$;*J>&ay+@e`Fyn+R32vZ?sPn|o}T$f42Ab`mB=x0AlKQeT|DzOm z0wSVwxK1Ulsq5S5CP%zU{ zCAD@m&GWU$asBwU5KBj{nr0(1QVaNC65@A_mu3_=h#Z=00ZWEVg{nH}RD*EQ=iy^*kM1~Bc{Hu-kV+mIhJ~$57-}ps zb^#jjTB_+MH4S@^dSA~1(tOuFEv8tUG5)^8am^YS@>^GDOd!B0_r}@5i{XA%hk(#6 z45vwWZE$e-IaSt1+Jmt19G!{UT%;3WdN)h6Z5OxkS=E_$wb%oWH1E`c2X?|3ru8*5 zAFY3@ZCz>M#!nV}V<|bZ#5r16NcBLsE?%nGy0H^)oA9*lB+bbNpd9htA~NlCJo=|s z$<>GH3T<1JYAT@AGk7utp-`<#zq(j`bK`0>-EE!P z%oF&$I&Jmo+__;YSBm>9h2Ic+ziHI=XmR7dVt=`|&VrkT5QU)4R!_?XjayiPg4@&H zJu=%+?^L&E0pn^YP`^8k&*DsPe~uS*cKOWfYL;cu6@#5ZzK;g3rj88b>F)nd!ZXPK zS|^{FJOc0#!2j*m-Omf-A;345^M1%XUQIj4GyxwiWN8rbG}hJ*WK(&Vy^NB&Q9?Gi z7UO-Q_+mr#NPtE4Nw_mL#N21=JjL~kZy-lE`R@>s*7%?FnU`LdSK=rJy1H*NG7c-T z!oP`wAUc(HY4PG&rPha}*@uVMEq_*$Yc=3uvJ(KRJ)$w|h8I`D6B_#E?EI?=7Pu1E zyZI99LFhX3gdHPC?f;+~W=sdJkv{1+synLa(d^O$dGc|^y05pkZzqd`TW_-cUy{`? z*eRpe^&8C!elbJZ`kzAU8FVU&`LIzh$PoT@guqpec;ay1W*GKVZZHSowaCg-ZoY+0 z`fPwf+)+M`dU%2P>5+exd^XFpsg81(Z6=lr5~uG2MYHG!=EQi4xp2)<8#1z{A787O zT*w-CK)h~g1bBred$AV?D5-|~u@$0)Emwp!rs*S|aK&z@Em?k&+UmQBi6_#GuM<4w zGK#0;Y+_xY`(L~~J}j*^gFZ=!ch+I0*Bju@tKJ>OwVICE%?1q;^E$xQR6^o?A8tPW z7>KpAzb?wx-DiaB`-Ei4Vq_}9){=Gy%AFYZ{wvU1(Xu}5_n|C%BJdMs_sHjf@jUgD zxaCe1K>UN@AoI)h9~(NEIxtw^bO8DCrT(`;^4g%_Zqy0*8%jFiN`!v&X6&VSUlL#` zw6JlaRmWm8YtiQX8)^OnX?icLziLeDoCm(BaU|V=t(@=^2TM5ZXHlZPxLja@kFl8% z&A^huc@k6pT;t$z?TLKdxMVHcqJ3bttr&pj(uyxV>dO%;I&0y1TE?dAthOnAx;fqR z!9dmUNvMiN4MVf!JC*1kL>8>N&_VGc62PWb%WmF8!(*<7jE|PTY;pQTpbXVTYkS zPS5O`osQ{=+!(Ki*Gp|@7zpikuW`0At5l$|mcOBiyOq8=YGX53v$gcQhF}9XVP-pf zcO6Kpq*7mdRbz2=Y20wMtnP4STw_(J-(H@X<6vOV*CN+M-PYYe-`3JloeCJ!Zb=0m zc9#H$`F&^xe%JD3+zc7LMDEV$BAIj|ju-{_F|4l4X5CHwrW;v5*`>A7qiF>38}d3* zqf=u{^)3Y9$c0Y&;V zYe9lnidXK1@Br$dG2#wx4Fk!5x?l zI}bC5Wi5z-&`ADD;{Yf~C_fTGU2#ntHRu9`v>nI>jU?@wO=i;Rfs2)f;5-%(2!SN4)H)>zgQRpo3^T8eZG zdAKoS#D=%J7*&9*>vRCCwkVzoo(!G>p2TmOV{~_Tz6i%*+hJO99I~`zF2p2_CU7<G+UoJBhi1JtpvJ zbUR)Jd}vv(YNd7l@grdqqmAP7C+(xYK*0Q0WI@l((>i;H5HFFCglUcdy9JhiEXBev zfknjj5*s-B+W2hZp;|{JZWQjM`e|ye4SmOw7a7M_DXR-@>IN+HF?YcOqXVL_*&(K( zD?peQO#B`;QTV&dzca#i=}f+v3+oO&hr z>Ws~X9DtHN82r-GoB&|O zM&u_i*1>mql?-MKA25X>7K{-mEn|+5jv;xbEP~L{`%EurAB^c4zo@+*RbGUz>Xe;u z_lVy_9*y=L?rEwAo`R0^s+-@qE_r~Yax~}p-N@pB^KN!3&Han%9A2 zKhp~aF)!$dINnF-OYSzEAkExsL;$AF!tNhbO$QvtC@WQzE7(BXUVP^8X*1-gHPgBm zVW+$grA;RgLnmQFw1F9gqquYA)?($|TEiID^I(we^!ow$3$Y+vl4#|J(v`3;QK4kO z?dS_E+n9?PEJARJ#ecWh- z90}46#B=&@vmbX$v3`$YOJRMVRbO#kOgCf`AliOt7a+I>ctH3tZlB3{`pNyMchfq% zJB0HE{{?%qkdWzbr2JawIFPjHUu5>uD)miiaBcAdRlk`fvHs-H3b^fYZ$M4gB)=Qf zH2HmzIN}4+;e{949|=ISp|7y!pNS(Ae6A0Cg8D$#$RFnH+aKX$T#|E_M_y(V;-Fo7 zZ+-}>4$qFw$&tDh!sTcJ!=WXO{v~T|ql`0;p@&U4!LG~wbS}vqTW&^rw(llP$nxxp z&R|XD2vL|bRilS&07wkl9oBAA(A`3v`^Sq{;0Z(t2rW8)w0qZK%m3z)KN`Jb`!E?+ z{4l(8z(5S8Hn4ySe`_?=#0}STI%9bou#n+$30JewihNB13|!v{7&?VDZ_fwS^9m;( zB4F3yv}p}#K9Gazp!8h_X|uaf^RMA$qeNFbw;Wfc?4N(#141Nd4Ui7^9&3EIGzD8m z8sOzsR9N?L@T!)!nX##>oNdNb7v9aqI;iHiiSx6bN?v!IGoO^7dv;EDB{F+kZ*_V& zmU71IQm_d|jr6n9GjfFdfwH&m33F5N2dOa$gp)E!1oKk5*oz$cQ&$CZgqSqU%)nMy zL$+qx?&2<90L$5-3Co7RYZDvRDjQoAtcl=Q>N3HmX{IkfqPiL~0KM)Nv|k>uc+a;c)ZwvhXmt%llMN zWuHiuSy@SKCR#RZxbA~#WT!a$Yz3R^;a?ytPttX{Ijd3=a4C@N{-e{pj!OJm z`3Kh-2s{o8(4TlRe5`nh)TC*5C0&(6_0lYj=3BGNu1Wj6K}QZyDPIB;F1hh`Ks-^1 zb0`IGGVg<(f&IJG(}M$Svj(9c(`tOQZtM_Ua77e)xnAnI79mAeJkPHICNq*g)iUT zv+{mMi=@B^O5yQ{8?!gbn7hW}o8R9zz&(Ri6dz=(erb+J9J&Y$+$tfkrnoqHVc%Tg zr~FYe(Tn4K%N7x(U8|v!W-an5n8i;qZpTbPt~$T2t}Z=j=%D)<@Bi*aE2vmlkk3$q zBDj!5x&-|irEh}z2xT;k@2)0!Nl^|I+kRu3vrv9OQa#L` zbw-RXvUR>CPPBQKRk-xEMJ1#g0^Gy|89AjX9yN^trenhmdK71~MOoV1Hi4ST7FScN z`#0CCkYmpJMY ztg{!{UEUq%F6twRIHDl`OMWlfK6!{lfzQK>b4H<>lF*5kl90RPYet1VxUmfE$`maU zWPPIC_D}7j7YqLSQN+;6TGm7aA{@@nQH;V!z~2yl19mp+{29Bf{12`@i~f_Dx48&8 zfF(g&U**=B1&&-~?O$~w0J=+cm?@s+V8T>(Ogv{df0tWUQd#%8#?X!%u{wzzA zS+c3}K0h59nYC`)qp#x1Z{v=N3KunPoUDC8Hmu_nf%_0$cqwFyuM~1gM2zsVF9Vm% zK$XO0TNxXhI5%`_wJ0fG=1;y<o-sA|y~!!+BLw(%#vNzM8_tFg3-}I8T(Nn{2M|1HjKu=J%$x96-}d<3!P;@@zrs*wrj=^m^!A31UL`Y1GNU_CuA- z7n%%FOHv5WBq-MgY}&%3;@*p58+CO#ytdHH2{`}max*)s*vJWPD#uC08|eM7o0$sW z{fCvL^&%2Ea*o!k)a982vm&cQsNZ?BJk997;$1#o?BdbEExzqlB&$&KVi3Bv3fVY| z%A5r^d~RktDVuSIQBQmOM&YXv+danaE;rgfjX)5X7}1u{)b@p zLP)cdi;+w$XUUM0L&m@5W7V#a;CUyEarL+w8 zXWiA*G^$fBk@an20->IV+{P&O%A~)7?|)CrmP8YGrXrCNtG|2roT6r6KNz|%%98lW@Pg>IBkx9%z05! z)e2-XcU4k-LwOu460zN@QxsvTozNSr^3M>wrnB+N8hEaJan%;>>FWh<2;DX#zpCl9 z(hgYZ05(>5!ih`XCaUyE*C!!&hNycn^As1^hThUDl0(1@lKaxw46Ooo+U>+vTBpo+ zm~p7uP6&UPuqWEwHdM@%W90t50$4`&9PbtQzA_<%n!Yb zK2Hm637PlLGD1Q7Ij?a%;zfF$r{U#S`r9w-^ ztg`KcG{tURVjj8fe^1x!kuiY*-+kA1&G6Ya+w%KFl`ZBi-*E=kGg>C2hfhV90m%Vi zr;!a%eo)EhH9X6cDCh;N>S{6t>t- zb$YCBbE~Uf>vEU7xNc5CuRc25i%A5aE>q7oC@?9*)0ohEDB&AF|9S&9{imQ_wt;SD z?N4H|Bp5uLab`o4$#vEARPVKRXzh0r1?3GTRN z!K}e7wa?#M(R#6|dxK4ns&mzO)ESrI-wWD5>IOl#$-eso+U%%#9vMftA89G!+mw)qS}Jy&0&ObYUv5_dg`Xr zQ?KF#Eg3sqT4LXdrtxdfI;HXh9?ArIQ=^7@%)Kf<`>`j)^>e#)f~^Bywp$+ez*SgS z6a^7}qe6XTGx3QqJ1e%#W(ra>GmC7TzHj=(%QU?d64%KRAJ3>H#>me3@ZW2YldJy? z8vhiEe!9aAsB!Ad#WE$uGi`3EL|Aa}d=X_Zp0vetcHSOxhDTcImUk)#M9J0YR}=gm z=Pg4Vz1JjYK!zQSBG!Sm+H!t7cqmcH-X{LL%=*$G5S%}2h2QVzD020u8t5bhyAj?W zzsu9)ZTZQ)jjW@7?2q?N=L*MaYXPx25j`b#AJ=$ddP$!M}`JilHRpWIgBhw+RP0f9)XY)~Dl8daGprh#}7;#hAmO)ZLz)$(5m zpAR@K-`JCdq`Uw?=4&}|#=?Nz_>Gm~WcP$N^K%0;ecxrX0`pS?^FzJif%8m9qXBmV z8_$ngW`V#@V96uwS|fj}o&28_6%&t%#F=|-teZ}bqh5uR&*nxgY^E*lW*3)3WFTLJ zAF_G)N7G7(084YMJ7IWjd`1-i+8|R?AgWrX5L<-z=gueq@_D9GDQH#6WB7Poc=;RD zzfpYORr1|D4gUO(XzrI?|`(vDd%!<9*8YG#>3;UII*|U3=_Vx5m6_w`XUQ zph|<}<9GspQ3AH*oNG0yGMyCDV{Y3)&jZGV;gx2*D(uG_<6_CbnXk&k6X#)i@9ig) zH)bda9ZvyB&9L*PwdQwKs~2JIk5rh`8wgr^ESm_|pb(#mu`}s@QK$}N8H^?$fg7`) zkFtnC6lwUd*Kh5kG_>d=uX9Z8t6;X7>^NKr3#mMX+#0gStXiYye(>^^!#TTGOjvhh z76!_YsTpf1Hi^#U=V44#Zb}1K-cH2T-nZQLz7bZ1A)Pg(O$?!~ zPeulzM*Pxsa1X96x1FMRJ%~^<4-W@OoYf~ZFGwt2i=TbE9>p@aBcM>iXK0eo1(oYUwZ* zVHE*-t_!V{cuop@6r{%D_ZNRvu9|Q!4yDfbnEzm4e7KQZ>qK%0W7s9&?hb4Z_>gM5 z_z50eTDNhrYUcLm0Nw9|V{D4$x{L%CJ?NKb2bH)B0?sp$L&$DOk!+9fi=}EvHO2?} z!B=RKtDemHSNOBfedun#j(-KIg7Chd%5@2ViSKWm>IvUxMv24t+!nvU$}{w@d`rTh zEX`Sh0Jq#BEJ1VsEybsk;0pSOo1MEjfkrY;)rUV`y3Zo=G%(jy5J={six8NYi~7|i zUK{G+Ox3G8h0TFJ%YBpY7S+c&G8rQRowaw1bHl6gEO<1Hdx<4<9PSmy>|AR6iveN) zr=5?Bz@Q}MVE^gdw*K6`q_);W!I~lc%YAdJdGwMMx$Pm*-U)b3nc6P7a-6&O^bW3! z!5Wo;`pL1M?4X+ubha!Kp-RW8IFi?b{{WlM%`S8tq)d3<1k0vQJtX&k-IKrPQB`NL z_8LQEs!bv`O&3_x;5lCw^~1b22f^S0JiDQ9%HPV3>CV10-_+S3E3YhEb(%ZTY zSf+H!tuV-~u*)sGV)86ya?+ckPIqoC4+(+B-(El;MOVWof)X3EvIeR(A(k_S1F#N6 zR2C;BJn@Q3j*3fFML&`NFy}Xb_c! zytrqCF{(;a=w96ZhabKO^^A3zvH?v>nmcDEd_d5M3_P7_q*j0NJR|`#-iF5!dJ!M_ulMAy zZ+prT_TNK<31F}#{UmXbmIq-Ape6%!%G^)`16Fm%Esa$SyCu7#GDV>;_`gm}w{8JV${51xiMHh zOrk*UiG$}IES@zg-ix%XP9GMb`G$1v{Cr3v(KtK( zrA%ol<3Dy&ewT|&%mI7Li7o7?7jxvbOEjmBtk@Z2sW_*_I{Pi#beYbM7{{~m_&8=@ zb$x$Cmr(Bg*ljuiEWdgt{=@en<5gwYT;3V7vx>_0Up-1`6c)JeF!72lBi`!zpTpGC zb3K~^OAmecOG%GnSg1&^6hb!{`zMpS+j=C=65djZd7+hq8*BV65isJH2-@_yet4a) z#Exgzx>p;!XhDt+)?u-YtX}A!XmK$H(DwK$n!+=P2q5eMK(}yiw|~wn;;s9~p>m;= zjhd>~Q~CI8N47dquKT})wOwhNqEUH-_Awvg%&CT5J7bqg`6C7rpz@C+j6vk`p~kqm zLi_F;_0@m8hvBQW=`ltqsI(+xxonX~b-eoJH~r6+2tIT`F{(iz(MJzaHz6SV;OC<= zH$-xaccQ_6zZ{}@8y^crK70W4g&%Y0hi(G!@NKB1)GAFzq)tGTM!Pt;fUO4pr-G`& zQ@aQkslTJLNKk&Csky7T;!6->QEN7iC^!*V*4_CudQTksgIGV=Dih=2+wC1aw{4`Y-pi*T;?LxKJRi>f8!D)gPaae=eN|br7oAe2 zKZ-*Z1465SjvF1{gx$^$%m?$W#dOKEkPUy)fw8;$G0Rh));kMXHPg61vorkCSBUxU zPz^J)AytTH-%+ZWK?+{c;Q3Y|eK{QD6-x?pDPU-W!l54jPUf9*F7s0GnyW^TY^i3A z9T4(}ZN48Snb!vzy91fNhkpu<$?e=Gg~k`>MRNKy6f=s83z>NWQ-1ahd-=eZYKiQ- z(wJHHF>{ukxU|!2Q0ga>|Dc+<8yG_2P>(@aDR2&JV6&=7)=~D=YoE71jxhKZcm;Y3 z$qw!ayh{qlb167FAoyOzGmH%$>m>Jo5d700Z@BJDahz=1=1F0?&EtEo{l~eaxIi41 z#{XL=#f_(L8w~|JCl*2Fj>v&gTC~*+wawDUnLHU5oZSD3v0{Me?!XKj60+hz_W=_A zDyEn@xCCDDG(a$j;UpntaWqh1ja)B|-ME1qfFhRa#Taj(W76=7D}`STvkeL>z}-oA zbnda|FG`auFK7}AMB87YN?YmK0s=~bSqY@I`~mI3!Q$tq2XKff5z@Yh5Gf&_Yh`E{lAgo|#3hCbX z`;&rFvvHq9I57^pfURPvY>Dfp%J*d%$hq@Wc(8q8L$=&7Oq1@JzM7@V7a9BOsYVkq z*{3edM}e&=38lnoYU!QOxcFMKS2w84plL^{x%HL1t9|-tUUq)m*4)eB;^ZD1XTLj=_TyECj@*q1hw}Pn$-78#XW5 z)#W(Y2aVy`sGQ8hYHzJ+$(;0G3V5a{w!(M=?2Fpiy=yLG^_ z)>S5MxD)Fq<|Vjk!z_2NC3cm+>Ws*TtVSK3o^sMR?c{6w92o$aS&3coo9d}Yfsr8d z=zJ7qfPJy@OQi~6tRd|dwV3uhDtY}av6LFaJAG0&HcsQD8Kp~S_}vi;Xhe~(C`%Su z5YQoyC%d+@!7LqVli!SbRmdj57G5s>wkb|ajY9E>b}GEHsFHI6R^!NK_H$TR?AOVa zq6mZsdx5pJVzwuZ_@B+I*OmTazZx*U$7`btuH6B<=v7g3ir*R|h|@m|HK1*B8Fkh{ z7*SFC5Z+iv1UY;=J)jQRfP7gc$c&zBQs%IH>6Z+7dYpo|*L{s&YeZH0h0cdzT69aR z#eIhukHFA2?ROJDTa=!KKKCS|d$1gC_@@T7H^#KP91W&|HeJt-fI-G{5mHj2XU#iz z{SeeW_7Z%f6S|J<{_bwV#{RJmEl%x;I}((-hq;2F8+2NIAf-GHvRB!{ZodV=%RQia z>OhH6FCLz| zUJnN>46LIiqZ2->^<-JbsWiNXH2YHrEGG^B`1-Kc3q#;2uWNNMFY0 z>$}&(2)|H8c3_9hRE4GZjo9^Ge12?Mg{yHi4}slXsIg=ooZN$S;s@v?o$x!q97G{z z>0D&vulwqCcISSFE5lMI3pXSLZXU^5VFrn?HGTi$x3TbzXBr%P%nHWPc zt6q+mV^0qg36>ejcq7EhfLG^E+JV^HJ;%l6%8GNQy#er91x2v382!G{zN;!E0oEug zR27XvvMsQ47wfGOa=t*c)jL!}W=(}^CJF?b%w~y*d+53@Vyl!_;g?*wxlyq&8sjtxVF07tlI54vkN3!&Oub}gc3ui=r z=PF;_vG6fMx~A|#XP~v=vH(TZunV|P@iD>&2k|%UAG1d=F>S_T6{FWR^XMEDHO4S%ayCOiG6=SpluZ%g{%c6gEfMN3BL^y)C^L}I6k(CM zO&31AN9-3m7Vyv8Vi(jJsqPB>pW=_yJ+lo%4a@gNi>lD#UJ-IglJ{JiS z2Ews(DJGTk%9#-jG_ywyg!f_fI!#EBp#%_po()iU$rpVOho%#u6cP;^7ir=>^AQpl`&!Ygorc`^)Ukr3IRtHG=%h_VM18lFK8>` zIZu?03@y*B$Z(l4nft&$D*gn>nPO>~MT`3QeLVao4}a0gz?Y;j+N5&~h+F=LUDSg9~)`8C` z`w)Wu0YV3q4sZcY2?}Q)gndbN%uoOj7%m|zMmHgTj3Q>M^cd@il=JR&ikdB%I*l7>`9)Zwt0QQ4 z|3-4%$U65DLrywMQ_NQftE)jUEhR_2XUA0t9LfcX(qXKtsG7eV9~VK2$uEO^Qvn%qr9=q{A}{{9e7l}#^+Y&m0sgzSTp zQ*m&%-9_2a)qF{QhASM3h6Cq!E1mh~@K=m7(?S|aoOi`4*Kt0{PG%Eu(RE>}$uqs* z?4$Sm5%G4^@EP*6W1$$S2i+tp*6r`M_VC zp;=6bWRWJ^9%OsMZ!RjLDB3ck1}P2=l0-szn9SlATRnMA$M*y%rtwq;+r?k$wc%&F zo9T=GBNbGoh;gZ^zbj#s4%v%;kD?6M&D-pcY89#wG7b%^gtjqu()l$eYmh>(1_knG zP4El&-lc&ApGto&|HOgcBpQY`zXz^ZEj04gsdeCPVV}5c?DpKf<%4baf=l*oB_}bc ze;zeT`(1$i%Wh>V;4n#Qr>vd?a&{N^<#>}Yix#UZ1jJgu8BBCkJGHHI&*BO)Kee|# zZxvDEST1h^z+e`mN$^rydu>HIEM+H48-BH85sT`CE}N%J?JNsX*RW$u=nH$%m1GJQt-sACdaG z4r3m0nqfXA(E?!}ItWv8g%$shoJ>%ms+;?Y=CXbZu551@7=O& z6Lx%6vxB~gp!PCVD`%-G$jH2A(*={xPD&d;In9Z zr;U79h$MSKy%U(UPVYOzHSUe^<(7dFRxS;Z%;Ur*^5U-o8#oXL#9nmDb|sX?xm6PkE0kJY^>puuHQ|r3?Cy5Ce5krcny9ro%d@q(&VjI4Db90jab$2Z&p!Ymq4g}+n_G# z0TCI@JAz9VDw0hBA%^QzHt71OuGF`-fk4o&&{k06`E1B*#MjdmiI$zXaufs!!r>bb zIQEnHBW#hXYz!98pW51&?Tprv_LZHsvsY5_k-wbky*&+%3uC1} zmy0|Ph4}qlHFetE)ZvGfx4}(-x)Puz)t@)FiZnfWH_=k=zSoCcczu|+R@C<;U^>j7 zCCk%Uq${!rpG_HSlNTNwj6!rpTso<{8K1DC%aB2K;przSZHjIy=}H zbj*@sqW$4mD8FkF@>XwUrabRZteOo$V;pLh9?=d{vs?d84UdnMd|-;Usw^IMZTMlF zU9$m8Lc@mq-NPy}(SEDs5+2#wj{A05pu72?bSrV1&0`?pbGPo9HwjC*Vs&%%<0TE2 zAxiSBZmFfO&u3~U8xZ!oxm(5JfxDwrJiEyF!?Mbn)Izq6uRfwFTVp09rlS@)(gyUQ~y#=IKMbaE9e z#$gR&>J6&B#AokH4#LikJL2$NJ4;?m3sXG#B&rIAuZ_;@37Dv7l_nId|GPV#mC1~f zb#HFL*V#Z;?sm(f>=(DL9D~FNG!XbP6yF@PXv)kO!;TFdxmQAj_{h9spNOA?B&bLp z$s|jgr!SCdlNJ^##=rXIliKb=?qxK;MIHBjWb02|mv=!Vy!ItPrm#x#fP**`rDkCd z`>66XHyD{r0ow~}t%dBY_2%}dF}WgrTDj=34A0@ z7c(zC?Y)M~Qw~7<@b%w0lt%K0K)&jzmY#&mI*^!D0luD|P7|D;IhBz0Q6KQ2RUi& z2wHOby`z`^QPVxEs#kl(tk}<;eA4SXryTkalYr~&h4?==uCk*IDNq1MS>DEHf>5x&lqXr zY4pQlZoeI8%az)1h9g~Jq-)C=Jssv|v@|>f%98JDMDbO|3T9rx z=2yRFN7?oZPu!W*1D?U*1AV8A+^0O;11%Z3!|C6&PxFhHMKh z=pH5W{NQ>{906-|Mu9~mn;r6J-|E9TJ zZxEk?9K%k|;Zgf)8s+EBO5fwdh1yh??&7;z>RJ-dZj=>$mbtjp=;3;HMrH=GurO)q z9a4`6v9v*90x~i(ny-+TpR<@VH#|_+fBl03)Uj)yA=r0n*ze(%FoGo0sRI+J7S)FO z_3cuaLWt+RVj))?e2)vAeL$8u1cDW#x};j)9~-nBJr`ZmSGK%y^v|lUI@ah|cM@vV z^A7I6dKfM!kwfBE6I5xO7h5f(eN(+OTXs1*u<>D;ss^>Bn*!R_BIxiXso? zI)7nLZwT*+1dMq7$8HsHl{VRBQQbag*>t2bSdj2q(WK{viDWHpqG-A&ZOT}l z(Ox=9Xy#tiR$?iixX5ScZYs6eUrD_EPuUp43L#!CTu?2=XnLF}eKRn$D$d7rAR%5g zE<@umoYO-(ZW9ASz`h%C1t!n#9~GS3@voLpN8(s$@>tD9oDOX8q+u#RYkRP4u`d8F z3Go}vD1YBniH|tb^Gxio>pU?ZMQ_*|=;6J@GZ)7VXRGBY&bB5e%mLV2@7V2^iM@_6 z4_#|(@AnT2hFq`FJrjb8W6$~0H`0x49&1ak)Ww=Qx?U3mC#&>s$485%r`qmJFTInv zb}}0Oj(X%MzJ8{T~qYnxqbHwzQoZlEn@t7+>yYP%&dr2AYY`%X7 zxu9g81X!5K@HbZA&)o?3R8;uTs-3&4&M+El8KK4a_?c@*?qM#3o9>x*E6mb{lK&Kj zWXQA}JEF7oBkcdzvj}titz> zittV%?1WX9vi=dk_=pWZ;AmPa4WYtEnv9Q1?8J)kNf9>HqdFzl&&MQ8bZQe3zSR>@ zjs!8LRjXs;Zs=}#J>_Ohi^JD6(~wcxa{4#3+FhnD&M651Z@2ob(nZcz-^1Hj)~5`O zo*ArtTx21{`{kVPU6XTg%$HBg&Y7B=#+$*UoL{bh7m}5BPcd%`bN_oU)#;z&yeB-S z?#z<6!n{lzc{zFJgy*zrN=AyMdx$1sldojj%lI50fra0AUoC#w5Q?hWCDtvKDV->x zO;g69av25q#l_+Lru^RW_RA7~&r!;&G4*?1YDo9YRF@k%a#XHahj>X_OD~X7obFdk z9vJWlG>`GS6{!ed?Vo?Y@}|Vtt8@aPjYJs@h>#@`c16B4P6Yiy5^>Vc(qo=b5^C`l zTl(P1m zov0>qLcId@tKZ~@1qwtk-`L7IM(!AqNjtY!$TmsiA6agP(@b}EBzDWNj-Ey!0a9Y7 zDV{B^YPyd1Qf?lbD23!YrUkCwnatG9$#r(`i1vv!^$k{jnI-{#s^qmNSuNm=$e+!< z%F@6koQCgnD*8XWE-bl>` zq3ZSWfOtMOiWvum3*FrHfmW={vzX^7eJ6wI#U=*hg}k<>?Ja&cb;;xc3?16}w7`96 zw@|7cd#FD|t8YMDQfO%VD-W=&{ZzLWYXxA18D2gcOE>)_%Pn4b8l`Rn zOYj!)#7+X(C92&L-dCsV+b++f~XKPOpQxpGL=r_q*3x=9sFn+umrS> zd|P{??3NOD6#G!O8Zc*Z;##pIac3;wnm3g{-Y(BmxiuPj2lvXpx zrGBRUJ!5I#z%kE|!jDf##GtKPgBK?jZPO-&W)Gl)OJ|ktKy-_k)J{oKPBx`1Vrk@} zRnL_iW9W^9l*yg4o|7hPYaz4t9|X)5LjzB_R5SXiRW$ohV_{K$4dyrYb=hX@=vA|f zOOc5f-cg}>{7p1x^xb9*vq^dg#gULvjfxL|0C*FB<>KGJy*D!z#PV7o%d{~RcG!(( zx}9aw+KkMxB$YkEj29a}A8o3t*Sa@FdvK#KkQba8t5{};8H_#E8UB{ZQU>1YcxIvE zPz)z5%jm3AcWgP{EOg|<26HiDxw6<3P@u}6H%H*^^xrtLUD)dsQ6jN>L5RJONvDx# z%O##1JqnsK1w7yB@Fl!!o_elQ-))`wGop0lmSbMa?ktSPggt!{Ya(Rb+lQi(@(k)v zT54Au@o@PMccY_j4wBxyzy3dwvdNL7g}g|QX@2QpG@bM^Q+e63mQQ|OxyV0v*#5Uk z;JLWiYVv#Y?2|ino7?U*lSldzLyJk*ws8g_EO&CW&EyT8cl8qF!Aso>4pDn%!U;j^U zGoJtSKeBrjaLpLZ8T;guM9=Y}5z{UoowKJTT?vgUp=$XqV+Tj)rwJVID$4xM9oDZ0 z+cB6c{Z7X`?-1J9o1(;l;3QGGTx`xrQrzEzvYqZW&KPGMN`trD!`p8wo1tw$4CfhP zOkzJ(R>o)ige?pRfub2DB~LO%hkl=^p%hFcPDRFlI|)y5zOahErmcZmxF??+fG%3s zrvOIfMfYcbCpJB>(nqc2-&rqrBgGrN+?}ACYmEyJyATP!uK^2#+&tW1>G;W8P2)& zN9PK)PU?)m)iGQ9V$bPALaw@lT)^*PgQaXi143^lt4!$KXaPE?VSV)Lwn;)z>F(|j zx~VYl?K#~za>f|@hYmE~ezTOuDV8qmZ-Ws5;5}}zw8@|>iKCrmEMog_WX z7q4?VME_cUZC`r;yVZj+2ZedUhMM`bxYY1j=bF0R!bfBz5b|+FCF><6Mn&@b1{3E4 zJfRZLxvPDLwF;c6jV8B{uLZ%pN&>pqf zBA@2EV9pnBt{lB4U(70R%2&EcVYh~S(d_{hQ)d)SIT~TgH^ABPYS-NSdinVIy3OF) zz8o3Euzn8RO(tAy8I12}h90(71=$!1G`1go!CmO5UlwPicL3j}-{jwU=lIZ%6zRN_ zQ3JXgKTeF;g zn${D-^M;az;)6tK?Bep-6u0m#t)mMYyFV9Y$u;()ze*Juep!^!A~;d_w{~Fw(Jz$RQed3Q-eT3QxdYx=iTGtCMn)uHUt+-_lc-ZS^G|ITRbNh!g_`8@O|E8t$ z?07j<`yZlGFrL}=V@^4)w&E{9#quw^1&dnw%n3TrWXCAa@(@I;2UpMmycXK9ZN`>k zq(IxgSz10B;~eEEZy98bF(ePsTMU$mbkYeCed*gq2O(qY!z{#d!JfP2oeTD*Z`Bt6 z$Eig3O%K?4FlQq_SBt*IN+a@GN3Y3kA|TGN#9IjfrmM0b>Keo;39fxYjAF z+cY+BxM}&}{vshOdm8M`8x6|kH;7qYHX%Kc(Dx_hh(@;{ngl{yt*nZi!6~iEg%PUNPx_Sm^2^QvsyhT{sUEF?QST0Rq{Phdk)1Af{Vli#1j7AqSkShnx zQf)YU5QhMR9bFmx)nGhqdirlFt+U7%qScu%^VAGE=sIN5l)5epca)-Cy5;4ygr(ud zv(D4Lx?o&-;;4~wkcqUlK33QLcCOmIz1~L4%Tg<0Zg99i_WkQA@)Bt z!)!W9fo_hCih~X77sFYco*~4w7UhU^HkL{)JrzK_f>H98&m~|F&f*BNiEB{0f)^>% zSRzxwf44q28G8%RqxdmD`1K#wIeTy9`(f4myWy}4?`HmUW%@jC2G&Wh+@?QlX#a}I zKU_aO^7QhBHkWAt9trconWtHS6Y&A@UTFKT*Qx1*oilF)TF{LA-IakUrC$xU_MO#w z(Xi9;W%HI30(5h`)>0(RM%+^6Ss$W}RSgID(Z#$X*jlDW+R|ELT`Rq2$JuPgG$+eZ zAdQZWpsH7-cSre||J$;C}}Z3+(Pxi{fQe24j?)4$VQ+69a>SbBwVvi^K}UiUwX zSqPI;k(j>$32ld*Du$Ik-1A-aqMA2Yn;m#|oba^R6P(iQCXY5PD@4MmjXS49>?=b= ztCoq`IZGw7k|6mSRQXz#w7*mbw0Fz{a}dWtnL_BHNUkx6zhFGTFF4Fc`r(jsN92|+ zk$S$pr>1wiemrl}+Ud2C13#H}$K?U}ao zL~`qUKVU_8MpxaL&?2BChb&T6#xpCoN_-#-po|?0lrxe2F|s7-1g3e3xTw3HUjuY8 z2UF!+&|J)vu(GrXo@KmP@#&+>^Nwy2YDS9I)VI?ER5ik zyAq9vHqYILYezXQ3;(j?!ObEd=<$;D&zjI$Kf`y)^t{;{{EBEj zO^YtF5MQk1Qzw3|u}-ge+a9XUW4N9pm*LQH_923p&Xg~Rs^vQITvYou!(P=Grj!?! zjk%=_K8lxl>f+^e6zUXKIW8 zy*c40oDYpFWOr+5dHdqiK$fZ|6ZYTFg4Pp++&GN{E_;S)C#vL)mAN@VL>)T;Nt!F@ z=h{VA58PTQ5(*M)k9U49Z!3~Vgw4;S^6QW`=f0a!wovccJm=5VHXeEt<%?O>wcj;4 zJt&=3Gy`InosM_!`b+*^(Au{qI7%D#!dAuoF6A1SNQYlOCY6D;*3k&nh0Nb+>hpTz zs)>ZLRxiY>ZC0&eRVi<{F>s>?zM;j#{cFI>P1(B%yacwnJN5{;E1ZeiR+9x>lMP^xSX)qQFMEp&n>E6gxeJo`s`qWCm8*-QZ=@VHPKdtLJZuL z$#Kq-uD$ezx+g=}J&~-jOm2-g*o9_jHwEclV%N~H(8;L01bb&4pwcbS8<~a^ zY|@(Cy~@Ix%eo4jdPAo7S7>U-%$ni)QLC*l0->NI_}3VeLAReb^604DFmzXkR%<6~Dos<4^4e`S~^jP;RSJ!?f6f`gnuXGQw`Rw9Qf z3+&8CN5F?2-*nk8+g)eISH43&`qaL%+H5+!&;Ay~dv7|*r6jP?2P~7YO78~=XGsS) z^>S6IZ|F`f7Z46Db5aSc+VXN8dFcD-WvheJC->u2N*7LRQF$VNz`c?eTIdjycY^!_ zW@aGA`cdZo^+-mvd_cqafWVetH@Yk#0L1Ou>(;Cu2w<IThbmzHGf*UMqH`f1-Zsivs;<2%pPp+fQYa=C`ZvRxYbQ{unm-H&L(auY)!H z1~>gao%Q3RQ%iJp6Zlk^RS=UFeUp5b)W551-HGfS$zkRS- z$*$LZ`Thph>w$QVqoTij1gU`90rS*z?K^+a5xrA#d#SRcarpweqp(~P#9pC~kd6D+ z?32`+RNeFz{q3>&+vi#v_7x?Gwfmo}R~UlV2|I0{W#Elh-vJv5t%D&)Zv8>RQmr3n z>Hp6Lpos#slu)WWrF1%Qi8bkdONw^sr~a^)CK-db5VZ-f16QAU&6^-$9Sp5QS1)nO zqZ4I4@Z28>R1Ko21HE2={8c4nXhhCU=a4fUM^&C8QJS7t@dJi~q5~U6K;WS`PnNh< zi3Av9i<5i|Vey}s%b}8qk#5sYP#=(b468$rk`Pl5dKr5jiS>P_{G~z^c~?WFnuB>F znm0l0f9FS4Ub0+R9a@L;eBgROES-n^jBezsAdLC+f4fZ|p+tM;2=f9r{%n4GR1qvx zfpu3{qTE~@;EIriqa5H8SGeo(BVeW+dEh?5f9=>pi*~ekR+|H@yM>wqH9M~8@cxMU`$Q6%w&DS19f1j{ByX-N@xXSo{ z*clLZWq>uE2=vh^eDzd+<*O~!L+G_ATzFCoV~VDa;wKH;2>2>7V~}e}BbTU=v*k9= z%e1vpn;@t0#-fX*!0dV3AK~^~1fR{{M}Sexr>YIkNwyVFauqpjBCWN@-^d=TpWF8c zSYel_`l>$oS=FvJC1Ht7De#+!FHqmZbPDzWGbTU@hKiD;DQ>-Wc$q9@-{)?n+m<#J z*{${Wg&$%!Z8I&bXD6A5p7c2 z0wIemlGxpNPe#XZRq;@5#XXghM4}@pkI{JA+9q<#q?=x9G_o|$j%ZjEMrsWc@<&Cu z&~Q4#&)ye#NH{*l+0^6l+SMle-SSE?38C~Z7f zDocf>K+gX*B<43gKv`_BE`T=qgH<9AW%?&gsvNn9xWP^NPu94wGg^H>kQg7H++N3ySdxOVv5CczngTf@G{;I^nJkGl(FTleO(pEthj3*$a8OMjJAaB5W#B$BV z@hJ`RbYrj682}=ICp{`&Tw86l_tSz*S&~9Acck)PAb43GwiZ4UA?8i1qW~1<;NpOJ zQC_IoU1fydUvksI{S~4Z;wx5+>=6H{EQP;d17IDoW^@S(`+T{7+=~_z0;hwW?>M0o zUxUg8(Y-04?DFV5@mh+%OMO+HtY%>B6D4iF4NW-CO(IR2pjuze9usp4;*F6DUOat* z#Uv|n|F^)+#+>{maN{L^Jc7ymN8x7qkHSq3Ftc*9F#iw3&C1Qh&hft(?hF_`rNQ?n z)2;Q_uDbS7iq*{yBhfk-+&b>oI&K*I{UNy)RFRfwTURBEK@A8nXo3k_NZY74nu_XLvFcdsKaPU0UHmG_^`pC15g`sM4yM0zUcZRVwx4Hh(c z!13;v$!DH;A0Lt^v~x%ovbezZWM8aXwm-%pD^yCKVKH+Y^879;Uu3O%_56xfZcG&P?nNPUWp1YUVsy1K0oX4+{+ z+D1bXcUrm)uQizmC)@bGh&+?Eu1@r*Y zW1{T4zaP;jc%y!-#iUNQ*)y3&A@Qa)Y^Ox2Fz%Cno_Ng2gys;Hc<9+OFpTOw{IUgN z#GAzTqOqfrAKdmSq_E`0NwnPZd3$6w$m;0mn1e^CES0?nB-SEbJ2i+m>Uhi2$LH1E z@Ib4A6n;fv#oh619K^mR@5tvs^ECh7M?Bt2FL=zKm~ev+qPxq*o`RM4cA^f}LxAiD zqvUQ3OVO|q{vqWVlfcW&<7`p=eHek^&G~J7yTQS~H&gS^b-ad5Fm=$mTZFj>rt~Ms zlMdNr(SIiyZbV(L8`rb({d1AHm|eTsi@>g6uN3A?*=0K-sr5i)%jH(@iuGI98Pnd6 zjgVJ&sJ}}0Udy|ms=-GQ$1>a6ZVi9iX)M3Sp>Y0Z0IuDCO+tADrh9d|{-|*g8nH;u z*SJn|-^lYzY{18a5$`@+)^&X{^33?&jO*V_Jt`;XBA#Af&vfDZ*IeMg-}D-VPZ%%u zc8<;sLwsd-i2~yW(#JgjAprK&Ko%%V3O9;c7g0fm6&HT;y((rsUMY)>+gwA@OOeUk zWUi`!S1Z}8S3KW9U1Ki@&&r5-UrktL*gk-Gn2dS&*`J$gKWBCtI#d8s0sBqNMXX6? z$(!kOJi8fD<`EdSNWY)NmDX&%4UPEf^d%5|KxnV|hJfRM_v|haobU+rVRz}b(T*sL zB<|DH`TikO6?H{hgMJr^gHWARlN zR&Ub829=J3dwOPJS)`E8bI>um7+*$1J2oy2Mvg`qK?ra*wUpElZFBPQS|(7^D@)p~ z(SM20^0B}hL}h2p6P425a!6gXgqc-23Auv2U|ePH zrgTzRdXGOL3gv&&3Ck+sR8+V&E=-uC62grGjSh%g)sifk%UM_VPp5bm8e4~j4jbLs zZv&sL>ivkj5Lle96ScK*MSj>&iIzYcYu!ZQrvV!%W2iJVqB576+-*DoJu)$TA zO~Gjx@4W5~Qkv8v=q9hQ^>vm8gxP!FK?Hr);v$O z7l2}@DUtm1m#@1ed=Mzc4ot(~n#OA8U;jI+%9)TsSp=uv7=>ea^#B&xZzv2)P~o_K z21#m7F@=0$8taCwB3)y!DvYYes+JHcF^S|05RYiUUE~E{vSD=geyoW~bW9^2&*1M> zjErug{Xb3NLaZg#>d?+?^?(j4d~O8ij-B;&r~!l7dd&h@sQUPUIjm}o z!_{O(Gf|&11Xk`Jz6~ZJmr&uQeB-~`!ly)FqdU~K=o62H_C!6!iK_LQeB&BA=eC^f zvtDwEc(puI^CDry#@Iv#HS$jxfEv5(Bu&C@*}szSioJ{&G?B_oDr>E0qs-a^1OPlY zdIfJel=ZbalRa^izrg_2Qn<5f$U>W+Nbn4u4+4aRe}$FovcadkIdaiLBlzG}l)BX4 zWD(bHvn!nmti7?`Z{;0Pz5*Ei@dHJ{Kj|6PA$$ttAOm~MSN&SCzU1!obfSais?&K9KXN#28*Zu$vh1D z(JZmlU$MQqGo~z`@h0e|+A@9)F0O{(t3I*4)gSmE?yGart1nAOxEj& z7##1VDM|FRUkO-Pr0F4pSVAX5409f-C(-7gH8Y&?nwVYw2g#cbF^39I-N|$2~Fb^w^iuc!B#K*0u(#fwT)31icOC@O`zrO=BMIUnO53o(DX zLkBEz2B&rY%k3SU%%jH@DbV;1w;5$D&Hqoh>yRNnh89uwJ5*T%0=#qyt~sLQ8bcIR0(_h+nTRtyt?*|4VY3QxmN7P4l8@yiMJG81ZI(0ArpvnqJ>(PD7}h|MlCS+qj?;UJQp}YTO_+xbx=+0 z6n)fTZ>B$U8|-`}Z1X^RX5n{&a*X`;HuNr=locVq_43p{ui5kzUve`A3}pVg9RE`nFdJ?kT$7b;ZI zJ&E?b#0{eG=RSDS7ZlLj@cR5(rLL7AtFSk8Apv~Do6y(Ey-olvw8xDzbaNf83)p@q z!42fj8Vo2Ua?7DlgWuIgTOM3JpI+W#nZn;RH$BOHY2HX>Z?Yp)IFu&fNV0Ltvk& zL|B-aS?vMQ#6GJ(OS7oAOw+I|sSi>yO6E*7W_4-^&IAPLrO!aLvE4dQ5Hj_LTJQ{x$NL` zKz~J1i6)SMhM*3J4S)~04sZ>qi81Fv{|ccJ`s#>^>@CJ%08aw7D;j~wdjql>LKlD) z8#*EsAyNQ*X#{LyG$KL;L>tgpeW8*+$cRx<5XC@A2=paZkX6L+`uvzkWuZy?d}c@; zq3K2Zf08OelZ*JqkQzZ#i}>xJ;Q5h+K;wvuA-{*PhA<)*qW}^aB5w%S+r#wK2 z@^zj`a1FcX5|WI}6`)rEH@GvF2C zfdV`ddHb$Jw%}en3``@t5MPMBpq`T`t2fpT0^+MbK^3@F{91;nf(AqIU+S7N<)rRQxjbQg`=@3t=LNR2!UEr( z(MDd0E`2-TZV*S-iEMm3U~V8s&WNggJ78}lVg!BO2=*~zwtYL`Z%{|Vi5PwGpq}YR zZi%LS`QUEgN0?)_efSV=EMtm=vcTOy_o+tyw&5HBC^5rAi2i)AsIITbT1b9(ete)e zOd}vM!9oxIcRqa3bx1~xBVa@xLVZM=M8q+=z8gq4GBK}07mHK&TVOYEF`Gm#{%?5u zrbI5j8!$J&V?IGz!S?jM>t|C ziIz(MU}qyw=Xe92EL17`Sau_ABp5FkPfOwMr(PE+Z}Hg46*pIB|H;Z}%jPw0@Le7F zi+f)vI77^itqKIHtV&BsgR-xyb54lDbWpgcPdGIJ1C19(?YxRdai+tXXVbD3<)1hq zynSK{|I+z}EwW#|{tqc}o67%uf5g-0jv0>t%;4~R%zs#Id7PJH(iyktnT?E8PwwUU zRr9mUbxWg9$=%=2Uvy89{3vr6zyCk3&MCT+sNK^Y+a24sZQHiZj+1|qj&0kvZQHh! zj?Kw;=FFK{v(~<;y?52Bi+xe`uJ?I<+C?jze{?82@D_P1ymZNF=ax>7e-!`i8-H-V zAyL28L4?lO=1;TciDQ=Kp(w4)gHMMQv_Lw*&$Wc=qa1|n(=acKAJUKQ#*^5CBQK}o zF}eXx6KL@&Y@~^ch_Lr=n{g`3Jfw};uOL8k_@=(VlIbgzT?W%w*EoJDx@$q6D@+LO zP3V63{e$oN`=`&k{@*MRyd7q62~E45vm28qy3TBUSc=OGBUg2^TCmk%)_oh+Mr&Pq z+=L?Ck@S(1SWl+&(09a6w69>C65EAlS|R}S1vnFsHh-6dkul{%TjU}%Em`d|`L7`R z%`g81uF#~|(~vmQIO+n-`5v+DsLA-Xe5SLBdAC<(cX>}zM)744em|BP5oVGZF*DIH z8$lLg&v@c}H(c)+Gr)d50k2Ire>&e(ci(|IELYo8XG? zPFbQWV^i8ypj-FQHM9%6Kk^cLGGvVSP|*qFj^|e!9?APqtDciYq)#M{Kb|}A+wRJt zR)Kz44|=vMKNLW)CTI+g!oEhs2qBU4rcm#6FOM{b_CWLw$0HjD zqFY3dtcSQJb|-;9mOoy7?BWpaj%>BJ!1!U6;Wq*elKaTG%rO&fA-j|yM zm)bNp^1whJM^%BB)qh6%Uhu;Ohc*vLOAg)tNLPw|Hp&F9El>x%t5)(dp$TwEv`aQG zsU|3IbgpHx18Ejzr)ziF3XkRRxcbEEzse>(e1%39xVvBqC4l`oY|PG~~W&0cU~ z2jpKDk4TFQc@13KcXaBt&mDk#_BP~jhA}GJPl=BVK$-)4k+1^pb%cLn+{Q2L9{wE)buV3WLhhh!sKy~_)_0OFK^=L9-NQB6=JCM3 zS@sp`?EoIwCPq9FR~Dw_2~3hxS_prYc6##AdWoSZ^-k)jc)LPWkErO|)ag8FZQaY} zJ7LB|k%lPw;M4lhs|&~;m4qH6R>vg){zs~pU(^uhuW=>{6xDAnp+bQSrx&K8SDufK z7%g!YbXn#?W|BCo354qcBM(*5HDn;Me9}uuk&8*XGnB-AC7FChR8&~_J->44v)}ZM zsa#hR^A`OZ{oOm5?*qdY5d$)MEN@Y^Y;8(8A{ymTD39NbClG+!l}~fv~~B3I=k9)uxlDApMA3GDyDnh#V#dho3YjQ5J>>Qlc^j+zHd{ zVGnAPOPZFMfJv$preeyFY_(Wa?xZAlNlHrkh1?THIcP#nM@5y-_Z_!sl*7Mr$}Z&6 zIdjGQb~2szk3Bqxkuw^iTei;FDSgYVArt+@B^$kT!v;VNO6pv)@`?_qU8taLyY|qM zT*CXzB)51w*xX?G9FySmIUF0k_+%?1u0MJmIWsNU3A)Nb{1i|knnUEq7$;?vrI6*y z8jerHqdvmVS z1R7wUf&kdYF`+P^{(f<;Y8KKP`I~p@etru{$t9UNG#*(TOV?&7{#o79BKt1*^qIvJ zwVdlIWLS~j6|T2S^(>>swUtvzNrTm8F-=lcb%M@K0WfgPq^tT)wZ5CrNO^`#7%90| zUcv$l#G^_xTVc;=d&z>mKdQPu&?5thDd3EeqyUWGpM||W1?*MZGPR<#3mhv{uNK`o zVjv#4%G%Txa_tnBio`(X{geHeQH7;TuT%L~9L~VE;)umdw{CzndWb)!>I0xlgA&F9 z6X$fe9`EnMcuIw`$*BbBTA1J@NQ8dgzB#Gd!v0BVp(JT3`aZz6jr}t8HyY=}g!CP6 zH~>c-jKTb|2`24WQRvcMjnVsMR{8$S*je#!sj*AS2 zG|FZ3i6_WF`slPKA}G@sm}S)ZN8%stJ~PtXl1Eu82+_? zq?#e!@aXz?|FodVP3OB{?GbC_s}7 zzu{cShQ>`>&`0VYIRZl61=HMS%!tjpdDT;XoU{rR=9WwGZKdJJWLFQ`zqddk`gG2Q zYl6mYqB$h?+cIdbZJnQj)#)jiF=K;;8I*tJ zb-7NlSQ?EPk|T#4_;MsibKvjr320?f@)BiEJK2-OcPlJ>+pHGrwbf#6U%=8UwpmQt z>f^a>`p^aW+66VVx$ND=nO)QZPs5hkzb3Nd$u={)l`@bCaaA{&R9DOURKWYcGmo&> zAIjqR5QWb3k4f@j>Vw%n;o0GKDjerOtr8h(Wv*T0I}*yoInbu%>J(0}5Mg@6=&nd) zvsCzA&g>{KI-FT{_~xo_3L0>})S11R>+6ZBDB66)$SP={g&u8^Q<-QsGgtI>7)K}6 zkz^S zRHFq0;l5!N#=x>`iyasnmijX_PtanWV&l;7AQe+jw6O*?o1b zVm+tp^x}}>N!qqn&m+=XtCl&(?bmy!89LRtmSB4M@MzqK`-S-dx|4m;HCjG<(#fg6 zFv}DAx&>F^zP(oAP z@fx_zy9X_y+6ToO>e{Ux=G2_JTa9(gW~1|$1T7`hGa;Cn{o^H(H17#| z?yuH`s7Q;@!Kl#J(dpDFnooVyQfJceXN#6TZbEK8P=Y|VdeoARYK~qE!?yUb@vQ#6 z8wx*ug9C&(uT}qqgudNo9HKmLg0+$!fZdC9&(dEl{J8LDbRCNb2Xb^2_2N$yr(ay! zWU5$8a}?Ieru0;`TBr;dbjP+`@s}5i>#pOcuL{?+sBoGiF9R5HioKlyk6rWnhj=TOiG?9Up<8Oa#eDj zeraJzeNHi`3>P1DAI)6Rz{t7t_@w5t9-!Rs>tU|WVYE3+ADg4x7BQhWXRKWQg~kAH zm7$(=^yEIA{B+fze;_-L9{p>T^LB>{aWNnQi`%=z%ZS{rf)<_?)m-w}M$H-rZ)Bl_ z-o5lGpeT59%$VICzH-BD@5B0T!|o@Gj*)$u#q+H-3cKWsq|;z(`#eTTd4{^V6_Ai1 zyM4h0QjtE4u_RBDw$FoUrd>W^dZ)Lv?fn`V$$_%4#Z`OdeFmyA)I3tzU%=kCZZKy_ zi&Z34y~K5R!&utLm7s*$H0sectRRfz`t+&qhrf!BM}6Di%y4NRT@Qk8m+pY#5Ezhz zF-wgn(BmO_2^-KM<=9{%cd!O72}qs{BafY+uEFC6SQ}?HkvcI&VcnH=M%qb5=*;Hl zOB(x#Qh#t2kXHOTfqs*qPf8Sne-M-UTyVlcgR^Ge@Q*o=@3YXrw{BUhWUy+2W|cff zHiZ^-s;5%yXQM9m>)Os7wM12FDw8X!mLy|UP~~`TKm~ySmC+e`*(Y$j0etrP61|e8 zt+no&*1_$?9za?%`MgVgQuMTPrrnO!=b6X0s}^}KsD6}vk-|;c@Cyxm+Gq2+|8BJB z%;}K3)Wx`Onw7%UbbtE&4~_zaD^2B0X*B8bGgwTru%4?;C~v{=p9ajTHQZA&3E`K9 zUG*neUGXg0(MmBoIzn{97yxX0My$1PdA7dHd}u^ov{~g3+acwv@{0I&_{V1#+Sg(l z^HGocM+`dB_?NlKm|ST=x)#JaMa&oFE)r;vk^+hqx`|S%Y0>zv5$PhPG^IR+<D3_F z2BC~i>nN+Gi;F~y9?d#1%_0V{xecZonU)G(-3=i_a2N1*wLrj~iir3XY0?KHfNDC&V`TB5qbV0kyv<)Ay!K9p!sBJIXvt*XlaFn-1 zsa!6N(%mYHtaPrzb#o8$=wrUcAV$RabBM+EJlSwEO%CS*s{mluj7e{mEqTIr* z2RwGMTRIH@voRAaif(U|)2-j8Vx``#9^9m`Bm$AnfqMio$K=1VH~Z^pHuB&M+C+>B zrS02c&$d9lnaN8w!mC5ChK)K|XPmE3r=Z0vL)gF`oV>inPI|XDHq%saK-b=HwwBUO zAJ3?RF1Bzc8-L>AJ9XZymk#b6=pOCAv>-?Zj%*455lnAE$x?0pV>A0PikqsCPxMW{NcxmE_m_Okz^$6wn; zu9!j$js4cQ-nA$WCmw^=kX85+jZXFQXX6df9rg2>jr|3Y;d-kdjXY*-plMgn7Hcw# z!Qu%pifJIxSmf$bs*#x`*QmU4EJ1WbPz8r&sJ^t4WsytCm1)+Jt^K$EM9Qx9pojun z->q{?JF$C;Ji`I*cw$I{SBnb*xxk+~l>&|mOO2>Ov$g@of-`2^Omw=D-(Jtt^!MsU z1Z}!tEt`49z0_Cl&;#MIt+IjbG8?y*MEVZkr}LAL7m)h!sL8TCQ*tJgj0STYH-F0C$ND5ghKgYYn5LuBjc>8pPpbQrl(8NX)%4x*83D zV)aWNC6CclYxNSB`g5p^utcKGT@BT;Y>So9R>x*;CtS8I=fx9uE|p-eGagA3wvO1I z)BJd)<0}1r%CU9O?yhaOk%<2!c4e0he}VI0j}V!_y-Q+Egg4)sle$+}BRxNHl$hQK z-o@vm0-ebKBGn;@6gN@i3Buw~lUxpvWmQ^M`BPa|TW9#m=`hak8ktri?@cW`6g9Q` zV(7#15!o#&d2r8Uo$f?8>gAqdZFuOwrOpzOHLVO0onpLpwyBY z-n%BwH+iXa^BPQ<<|S={TNpok4xaY)zU%YXd^4R*2m%H6OpvszQ|JQd*(Hu(Mv={> zKRf_7yykb=s_(b2fFs_)CHx5bzyt%(A^mEw1 z+%Oi$lP?lm7A05v0iC()tlFAPw9Eg+DM!>Tu*}P3ESHs3o8y!yf0vS#{Yx{minw~- zrV}`K;;rINBROPW{k#D@;qH8Pfy_%yU6`sLVomzFY%p2h?IGIsT1yfNtZYhA8+S6B zK5d*K+MY9f^=nL2)_} z81-^0iI)x0cuZt(CU=-ieQpTir%`{)w>i0ZRC|BWwZ(sVI357ttoPVo08s%tn*LW& z0{e;-uvCBbY!@q*Jc*U_R5@r&*Tbb%UVv$4{Q0hf>fuCq(WfpRV?!HB{(*-2Ie7P@ z1)0+vE@E`Y{(+;h9vt9DHXRU5&EzmOicYgw-(pDJVP4d#s?2k_!rr=)M(92+#}DA-)BQ<7t=>lklog;8a)c>z z7FyTbcW$fTnZ$4lJ>HlpH_;{6bg%-Wc)FB}lG}!rU~xB&m8eFkTqpQlB)3xeH6Pg0 z0b?0Gs=8@PW5%%)AgDo&jvhS(R%3u-=nt`WM*^{LKIOhbAOkp`33}pTGGc^HO@E(4 zWojv&TjT~b70YbZQahWJSobb9ws^|g&9pm22~`F5^0Z?jX8~3Qu* zB%ZtRqLX?`mfMIoo=Ckbe0U$pxJ9U}eqH*4dxr}aL8|ys9CJdW5h8~wqc?^!>E?^+ ztJ&n&S@*T?YWd*||AR-=-5vX^x_!CX`NTB+UY!GM>Z+nvQChmfkBvrMf;`EzIdOQ}ZFa3gw z#yl4w|3R3!`N!?Hs!FP3V)L-fZEf`_2UhYBy6A(Z!v1XM@M8T?xy)_8VX{`^LuFe{Uvagy(Zu1`{Za3*EZ=a7q+!RX-wm_2CmDv-X@t8Dbx3FX zy<5P>aIzm9OR!p7YyDeqoyTdMqM^3ru(}Gc#6-5IBEl15{H11%!SgT~ta{?I{%G)s?p1;;-reB(qN|-S@xeZD zk&?7KRW4gXnMKAyKDgNCyFd`t=;UIkS?XK7HCwF?#Yvk?i-tz_W1?S-L5zA zC1|WD5#vnU+F{nZcCmGwU+Jd{+1fccZYW2#%o%f7HDG6hYhisu>yb}ldE1PJ-XE!Y zkWXz{LuBf0cSE!fYAugqcxA?TxRYM&i!c@InzP_60@|maO(HL>nHw4H0sS|exy$ko zW}jC-r?#9N!mBqja&RU8Pr3|%lyoQuX^%pNAB}}wHpun*N`|+t;(l-XL|V40Ik<@y zUd_CPqg^Npi|t{f32!Fvu=~1%TiQnEIm#3B0yE>BNkyya>B;NIJB!#wQ1|P|Oy%#g z_+mU(1STWP00DIUTA%tN+6ARVFM&6do6;>AWR4(<53-^_S1%AKnE@~$8I6N&FXv+y z3~W2@7kq%s?WO(=j;7&;Dvvmi!&6a-3!1*P`O=fc#i94&(VSYUaPzXEz9g!-{?>M( zfv~=}TW8_2;pp{_FIkJ&Fb`dVn@tQW%M$0h*r&#m_8IjgeIYTTZKuVfH1s=Zc+W1I zyCngCK6|e5*7oCem)NdA3`+T9X6Jxs{O}?F~a6f?Y)J{Ht%w&8@qFsKNi?Mb5Xlv zlHhyn-1%kHGv`*<2io_`@Nt~ldt`B;j&&7F+Xwt^zpSn)UpzdZz;iK(OTUircO%;` z2zu^6toPB<4`(oW%4*CCfVwl27N@RwHDt72pnyY~Lt7fJePBevpwFp*$$`oGfvqK% zf3!WlFL8-M{Me_t#gs*dj_5%(}lLhOuX1-hLl|7UW^0|2pc{ctPPA(zXiSX9e zmRse+&|Q(f6NfGU6)arelDLaajNbFPa#{ALcXl_`HJOkp&#tY{obC1TX2l&xwLP1& zGrNH|EFhmQm@j)j$X#C$%ifcyQrJYYrb+5LU#eq8LH>P0gr-4wwIGLRU`sC4 zedDwxO~0PTDOX_d7d1=BTzT@v!|?+6-}6Q^6yqFo=s{4Ql^SpdSRKRy**S@RcNEIV z6Px<|8P-&kskP?jf4Z6Ndqa2VS@cVa%XdRDypb%68g0ZP-%_?LUdzotl`}k_P1``orBycasSxg+3AHPG6Io!g1>8H9W9B)B;!B67Z#5_73B96YCf@CnM9=$5RuH zJ+pe5fA%-M6YQanr$nq=S;5(xxTUQA!e85|xACtm<=|Ca%Vcez?VP3Xl&EoY52v56 z>v(1Xj%)jL7Vs~&Jz4P7dOx5)!`rDa4b}ZuY-1K3P>-ga$hGN|%QcdOhsH^h-<*=F zgvPkGGA|pIERE$l`cP*!});t`T5OK=f>z{!Lo3G5fz%f4dGD{EgKh=DG}27;9TF850V^} zvJ}2@1yNjdL4=|bL7x}px8P@hM~-d`4dT*Yq^zkk7DX{)A+rRjXIL1XPMv)QUNAdX z;MSL2Dzrx-0)k2>ANRRoNXEjO2t*BPr6HL>(it7DQtLG*3+YD#5))Wea?cQU%%2qa z9A^c7E9)i|2r4eEDOSO<&td+mE&kK^GM4(0rDNKRviE3ZTD_E|0~Rk`wwQ&$&@O%B zYRN5w>+K#PuJE4HLPRAkTf_=^JqEt2>TXx^|~~wJl9kQuVg-BE2=hL%<@R?(tsadoy@DRvXszTHD+=<0M4Ri&gF= z;I~6?^MW`g>V3XR)M5K!`vYM!v>(Ka!m|8sr}y6v^KiQ%$00}4m2aeNMcWT4cdXrp z9n@0t#qV;=Zn$WRuOqctswRl0nqS_HpzX<~4nl#?V6)es0KXFT-55yUc3=R&!rXFJ4^4*4+Vx)&uSLDos*MSD@52&MUu@M}LPiErION)mHi zA~|Bz3^Ye@)p~AGoq$Ojp}``Fsp&9|2$@Smq?EjBBYr27IA5xL=tA=z>V2xk(JzI; zrP2N`ok7+`;P<*vYek7KU|-@G5yElhUN8^nRswk-^g{sUZU7IMPHc%E%-wGX!E9i< z(f?=lzZXyLLxq73#iuAhS1F|OG4>U~k9L{;?-Z6HFkGV z*-?RkM0hXgWO#dsT|QS`u}STXyx@d#jkzy30Va`*qMP5Bg_`T8q|SSytTc|r)m zSpn};u({WO%~ysO`aZs zdeEQ6K@sW8t(!Gr^UIm&CZ7BNl?YybdzVH0k{m=yDuG=<^m@F^z-um}l1lI)9;pUc z9hgt}!!)>Ya1pIh^ArHFA`{2q?1qhMWT>?`sdNp7AQ`SS{Pgz>mpmTL}2M918BRDwL=nSRL3tO?M;nUFCrI5QzSq2;^W z4c~e&Q*yMUz?UU8-az(-A3hS=esb9JK=GyH!AlEU5_Nvm;pv0BOOx@X%9(L}@os2n z*gg{)(ZiTb$(b-Cd$n-2nns_HH<^FU)IdEznRDdx`1O-;z{;L5RU9A25kl?WhMR_I z$Hg@=b%@-Hasvp9X!8z^xDB)5+uPe8(_ur+x_6j!$(*>8bjSKj1=R{7z6Q#lHF=WP zamz`BlFJ)6?9=xbCuI+26cn$|x#O_jn~V=-!i$5($>O5V78-Lt@Kh}NKJmSN(}izf(GEkgAa3{xt8UZo!(_dfW3CUw;i?w?$ zo{Y$n`2-RoyD2Dl&;z{ckQ_O$)^1L|BOnl2oErz?Iqx^Sqi;9~2gt?$Ly$42xQc*^ z{|6pp`42osLB_!OKjau269*g1|4)uVcxx}U^`;<*f)WCd85ATKGHRdo&SFt1-~$!Y zg|s09lZEhUF9h{VG;M#V>ZkYVPm&S%0-p7q0v~?-_swt4o!4o%nb+w=CP-n(xqe}h z{zuk7xVYA6-NK_2hC&?HK2sm-11VtBWQRq@fwk&1TJidg7nLbHB!O!XP5oQgT?rl; zRzjKT&(E$;AEqAxrUvq0Pf~A!xxT#)hANvKA;53vz-OjFl-E@1TotlzK^<5!T!|}; zwpdc8ezGvlWK>FxIc*WfJ@Pa2JufKD+Q1XbalL5U*zIvo!5b3V13dH#3;DkG;K@Kr z!8F6v>6eq1(}}+#LRj6UGAuJB~{4->~-eg-0$tDATtg@M5V$!wp4@Q1#+Ui!?QW1yVu=f{iMSQ$Ae18;O#@elqhk;)GhfN+pr z0xZ1x(xYZPmI85{I8}2H*@J!l#Xc@n1G(Uu?hz258m&w%hx5w|f;v*JG%) zy-1$QV6zL&tn=0VUhm$nX01@K(y8P54eLUqxw+kJu@~@KMLQ)M1qXQi)5LoJvE*Dq zb)u4(l9W6-Ju@*i_TV|cWhqyoSR$)L{U5N42+fg3*y(%=&XDgMPFz&aO<9?4s)Ji_ zmQxfl`i{UVwHoDUAOMSBcL5>Yr^ND2ae=>V%;Hr_DJ^4QrQ`KqO=xv_M zEi@(?>I_Us>kyU63NYHLqwu$;l>fv!LQV?>y zl!PerR)%=$WLspc0rLg5mH4zPWRoP(b7&baQ_EdZ|k7P)&g? z8Tvgjr#zfKQ#q-wVNUtT)R=Af4_kQkn$spQBJhV?=^oz@13Cz(#J+EPqNw^U=N{ z|5<>HODS)tN4CqF(O}LsjOZEztp}B&5%g?)p2(QVW9^1$NI4(Q_tv(36#SptHqKq2 zy%mrig=uk4dq8VNM^1f%+p4a5i0=iY*yo?v8@J(iEqB9a_`eHX8361=d_X<<`hi-@(9#~?7VB}@CX_pw-5r{GOExVW-oN-o<=qBkeDc-K^j5 zav9cdHRl;=Lyk;SBaqR5lQY@SPWFN4&@xsEbt{g_n`OvR+sXazuxYDL{5O<$N~AxE`|qwzw=t`Ylb9*r=}(}wVHJR_*Y(8i)a72Q+pyKy#i-QUeefE8 zTL=HD#_tc~rQH>#D?OCjdf+fU{8e3+7$B1z z%Lpj+N)$~)(?3MjI}Evyd@hBQ{(!PY8a1K77Wy~Y+6*hz*m$VeKTnNLJIEkZ`u7vM zPX9RNU9~bDEJaBXmLw*iI+GAWH8Q;}%5Q2Yz&#NYEAHJk1XMcWAYC|D(o{Hs$%0cg z!w7Oa1Q#LmT+qPdCO6#AXAcUCm8F-a@d+@16L4RceVuo2?^X)nz{&4`U>wj1(kB~l zJ&C8d$cCX!F3pE_vzJvltIoT#qN)*V3mh+ExW{A$`76u5wO5PHW~9L=Xk_eA<+w<~ z&&4>0A z@Cque9fE=ShjyY^j;iQ^hY_fst_C=s;}yS*3l>^+Cv0J_{&l@9mNZLr{abyQ+RrVw zG_4uT_gP4}>>Qyq?MbnIwF{+LZr!3U{&#qRmAo@~rk#d96tbIUeto1}z<6sEF-6jR zc17=XEor3O=E?R^Iw!X9_k?xs`jx;4yftx40*_y8WRHmMhF{f?ox z^OkKM?KFnE2++KtqW{X){FJ{#&X&AYS0S=z;Vwd6?+jPH)%)5A?_BBO1_(uGi1aYd z<**NXp{7{SfrU#co)tthKN?xFy@@Ldvz3we+!7FyOaoLc*7gk=Y4eCGNzzyMBkGfx zCc2)sm zBVscMg4~Zx^}#dXHPW5b138UZvj(z8@(I-wLCD9Zb}<>vH{Rc2>sAX3JVc820<=?*cU~4#V~!unUE0uc+#4X5~8a{lo7uuLrnrv zCuksJDTt-@P&9E zq4dsc@EWmasQz2%1N@e5A6Mm7esF`r8|Id5A8zo1aYCuL2B{}pGZ{!v_yYnO5ax~= zRP%$TxntfstGr?!>-BFk4n|OT13CV_<=N*Plvlhb55+II(HvwB#V3p&oUEm1O4*^H-Cv4X-*v zuXD#+kRT8di0}k9V;-*tpr7+^@CLU+I}qK7t^_utTj3n7Cl|q5!5p0@7vWkd&CY=~ zF_<5L?LyPueytE!3+qI*f;+Mr@$Z+X0?JSvNY^`oZS|tr=!JE{i|PBI(TRfaazHq;86g<4@6!yXF``_KP11P}$gy;R)Zy+h zgxX>X*bF?|4xMWwW*}uCVk2QAA`N1$COy7%{|Ws{Ed#AZn?q;LhB%GFJlp&i2Ed#F zav6zi)I*2nHWuS2i0o&j(7|QShSNZ0&PLF{WY+tofyCTPI7t(OiQ|VzguzVlYXVW) z7@XrcOd=E5k z)Q%%yDWeWgAvhU*b{b)}7ZHvm%82`DK$uTZTo64eerTxegv7Z@g3m^Y9Ty}!E@EOp z-1!6E;qDDXTb7>nlijXDl6TMe*A|#j^#?C0BaT5*TWKMh+2leQ4!U9iZbW|$^Mxgn zBqW?r6cyDZrSZDzFHxBeEP@!zm0$nGY$6sEL$Y%Iu7?B{{6Oi2n#G354t${;>=EPv zs+Z5*ABp{<9{dh=$Jy79Z_fk-zbGoT+z%Auoj@S&guLJlGRA&G?c?WNBE)|44(baY8vcfx_gXM%e~zZ-ia%V%kL~(Eelid*%w|m7^X^C;0vn;NkLcyl-#zOl^ zB`?I&hgFO|z2YT&CML&4Z<@cZKdyfkgexeXDLs+TB_F`l;N8E^&tjH?gJJa3G?tTL zbP@Tb|c+=kxf)Xr98#o*8T2_JPW?S5KtSzc^6u{QovRbl@>F)v*dE8AaXO>@| z9WCWeaN58bz!?ZO&^DJFh#ClRI_k}Avj79z+?q=AS+893S>0vSQyLhE<-{uJO1@gE z+IkxggCYMTe+5m3TG)q_nF8QB!M{lZ18p zP0W<hRCo?!v{$+RwivR6B=Q7!| z$*YlT*w~0QF^ZRwL(%^34r_~bfSq9ooCD^-JO%I!re?xz8oqryBQqMNQj2jVzj#*_ z%!dtCDQa$nQ#8<@YYOFjJsl~IbyGmm=p*46rcMa;w?rutf3|Ck>N}hj5t|hUuUcyr zJdyTcxV`^-N>{LU2`&EgCFB)c096qZ8I4Spl>KQZ-`Hi-pXFBn56+i2^7uXxH@~GKk-$3=mYU3J#TW*UFJ-BWBIy{@njV14}!A@@*-$8*ljQKbjcL z*emjt_=~_z+e0~u9@lg_TJ9nMOdIMCZt3QCtjpka#Vwm+wJippFk&djE1d1O_C!*c zO%_7%$>NdFDY{MCL)t^!W9}^jwIIPq1R$gjcz*A_SUkxHI^lA!&U$zOz~9*RH<V*CD$lfgxS%aV5geh>`PUITs=ODFF{1;eN_Lc9G&&x$RfOnOXn0fyEpjFDg zWWK=bUOg_B#H$ zPme(eK0ajZd5qY^h3Oj+a445F0xBtVa(@&PKN6^UL*gNY@r@r(9B?Tqfg7H}he_|D zBEc`49i)58dB?*kJ`p2MMuI-_)X;n5($dbBhBKlL~yXtx?=FW8=5$Y~7-&1zX{dC{4!|Uy z5*dF$Oum^U6#;ik!0XW)^pqmi>5#43*qm-$kz9jU`h3H#sqs8`Ea)we`MNN!iL{jO zW-6wqpP8W#q)sMN1Zw=uwX2fzhj#o7=tnGYJ`1#1tW_!#*_WFzmX?;53Xh05u5U0u zs=H!=6S=e=s@!R1$ojL+B9@f8HzXHP zX{DG=IL6A&ypVI9NJUbqK^AAKx>&SSRV{%DNKK$Bg5I`~n+p|HN|Ko-$Bt*szXh*5 z9uzm4+OZOi>(QM;o!07T74hy88V)CkA7G0xm-|b-Hi8`wfYnM8vthU(Rr;%UR%ONM zQ8yWH6i4;@r(2h>MQe(wO(=gpo8~X~_I?;jJn`-$G?Q4@Cq+jHZw9E(0~I>to3)dU z*dM)Y!W0S_F+TOY*}IezCa^XcCAxw`CuizARfn_8rRbnf#Ml00GyybJF#9MI<(H~ z6@!zP)rfI~FH&old^sSYhG753&<`lCXzaGHZ^^6}W#NrG?Wq`@gsV3y6C%b(m@!>W zH}Iiit-r-SPkWDKsY=fJ_&c7976C8G9IyG4Wx8KTRdf{aYqg=BB zC`cw$YMsN=K72=pJ&Vxh1MibR&wq_5HP{qPhWL88x+W(cco<;>-KtK?`OtUtUR2SP z;+ayxOBJc1e!dmf&q(zcQfjboQMTWGFlq?B1{e=VC!T1JH`!l|mno}kFFq4lNJ`1_ z;s9wUEv^d3jdufE{jewLye~jre2wgYvr-nr8I-h0_#dwpXfmvw@1nGl91&;Jeb{Ly zQd2EanGmh&3Cq(8El){L6$@*FsqjJN*Je0lkW~I6bsAlTX1#{Va+5E2?U{d-Gt;qs zx08wRZaJ#d)eqA;RIbjw`E;4Q?N12!d=rbAo8(}{66MOdl)}AcDwf*Xp8o<|3{2p) zer>8YbPbK|t^|jn?5i8-sm5jmda!0tP$iZN{Sh=2%x2w*zm$}GF4U%#5|xY*&5Exk zcm*3%OJb^-RnGfk_Qdi2___Z@>D!wH{J$7`3!u1yu5B;`3mzc226uOj;2PXrgS*4U z-ED9ZG&lrzAKW3hyAJO1!~1^!?!Q%AwOch)&pj>op6)tNpQ*Xs=Umf&-U~n5RhF8| zdv>iWfbgcIHSPDPEpHVVC5Nj-F7;@Y$z=xO8OT4UDd7_VrU|mlM#P5V>Wvd7vMwEM z7d1Vgzf6?mD$I?#Jp#Ggf{mlk#)ZT0#X}`v&}{k&S22e2GbG3o?3#sJ%@v=m@GB}! zdF4kVvlAesX(KNnZ{qCRgw0gCWheiI1eOkF9-{~(mwacPt z`O2j!^|z&1D*KvRy@P3@&8o`oPDh)y^~)|Jb-|A+IE=PUukWO^EF!wPpYrK<^VNdK z7OK0|>I`)?d($$_Air8IGk7|6&@S>~hbeCm*hTd~cX}|`faVVCUb(1^i*a$@Nhx9T zvd*=STuiebx3^ng;LLj?8Pz+m{w~qbSfMz+OoCZ>1eGgI$eix8rg2ka#@`$AY@=}y z%U%RzxOCd8PJP&PE>GQH#dyp9}r**e!1Smy#j+)ei;1`q}+@5Y-TODyQOgA`BPwX3Jw-c)W*rH)_YkiFm zi_bh%W2ewD!D1aaH98XjOMR6bS$MLLcCl@eg6z!zH_`?CP}(03ZI7H=8vNL6j5OB; zl)N=i4wwVxL-`fqs7u)^>hluU*EIZ*BFihtsx`<=nzKQ8)su?u0g=t#kMJ0^^G1$l zKeu1kK(zc)rYURrDd~BFRPC_Iqrc66b37v3IQit&tX@a!{iQ|g$Gk9p{ds>DF z{TA^7=P0X3GSd*ke;|nj!(iT7Ayz87kg<8*yflF**HXv1-%73wLG<-}D2UrX^K(nP zvkj$&*4gmC(16fiqunM% z`eQ~`uy!p$!XlzzV>9#z`GnLdgCJiO{&Einz-?%79fmwF@EymsWIl#dw2HWx+2{u#f#GoU}j(4-&99PQIe67m~i?uY85)ziRJPGi7F4WJ&f)- zH5`k-r;5UL2W=Eu_gxu&J!3Ax7m^DS8dQ6xGI{dRJ7cd`a(V@6`p=f-$5WHp&m84| z$mSVQXVTHfg|_Vlf`2C6nj#!(l7%$`pLUp*;qw~q<&S?&AD1etKh@vfw0d266g7Az zv{%%AVFE=MTcj2j=N8g98PHHS8t+G^H}k=1O%a=0rp{LXQl70pnePR4~m|x}?6cpqCjwMC~LO_jPN8W1)s@ zXSdQpD)}GwYdqKQ`D*I??$-P`CQS^8-{X6%>hKc_K2`G;G6B7Epu$>!FV}oN`aYcg zMz7^}L6Jw0YWINhr0~ov4r7?reBz-cor|*QbvNGz6)}$3ZC{=r1GQP~?mq6A5(c>= z^60qBj*g<7Y9;;#Mbt2ByKa`6dJerSb~69AgPHs9(91|R>L-`%t&xR-7S0OlA!(-! zU>_-M%NX*Q`pa=s@Df3{tarks24OHnt;Hf7xjfM{Xk|vl`tUzi$`aN7CuJn@4xA*6h`5 zP%@1ybml|LqMmrDqz&a{52-11DzXZK3*y1GYjTd#M4=b6m*{afz6eTm5c5t35T)PbO~PGnE}VBThf?E&zxgIuzvc8Voa+GeT}8okHo zqjcuZ@PeE6J|-WTdgNRF8w34P+Nc*km44n`dJ&S> zNKu#yFL1LSoMG|t%W`4QGdsA9FgQU+FdMZiIQKwsJ-)T2uqf#>ke{_jG=2DtV0rp2 zOm0wT)SQB@dT8hLfS2>)#4Q2*3uCI{B<17`(8`kvlPI0EOz9q8CdIMG2yzk_tbyW* z>-yzadrkU4UM`nIuB#|jjv*BFC-1H&Ss_iTr6~U{cbLd0UGAG=E7@o*RL|XM9LodK zVRDYA3iJ}!-df|4`&?%gE%nx1Tu;)ELbJfB3Hvk@%WECrT@q)rY4Hw*G&`Hhz5Xryuu zo$p6&`do`2H5(EPP&)Sebec&e$=R>~dFYaoS&yuyIFjVH}?9aqJt1*P!>Y?PN{ zeltZsWii48Eh7$8A3745VpKM1Q4^5LwJ*B~Dhb=)p=X#u@oNNei&9W>i@-p;%#_8w z7(gV~a_H*YU9W=-2wb3;f4yhxFj2q;%^4();Mom0r{L)ur{I~vLDhd8$R8dCYGkVI zhGe8)Ke8yCoLOmFnsup`CYq`R$jdtH~}9ql$-(m%Qes-!DEj!uV13T+5YS_yX^7{#+g(@3 zZI++N5}KP~*ADMFx5l*PE42_=L+8SesgRUWrH--x93{s~ISqa3{MSuMM){kdbMvMa z#td01YBvN$9gqL>xAUBk5cz9*1Fme7$1oz9o83)3!qJVGKF^P|v`T>4*$s;tWh*~= z6j9~m2V+4BW_&+8^=o=C3&E;cY8CXECvgQ~y==;&JerX1U1ed(^#>iYC?f47S(H#D zOvDB4f;mNz z%0Yos7<%J{>EpdB{Rsd9bhCS7Lu3DY@_?>~0dJ2GH)lvmq+t#Zn}F`Y2AMm;hU;E~ zU6ZXFA7;Dx*hL2#vB(G3_(mzJ{_Q|&^e^QS36cWPfh$AHR4`>97x=aKdhG)!M~Hqc zwSWkgsXhd#;s#xhnp!qz53LkJYf!WrQi?p9l&<01N2}0hZfdWb@t3F}hIVL2Z?mt|R{SAubgDJ{roqecO|Fd5Tm|>?%@w zrZMa5fuDz1x!4T{!yjY~0c(HM1A)CPFf*BH=uhOm22iqT5!kN++=gIehlFdPPBBwe2 z`742eyQ49{f>iMeDwExqeR(H9=#ONAmO(){db1arj2-!ro8a{g4=m4*MWru`8X=w# zaa+rYb?_o}M^vB2nDf~o`Z%NV=%x>CY?Gu8mI0K3!)Lt>CVL1G#`Je43QG~Dy-4W3 z^c&+WMWIA%(Be$_`nmxIw_03!c6zCrhMR3%A< zaIpf$`yzo5+Ed>Zi!RniWM#+f+9rP0_qioZP^of}o0TpGCW*h(ZxF5GP$XJ?Wn>Wv z)ggm7uZNn0lvH_?y8bkut;!N|@|;-sgVX`*>ukt*e5~X*TE1S%Ano7dqQkq=+9K`3 zWch!96EZcms*w6&N)86xCbH5)6dPW(-ips#B0LFYr?smbWGQ>7eNA>hyzoDN-Xg5$ zsN%?gtdh?P&dxh#Lp~eHe`{!dIpdvlMOqauW0&aI_2jcWAV1>R%Ao2c_OYX7+KhA0 zo93Pdp>8gRk@lWIvFvYf3}o)V{%eSPwX_TYdYrR9pR71*&p5ys(?S^;^6!4I<$Ylg zClygdtP=WpEQ0`EK^{bh>uvkL}? zf8{@7MEAWg3azmBl7H@evOPPR`7##r%|ISnsl39mB z4PZ_%^Z=ZyzYcR9e!3|%0NDl|U^0MC>H2+io!rE}{EBt#?d@L7#C+*vDvD@E+Km$y z>e#G@4(K`maz_Le^2-CRv&!%FK;1+??+ey3b%A~9jm6R$&{WIAt@{YQIQx>|Pv{p)#=;Wk+VX!hO`2+l7B3UW`mgX>`G1%FA40QU_o@x%$X8*7 zfM*MgPWwJ~?Z;}da<{;H!mc3Kc)LgEe76;`x-o$Kz5BzQQ1|JSf5rh6xy+ ziD??T#^bM`q#?`t6~;q`-~k)xH;$p&B{R3CA~$Eig!v*!j^s1j5eel%MFyuji9pT~ z{-cxd-i3l+|92d*(A7IZ^cClKwB+Z%)Zo}Xaz$72-W7b za9h>2`%3=p>jS}*4cP+QbEu!tVodP^ASvoS=E9IP_&A5-uF!SI~!aFch^N8 zi4k(rV9?F?iz@zxRDN5fG3_JAysjlL$4h6`#uZLeZylbiGr|3Ag zX>_{cd~M28pc+LxZNxua**rbJ61~S>6&HDF_Gs8Uu@Xa54Y1i~L3OWBDty_|Aks1^%$!(0NRwc(Bw{LRKU|_)Ib=7EONs@E$9X znQueHwWINcsnW>ZiAO5VO2zv5=V~o5`Wwq?SZm>zf-?Jr{(CpU=n_DlDcQ+n^CitH z11;V>>_ZDQ`Mh&@f3Jc87>J)g<3=sOs2aDhkLbi}Fxu*=Ji|tM@*ot(;_;8w8)uIF z^`sJQ^3{GSt{8pDm%=>6ZoFA7a=|V`tMVigi_opVguHS)l|ea%aYP-wvSSTjLvCh1 z-xAH{`+o6Vr^ehLyU}%QD>ck?eq1^amr{d^T4FN-=zp)DS~-y&Q-Dj zaUPkOuy?opsOVm{2a(7xrPi4@@Je^IbuNolyEQ}>=4L1h^rmKw_q@IDCIcsecqMrp z@@tQkbGn`911~~%+JH2+s>1z`Dl$SW)sRS4nRYk`4wdk}T@-s|W6Z3?V|*6WkfZBRrIo!w-G)C5~B#Etq6{ z+pvP8Q?<7>1|2Knx-%Vwe}BM^iVtftNIO~;F5wNFtXx}7_yCG_5*IPbg(0PI%KFs! zcc}QvKeRYvLU_UQ(SHo&n8fPBYs7B?=rYLZAbQ)eyWxv2(IfjAUAO6B?*)e<1{SFP zK@dx;lgfmm9bp|!i}jz5LXD`(8ICkU*Y&|>4k-Vo&}s2=S_fT6%>X~<1%|xNCh}&y zIqeFgo8n}_5Fov>bd+<-SN?#DFugSL=7#ZdhUiehjel}LjIXc+e)s8g{4(^Ko*4eJ zwK_dV$;EN5jlB+Cj}@11|2Vef3He`}2Z7O=7R=!e@ zNgh-8-@SP$m8l)+UG1)0mgK7j6bObS7XY}vi{x{!n zV>)=5LVlg%M!`XQeIEbTFyCW0Oiu8ph&2y!iph80>vq_5{i7JwYZg}AD@S{5)`PiXa7DzJV%dbCedtG z-JgSVS5I?!FxjxxBJtbb?Xo~9|0wF48pNq$bZAn|^?hMyQ=wTkvha0@?Jw?!(->#= zRkH!okCksT<3zVb%5sl`*RNb?#M+&%1u?bCKg8hL8gD2dTw`gfRJJJ_2=X}+1(UN9 ze9t$Enz2TY56A_@t)OQj$BciD+uk24z1J-6&*4s**1A+~D_GGsDvaDg3`m|#W4uxg z{Q7y&>HQ3nHw7(C64<;b@0^3GOhXH{I$$`7>UHC)*>^7~Iux$KJ(23k1*Ocx0HND0bk-=;wP-o*mW=u&xcyUk>{HVeoEOL;o?Npba z!9Pk$EGrncnSMQ{e9DjDZk>CfV#e_BwJ1@%Vnxdsh61Gr;rzAT$_%qk}7YY+ag zo-jX7>=c=gEBng!1&TqShd!lQy}SVl&a6`}h{WA}x#8wob1PSFUq^YULx zov)PhDU5gQ^Y>1k{#$ROjh`-*X&U=$@0(R}r_?MWl;Q$ous ze0>!pQjgndU;e9q76HqsTZ0Exs?n7&311&m%A)0XH-6n#pL|4X!Y|YD>v90d*62P) z2SD8x)lL2?bBZs;PQjPaG(tJ@bJ-uE3QhU&ouXPWqr_`r#=NMyAoILWV{EMau z7>O7W?+^@Apu|yG*PaxA(y-3~3R&?__Caj=?NiZbB$zTZ;ksu%J1v{zqi_3dBc$6KKfBQp7Xj^4v+ymMwcj*tp> zorsP>$mQDS6ectO*w`;0!!kO*lwQBD4|6<7#6 zjB;|lwEuQJ%Z_I?=VzgvQssJZo+|?Z%Ev-vTalqbWrj6hZuH3?ehy8)3B3=hzA|wB ziY>#iRVUR^4dALkk^QUi9>53tZ*U1p&6q~`4V2zsCC#69#Lsh-=2v~JP1-qt-wUn# z8Z`scwZ{sTiRVC@Y|^4@=?bInN%;Nr5ZP@rpY3xwExY_QKbCI7d~_Xh!ZgYemnm70 z&R>vxN_p(Up#uL zD2{_&!;g~;CI(DNP;Nn@KUR|}G5yRX(SvaR<|WQsh?J6QC8ff5Um}~d{WT8sA5=*G zM3%<;g(H(rPA!RsUHSr;Qc<=qDsGU-L^-PVwfg;+MI2W9FVbHm>0g)FU4%)3pWqVt zm<5GNs->7=_f^!}hI2O1=}c^N5t7srvy)H~@a9Q1Jj8ssx)Boxhvn6&!w&mc!$|_*yp;>pwc9u~6L3Tz%YZzxWKd*;)>dr-R1UL~QIN*uR%C(oAgX zarT-p+7x z!Nf$-5!)0J1R7xRy6Em|ZZx=MM?->~M6dDsyy9yN0cm1vT_$+9TZ(a8%~KYEm@n-= z<6!%t0=e;I0^@r{ICzsJD0eaDw}f(1_(O3KaFgDd?FU){kU#>s23FriG&{$4t{>K- z-$$OkGdm9S>_nKwvGato5m+mLwt-rUey1&{PfD!BzY(uVtqpWU!7K^+ro%S$`tl>% z(L?2;t`)d;3GN0iqS#^iroV5H>L5aF(Cnyv2X^xH%pqs$+X^IrX?TKi{kRqlqYb_G z@BN8DhdEpWF#AIwyeCg0=jS-bpI?k**yxVG!fx{$T*QwrWQNrj@P;ziV7#vd!`34RScLYVA z1fLCv%330g&iZS2SMdCJ&wE9Ntuwc$>NHd4awk(7QULawsj8E#FWi8JtZX%7CN3gw zupz4WFq<(?IJ$Th5^b_VrLI7EgN7t+(N|?9O{Fvu38^pNdNAW~5G6j7J!^gcPQ-v9 zdemZG2g?}tQBR*?E8*MoseMY&4~Y*!c!Fe0G=p`1o!D(IW3!(ka;# zH>B<+(0c*&-rnAeiyivqY`bhpP6fr!w6NDiw-F1snBNra%*Di1eaIl|x6+My)1BE^ zU&3Ky2PKLKFnq<{e@nkB zLoAPMMP4+c%4QsIn4ZTW--T0C5+(cLrW<<5?GPt17#ohsQ9EgWFi`Yvbex}iw?h_c z3sGTkq=_div@|uZiPC6Nw5Y`}JBNGP_y}dihInr4EoYgkQa(m(;A4CsA&Wf?P$SDI zyGCaNglI8u@9ri}(cXpWG~a%UmQmJSS>RiwmH#a;GJz+HWwqJ|zMQblx-IRtSekr)R{y?1?=^!K&j1Ywat~94tDH-^6O3|7FS3bY+-;O|k!{4YKA3r3+pP zJnH=3_dbdAAj}SG^QbQvl%(#)Mi?PmoF)68fts$LRCc0v-$^Mnro&IerXwrRC$NUU zOz(Z9HWa9UfEKt=&n~^4wq3lpe}(E2Gzoh)H#Sbl%X6P=?unf=nKFfJ*=-?^ZtPqs zm6)kqo%i>?w=FQSo{aa3Ey0c$VBAL7%20I9$hiIj9jDDEV^qSCLU&}Ul)---P8HXtE}~BW7vsrP7Qk&B=S!!f*j&o6l+7IQ@aHK*{wr+i;f=++p8Iz*wA}ePD3o!>5 zQ>XWtS-6>>Kqu#s+KDkml8*fJ29(8oH zlsPF|Ei0{i=yFu}y5WjMZSEVntWvgjZob+Ib;Ly*4|lmCH6+G+H@o&k?lM^}{(RyC7AJbmo_ZcXjPo}<^+l51=D|XL&{&jy?wrGo_3t_j8F}v~`-ezLX z91!NP>2Zr#7!%;V+Ips%nhcnF*%SQg)S#mL;B)BDdf5JFG5*txyuI_iZwG}^noG6M z`cDXFK>Lc%F>jlx)_C(@N?{Vsc0hUd$MwWIbo;3HHw;0@@?YDV6&-=r-Gvyl+b6y` zLeX-1&diqQ2shjiqz7{oa$2~E(|uBwpRx2~A-kO9ds=j7_dSO`*Ji?uDXzS3S1GhH zo4AOGn|BjJD7pq=?U}0E>J(~|Hu5d4v=&Cv*|IiH2=Dhs>3_jb4el)M9094RM~+Zs zGjscmzWIZ`rbhGP({*!P++@s#)ZdiB?>-1>lln&u>>^3VgXGb2E(nd%*sEW>Nd)xa zJPx607Ju@_*He!LCG$u)h-Pw8;E0CVO_i-C#bB&*dnq?w1q6J*)@jV-ad0{u&rd^w z4O- z`fy#s8~1$f4GP=@kvy{uybK###>D8c;F83p58jN)x|voSbtpkrNP){{_V&YC`_bC< zT%Wm|-)cXFZ-+hIWwK)|HbbUI_Sl~ktuNLsAw3kz!^hkg-lNeGcSDFSc{{-s$eUOf zU01VIXiJ`*c9$cdL@=>}0yW#}ZRhdE$II`lb4-MiELaM1Fu@oJy z!43hcMzc!$oYzv-Gx`?JOVPmCQe_Nyq{Y|F3ue`)Af9k z>wn!O7URBDxMn<=LXf_9h3g4lHtHFm)sqP6Uf4e$x}=@o$^mXU#&Puy2KAA!whY9g zH}{EunmFA1ZX5$`xV7CQDn@=DKP!BT(HQc%ZzTJ1_OKOE9njXQ-r#U|!gAGFXk$Wx zO@ddw-Va7z2~V%U%`Us8*pP|#*c>L`C3i6FnjK@Hd(D%uNoNz2Eu76Y(j?@s>teYu zu*I6xUbpwr7YV4>I0-h+zP(?i~AIrr^sO=4-&@v(ihq4pT{! zP5fK&zYHff1Nk!@O@?p`co(llKrJnw8<+&k>zwyNPo@IR+THVn6rLA11k$TyXhEn< zjh+ds=TMZ+6lu=-ahx>-3vPDK4xqlm#?$pg_mfohA3)Rxhs`t6XVb}(_pDlfS|vHY zk5A#Sr~bV6^0-$*(JenD=*vez0iM0&?;x%1$$xO7cp$G*8oW-*y6=ERTYSEsY5PnijwpVC%Y<2}V<44Uu|CG+HW3-E=EI!J{Kyh!kg zco?%OlK^a3{$hp{#8cOXzNH zMM%L&_2kj!LHQ?mYtzEAAMLW!+WB6ATv9I#bT}d3iv2SAvV6Xto@2zk;^k#}82cLp zBx_*f&lYZxywv*aTKXgml2w$eK`_W_rILCm*^zVOpq{dPD3B#UqPuR-u&@6ul($Z4+w`ayyqn_t#2ge%IM71q z?7leWUKYMtxIu94c{O^-dOcwqqU*g4MBpS7$#6nah;4A(9$8q8cPGKi>y(fdUZ0MP zZA?zv^yDGE`8&-MPWcNG`5uDRri<;^C&zBoM2I>z$Bf5LH}b%yjS6e2b;3OmzZ=ZhOkc^9Mht35;IcR)*Z$`)cC zw`h!&;$yR1uYJ+R+~F0E`ee~n(@sGtKHi!VHNWhxQ&>11jQChB6`MGLRsv>)Uu>*szEWv$bUBy%pR*%YIgW=t^^5_!PYnRmK&tNCC8q+C(k?XTWbg#pu* zyq=nOsu-M4WlZx?GkS$I@aKw~_zUdsKa0^)AAc-FW%je~}- zr-DDYs@F>K6f_Me4Mn4FBI5y;yOX;@p82!NXXDal<(H+YHGFDU7N`0oS63uU$u+0g zq9-<1nw%UbH#^i>8a3{_xgr1Jmz{ZsmAm!XA%Eu~AdIuK8)$1Fm#U(v_64X`%Fb`Iw|rhn;>X@!?GGcy z=6&4qw*<@A3JYac8fu>Rux9H)98I3$eN3ieKHbX?r2kd8cXF`2yvC%xuZ?hV9An}+ zJ!#7Arl`nNkTePot`KOxy)(F|s~|EssM6Qn)Tk;ubIQgmV-~(L^ zBiYwu?*OCe_k;6Y_lJeiQSzs>b&n}k)>XIX%l}04$wm2b{^(*I-aEyyYG0T#Ig~DP7dW12>ZKQRG|5mh`{RrgZ1cQPEbl%zBayNk9!AYTfmdAwLzwZ9P>|R4 z<(8|WtALQB<>7S^MXQO!_`<}pOAYqz3d=TChQx!zV`s`|E$(*Dh9K9{)jM0J=CLQ6 z5l_9+a7kPeyhF~t#kJ+%1KzhWdQZOM{lzZX9;5VQp4}WX;Ihr@KU`=QOXqI3-{zZV zBY{)#srEVh9PLJ(d&5l8mQ0&ZaybNdCeeq`vv=b5jB76Cuv2C)l#N_j1*u#3cPxc9 z*MC34&teR`&grKesT^iv@Pzp-pj(H}3ov~kRQ|~GpqS+mQ~0Wt2yJ?)fb)Cp`D zyGuM6pLQ3_|pT3IDq))RKc2nlgI+O>tw6kCcxW=Flw zIUC*Sf9oQJYN>;@s>{#s#b;)f#2Ihycz0VX;++p=n}nPzI4qZQV)6wa7P<9404CI8 zGZm_0x{0l;46n(Z*L_Rs+rvDjTRl6+L$MM{_1p_=6ry>Zx21O2+2!PYEYF8+3gO#$ zT`%)_MP;Fc8Dog-ZG77IxG{nu%WS7|k5#fuP_}Kg24Bx>a)FQI^v8jw9)shIF{=5@RzNl4+!EEA|H6V4W=Wl zd_dN!u6y*9y*bkprvXJ}de`ml8GR0bFTS=SzLr2csg0qIiWEe@jxwoPjLle(ZAaI$ z)f@Xs_%P1|k>zh9VkYupHECSjX4!UWFVj%^J9cbSC3ipQQPl_HchsHp1g-))46;Yu z4j$f9wwf-tFt?N3od>T9ud4gF++0ji>+!mqoQj%k;k)b5S50*rsVK$ zvaf^SFk?MGE`g7EN5H+}Ksz6=r_Cbl3>Hl&2x2cgtqTRwhM^Da$QA4!gGA2I&FKJKYU2-8&SMF>EHN%ZiK^OyF#g_1 z&DdO)T9o+G)R9p!FWG+d%{+O7O#P%C9cV>=9m&ce0ez1c0pL4;w2!`$Lyp@#!*HAj zomj$SxWgsKPjHxzN{rR$o3o{qj|1ztS!!dLXwm>|uLmAwBOIz7bPBS}R^%sRJTF{D zyvfcI!j0E2X^A17UyI?MN2c;^F{L1^hE=WYePLZbJ8_`0)V@xeLVP(#x@X#2_m(7k zX2FbZ@m8uG3n!o}-*?ito)ghH@^U@jzQ$+Ly6U~XF_v1NaPxY&B$GIOf$ewQA|vS~ z+*n?kwBia|wsl5R0F%{i`qNyrj=bYFyVicbTHRo>e3jQF+jd@rwP@10+4_PBrc|0| zdxol!!xY~%Wu@nUlxo(zExV7&o~+IN?E%C0XO;CI{;zv{u^U-}sHR z>5*K|4gp&c|C*SXJr8~*HQm}-{ZL#qm^ZH8x4yCUem;}5T4%TlF$2l9pE7aHpV@Of zWVrCxIn+g15lC-~UQ<%pJlXB0a16CQMEBistSn2<0PIhTT`51gamyK<776a$?-k{2 z(fm?T&T()#Vr(*6r&nsVM=$5{MI(09RA`^hQvka5`}$^w?pb%?Yct!Mtmi%FpFz{< zYCG)olr1lMi=`O~_f}68!pjwn1iB`g6)#4o$-RY;=cmb~t*|2;j!*3->HFllL>7U;(366mt-kDJ>8k@k9tZy-CC&u zCNKmSK$XlX6B!8?r3Bx!(X~P?B9qKuF7Tet>sMGx!h9^eI~Cj>B23I;?ASXE)#)#I zzvFs8s@E`B}t>kXIts&ZFzu(Ew342_l((j9wTY(YI=&P|cM6HC}_zDWT9oH)=0NXJs z>AD5`_SAt~ijGGaw(iuRnL{qGb!D81^lDqR9)FHT@|~GTCVnA3-HPHm!;{3TOx+=W zRR&>87x{jk-ILbm=&>N1X1&bF8$jUWΠ2Wtb%NoPfgD9`tkr%mkVr5+5@-9^kB; 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�dATGLagwp=GYx<-h^}C9#Y5Y~i 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 diff --git a/doc/cheatsheet/Pandas_Cheat_Sheet_JA.pdf b/doc/cheatsheet/Pandas_Cheat_Sheet_JA.pdf index daa65a944e68acfc9ce5e4b47929d806ae6ba41b..05e4b87f6a21063011f0c161d0052b3eaf01ab35 100644 GIT binary patch literal 420841 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≦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

    -ObnU=`9k=RWhkUBiEQ!LYYEVK7MGO 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∋FV-&2drG_Y)Hd#xf*P$cwb4DD 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

    U~Du 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�ci(J|R{ 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

    1Dvm>^3fCy4Q$aJo|lBt|rbomhcma08#`% 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 bk2yFx0Y2al*9kC;i;ErY-n~ciVrc&d3i$UZ diff --git a/doc/cheatsheet/Pandas_Cheat_Sheet_JA.pptx b/doc/cheatsheet/Pandas_Cheat_Sheet_JA.pptx index 6270a71e20ee88f5020a76f139f404f18f4bfc30..cb0f058db544809b9de0bcef31776f0f66883053 100644 GIT binary patch literal 82359 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 R25lhZ04DI6Ccx^30RZuu|3d%( literal 76495 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

    Ql*HwboAFVY{v2e%FIi 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 Date: Sat, 9 Feb 2019 09:42:05 -0800 Subject: [PATCH 090/215] CLN: For loops, boolean conditions, misc. (#25206) --- pandas/core/arrays/categorical.py | 3 +-- pandas/core/arrays/datetimes.py | 2 +- pandas/core/arrays/integer.py | 2 +- pandas/core/arrays/period.py | 2 +- pandas/core/arrays/timedeltas.py | 2 +- pandas/core/computation/pytables.py | 6 +++--- pandas/core/dtypes/cast.py | 8 +++---- pandas/core/dtypes/concat.py | 8 +++---- pandas/core/dtypes/dtypes.py | 3 +-- pandas/core/frame.py | 4 ++-- pandas/core/generic.py | 31 ++++++++++++--------------- pandas/core/groupby/generic.py | 4 ++-- pandas/core/groupby/groupby.py | 6 +++--- pandas/core/groupby/grouper.py | 4 ++-- pandas/core/indexes/category.py | 4 ++-- pandas/core/indexing.py | 4 ++-- pandas/core/internals/construction.py | 14 ++++-------- pandas/core/resample.py | 4 ++-- pandas/core/reshape/pivot.py | 6 +++--- pandas/core/reshape/tile.py | 8 ------- pandas/core/strings.py | 2 +- pandas/core/tools/numeric.py | 2 +- pandas/core/window.py | 4 ++-- pandas/tests/arrays/test_integer.py | 4 ++-- 24 files changed, 57 insertions(+), 80 deletions(-) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 52fc7d259f39c..ab58f86e0a6bc 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -2167,8 +2167,7 @@ def _reverse_indexer(self): r, counts = libalgos.groupsort_indexer(self.codes.astype('int64'), categories.size) counts = counts.cumsum() - result = [r[counts[indexer]:counts[indexer + 1]] - for indexer in range(len(counts) - 1)] + result = (r[start:end] for start, end in zip(counts, counts[1:])) result = dict(zip(categories, result)) return result diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index f9aef2401a4d3..1b2a4da389dc4 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -128,7 +128,7 @@ def _dt_array_cmp(cls, op): Wrap comparison operations to convert datetime-like to datetime64 """ opname = '__{name}__'.format(name=op.__name__) - nat_result = True if opname == '__ne__' else False + nat_result = opname == '__ne__' def wrapper(self, other): if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)): diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index a6a4a49d3a939..fd90aec3b5e8c 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -561,7 +561,7 @@ def cmp_method(self, other): else: mask = self._mask | mask - result[mask] = True if op_name == 'ne' else False + result[mask] = op_name == 'ne' return result name = '__{name}__'.format(name=op.__name__) diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index e0c71b5609096..3ddceb8c2839d 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -46,7 +46,7 @@ def _period_array_cmp(cls, op): Wrap comparison operations to convert Period-like to PeriodDtype """ opname = '__{name}__'.format(name=op.__name__) - nat_result = True if opname == '__ne__' else False + nat_result = opname == '__ne__' def wrapper(self, other): op = getattr(self.asi8, opname) diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 4f0c96f7927da..06e2bf76fcf96 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -62,7 +62,7 @@ def _td_array_cmp(cls, op): Wrap comparison operations to convert timedelta-like to timedelta64 """ opname = '__{name}__'.format(name=op.__name__) - nat_result = True if opname == '__ne__' else False + nat_result = opname == '__ne__' def wrapper(self, other): if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)): diff --git a/pandas/core/computation/pytables.py b/pandas/core/computation/pytables.py index 678c1e678fe19..18f13e17c046e 100644 --- a/pandas/core/computation/pytables.py +++ b/pandas/core/computation/pytables.py @@ -252,7 +252,7 @@ def evaluate(self): .format(slf=self)) rhs = self.conform(self.rhs) - values = [TermValue(v, v, self.kind) for v in rhs] + values = [TermValue(v, v, self.kind).value for v in rhs] if self.is_in_table: @@ -263,7 +263,7 @@ def evaluate(self): self.filter = ( self.lhs, filter_op, - pd.Index([v.value for v in values])) + pd.Index(values)) return self return None @@ -275,7 +275,7 @@ def evaluate(self): self.filter = ( self.lhs, filter_op, - pd.Index([v.value for v in values])) + pd.Index(values)) else: raise TypeError("passing a filterable condition to a non-table " diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index ad62146dda268..f6561948df99a 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1111,11 +1111,9 @@ def find_common_type(types): # this is different from numpy, which casts bool with float/int as int has_bools = any(is_bool_dtype(t) for t in types) if has_bools: - has_ints = any(is_integer_dtype(t) for t in types) - has_floats = any(is_float_dtype(t) for t in types) - has_complex = any(is_complex_dtype(t) for t in types) - if has_ints or has_floats or has_complex: - return np.object + for t in types: + if is_integer_dtype(t) or is_float_dtype(t) or is_complex_dtype(t): + return np.object return np.find_common_type(types, []) diff --git a/pandas/core/dtypes/concat.py b/pandas/core/dtypes/concat.py index aada777decaa7..10e903acbe538 100644 --- a/pandas/core/dtypes/concat.py +++ b/pandas/core/dtypes/concat.py @@ -123,8 +123,6 @@ def is_nonempty(x): except Exception: return True - nonempty = [x for x in to_concat if is_nonempty(x)] - # If all arrays are empty, there's nothing to convert, just short-cut to # the concatenation, #3121. # @@ -148,11 +146,11 @@ def is_nonempty(x): elif 'sparse' in typs: return _concat_sparse(to_concat, axis=axis, typs=typs) - extensions = [is_extension_array_dtype(x) for x in to_concat] - if any(extensions) and axis == 1: + all_empty = all(not is_nonempty(x) for x in to_concat) + if any(is_extension_array_dtype(x) for x in to_concat) and axis == 1: to_concat = [np.atleast_2d(x.astype('object')) for x in to_concat] - if not nonempty: + if all_empty: # we have all empties, but may need to coerce the result dtype to # object if we have non-numeric type operands (numpy would otherwise # cast this to float) diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index f9eea8c63cfa9..8b9ac680493a1 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -414,8 +414,7 @@ def _hash_categories(categories, ordered=True): cat_array = hash_tuples(categories) else: if categories.dtype == 'O': - types = [type(x) for x in categories] - if not len(set(types)) == 1: + if len({type(x) for x in categories}) != 1: # TODO: hash_array doesn't handle mixed types. It casts # everything to a str first, which means we treat # {'1', '2'} the same as {'1', 2} diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 19da8ba5c547d..5ceb9db39f830 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -1535,8 +1535,8 @@ def from_records(cls, data, index=None, exclude=None, columns=None, result_index = Index([], name=index) else: try: - to_remove = [arr_columns.get_loc(field) for field in index] - index_data = [arrays[i] for i in to_remove] + index_data = [arrays[arr_columns.get_loc(field)] + for field in index] result_index = ensure_index_from_sequences(index_data, names=index) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index bb5c0e49e4dd1..ef629361c291a 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1564,14 +1564,14 @@ def _is_label_reference(self, key, axis=0): ------- is_label: bool """ - axis = self._get_axis_number(axis) - other_axes = [ax for ax in range(self._AXIS_LEN) if ax != axis] - if self.ndim > 2: raise NotImplementedError( "_is_label_reference is not implemented for {type}" .format(type=type(self))) + axis = self._get_axis_number(axis) + other_axes = (ax for ax in range(self._AXIS_LEN) if ax != axis) + return (key is not None and is_hashable(key) and any(key in self.axes[ax] for ax in other_axes)) @@ -1623,15 +1623,14 @@ def _check_label_or_level_ambiguity(self, key, axis=0): ------ ValueError: `key` is ambiguous """ - - axis = self._get_axis_number(axis) - other_axes = [ax for ax in range(self._AXIS_LEN) if ax != axis] - if self.ndim > 2: raise NotImplementedError( "_check_label_or_level_ambiguity is not implemented for {type}" .format(type=type(self))) + axis = self._get_axis_number(axis) + other_axes = (ax for ax in range(self._AXIS_LEN) if ax != axis) + if (key is not None and is_hashable(key) and key in self.axes[axis].names and @@ -1689,15 +1688,14 @@ def _get_label_or_level_values(self, key, axis=0): if `key` is ambiguous. This will become an ambiguity error in a future version """ - - axis = self._get_axis_number(axis) - other_axes = [ax for ax in range(self._AXIS_LEN) if ax != axis] - if self.ndim > 2: raise NotImplementedError( "_get_label_or_level_values is not implemented for {type}" .format(type=type(self))) + axis = self._get_axis_number(axis) + other_axes = [ax for ax in range(self._AXIS_LEN) if ax != axis] + if self._is_label_reference(key, axis=axis): self._check_label_or_level_ambiguity(key, axis=axis) values = self.xs(key, axis=other_axes[0])._values @@ -1753,14 +1751,13 @@ def _drop_labels_or_levels(self, keys, axis=0): ValueError if any `keys` match neither a label nor a level """ - - axis = self._get_axis_number(axis) - if self.ndim > 2: raise NotImplementedError( "_drop_labels_or_levels is not implemented for {type}" .format(type=type(self))) + axis = self._get_axis_number(axis) + # Validate keys keys = com.maybe_make_list(keys) invalid_keys = [k for k in keys if not @@ -8579,7 +8576,7 @@ def _where(self, cond, other=np.nan, inplace=False, axis=None, level=None, cond = self._constructor(cond, **self._construct_axes_dict()) # make sure we are boolean - fill_value = True if inplace else False + fill_value = bool(inplace) cond = cond.fillna(fill_value) msg = "Boolean array expected for the condition, not {dtype}" @@ -10243,8 +10240,8 @@ def last_valid_index(self): def _doc_parms(cls): """Return a tuple of the doc parms.""" - axis_descr = "{%s}" % ', '.join(["{0} ({1})".format(a, i) - for i, a in enumerate(cls._AXIS_ORDERS)]) + axis_descr = "{%s}" % ', '.join("{0} ({1})".format(a, i) + for i, a in enumerate(cls._AXIS_ORDERS)) name = (cls._constructor_sliced.__name__ if cls._AXIS_LEN > 1 else 'scalar') name2 = cls.__name__ diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index c8ea9ce689871..27e13e86a6e9e 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -1462,8 +1462,8 @@ def _reindex_output(self, result): # reindex `result`, and then reset the in-axis grouper columns. # Select in-axis groupers - in_axis_grps = [(i, ping.name) for (i, ping) - in enumerate(groupings) if ping.in_axis] + in_axis_grps = ((i, ping.name) for (i, ping) + in enumerate(groupings) if ping.in_axis) g_nums, g_names = zip(*in_axis_grps) result = result.drop(labels=list(g_names), axis=1) diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index c8f1a75b2eff5..c7f1aa697c2e8 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -443,12 +443,12 @@ def get_converter(s): raise ValueError(msg) converters = [get_converter(s) for s in index_sample] - names = [tuple(f(n) for f, n in zip(converters, name)) - for name in names] + names = (tuple(f(n) for f, n in zip(converters, name)) + for name in names) else: converter = get_converter(index_sample) - names = [converter(name) for name in names] + names = (converter(name) for name in names) return [self.indices.get(name, []) for name in names] diff --git a/pandas/core/groupby/grouper.py b/pandas/core/groupby/grouper.py index b0d7cf9d431cc..edba9439a675e 100644 --- a/pandas/core/groupby/grouper.py +++ b/pandas/core/groupby/grouper.py @@ -195,9 +195,9 @@ def groups(self): return self.grouper.groups def __repr__(self): - attrs_list = ["{}={!r}".format(attr_name, getattr(self, attr_name)) + attrs_list = ("{}={!r}".format(attr_name, getattr(self, attr_name)) for attr_name in self._attributes - if getattr(self, attr_name) is not None] + if getattr(self, attr_name) is not None) attrs = ", ".join(attrs_list) cls_name = self.__class__.__name__ return "{}({})".format(cls_name, attrs) diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 0a5536854ebd4..c6d31339f950d 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -780,8 +780,8 @@ def _concat_same_dtype(self, to_concat, name): Concatenate to_concat which has the same class ValueError if other is not in the categories """ - to_concat = [self._is_dtype_compat(c) for c in to_concat] - codes = np.concatenate([c.codes for c in to_concat]) + codes = np.concatenate([self._is_dtype_compat(c).codes + for c in to_concat]) result = self._create_from_codes(codes, name=name) # if name is None, _create_from_codes sets self.name result.name = name diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index bbcde8f3b3305..539da0beaefb4 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -347,10 +347,10 @@ def _setitem_with_indexer(self, indexer, value): # must have all defined axes if we have a scalar # or a list-like on the non-info axes if we have a # list-like - len_non_info_axes = [ + len_non_info_axes = ( len(_ax) for _i, _ax in enumerate(self.obj.axes) if _i != i - ] + ) if any(not l for l in len_non_info_axes): if not is_list_like_indexer(value): raise ValueError("cannot set a frame with no " diff --git a/pandas/core/internals/construction.py b/pandas/core/internals/construction.py index c05a9a0f8f3c7..7e97512682720 100644 --- a/pandas/core/internals/construction.py +++ b/pandas/core/internals/construction.py @@ -197,18 +197,12 @@ def init_dict(data, index, columns, dtype=None): arrays.loc[missing] = [val] * missing.sum() else: - - for key in data: - if (isinstance(data[key], ABCDatetimeIndex) and - data[key].tz is not None): - # GH#24096 need copy to be deep for datetime64tz case - # TODO: See if we can avoid these copies - data[key] = data[key].copy(deep=True) - keys = com.dict_keys_to_ordered_list(data) columns = data_names = Index(keys) - arrays = [data[k] for k in keys] - + # GH#24096 need copy to be deep for datetime64tz case + # TODO: See if we can avoid these copies + arrays = [data[k] if not is_datetime64tz_dtype(data[k]) else + data[k].copy(deep=True) for k in keys] return arrays_to_mgr(arrays, data_names, index, columns, dtype=dtype) diff --git a/pandas/core/resample.py b/pandas/core/resample.py index e26176cffc66d..ff4dd7da15bd1 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -83,9 +83,9 @@ def __unicode__(self): """ Provide a nice str repr of our rolling object. """ - attrs = ["{k}={v}".format(k=k, v=getattr(self.groupby, k)) + attrs = ("{k}={v}".format(k=k, v=getattr(self.groupby, k)) for k in self._attributes if - getattr(self.groupby, k, None) is not None] + getattr(self.groupby, k, None) is not None) return "{klass} [{attrs}]".format(klass=self.__class__.__name__, attrs=', '.join(attrs)) diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index c7c447d18b6b1..54f11646fc753 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -88,9 +88,9 @@ def pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', # the original values are ints # as we grouped with a NaN value # and then dropped, coercing to floats - for v in [v for v in values if v in data and v in agged]: - if (is_integer_dtype(data[v]) and - not is_integer_dtype(agged[v])): + for v in values: + if (v in data and is_integer_dtype(data[v]) and + v in agged and not is_integer_dtype(agged[v])): agged[v] = maybe_downcast_to_dtype(agged[v], data[v].dtype) table = agged diff --git a/pandas/core/reshape/tile.py b/pandas/core/reshape/tile.py index 7ad2549bd22c3..2a654fec36a9f 100644 --- a/pandas/core/reshape/tile.py +++ b/pandas/core/reshape/tile.py @@ -372,14 +372,6 @@ def _bins_to_cuts(x, bins, right=True, labels=None, return result, bins -def _trim_zeros(x): - while len(x) > 1 and x[-1] == '0': - x = x[:-1] - if len(x) > 1 and x[-1] == '.': - x = x[:-1] - return x - - def _coerce_to_type(x): """ if the passed data is of datetime/timedelta type, diff --git a/pandas/core/strings.py b/pandas/core/strings.py index bfa36cb4bfd86..183a91c952140 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -1872,7 +1872,7 @@ def _wrap_result(self, result, use_codes=True, if expand is None: # infer from ndim if expand is not specified - expand = False if result.ndim == 1 else True + expand = result.ndim != 1 elif expand is True and not isinstance(self._orig, Index): # required when expand=True is explicitly specified diff --git a/pandas/core/tools/numeric.py b/pandas/core/tools/numeric.py index bfe78b9296c40..b8a7eb5b0c570 100644 --- a/pandas/core/tools/numeric.py +++ b/pandas/core/tools/numeric.py @@ -138,7 +138,7 @@ def to_numeric(arg, errors='raise', downcast=None): values = values.astype(np.int64) else: values = ensure_object(values) - coerce_numeric = False if errors in ('ignore', 'raise') else True + coerce_numeric = errors not in ('ignore', 'raise') values = lib.maybe_convert_numeric(values, set(), coerce_numeric=coerce_numeric) diff --git a/pandas/core/window.py b/pandas/core/window.py index 5f3ea7db53d09..fb37d790f950c 100644 --- a/pandas/core/window.py +++ b/pandas/core/window.py @@ -164,9 +164,9 @@ def __unicode__(self): Provide a nice str repr of our rolling object. """ - attrs = ["{k}={v}".format(k=k, v=getattr(self, k)) + attrs = ("{k}={v}".format(k=k, v=getattr(self, k)) for k in self._attributes - if getattr(self, k, None) is not None] + if getattr(self, k, None) is not None) return "{klass} [{attrs}]".format(klass=self._window_type, attrs=','.join(attrs)) diff --git a/pandas/tests/arrays/test_integer.py b/pandas/tests/arrays/test_integer.py index 09298bb5cd08d..67e7db5460e6d 100644 --- a/pandas/tests/arrays/test_integer.py +++ b/pandas/tests/arrays/test_integer.py @@ -339,7 +339,7 @@ def _compare_other(self, data, op_name, other): expected = pd.Series(op(data._data, other)) # fill the nan locations - expected[data._mask] = True if op_name == '__ne__' else False + expected[data._mask] = op_name == '__ne__' tm.assert_series_equal(result, expected) @@ -351,7 +351,7 @@ def _compare_other(self, data, op_name, other): expected = op(expected, other) # fill the nan locations - expected[data._mask] = True if op_name == '__ne__' else False + expected[data._mask] = op_name == '__ne__' tm.assert_series_equal(result, expected) From 2448e5229683acbe7de57f2d53065247aa085b1f Mon Sep 17 00:00:00 2001 From: Noam Hershtig Date: Sat, 9 Feb 2019 20:00:15 +0200 Subject: [PATCH 091/215] Refactor groupby group_add from tempita to fused types (#24954) --- pandas/_libs/groupby.pyx | 51 ++++++++++++++++++++++++++++++ pandas/_libs/groupby_helper.pxi.in | 49 +--------------------------- pandas/core/groupby/ops.py | 2 +- 3 files changed, 53 insertions(+), 49 deletions(-) diff --git a/pandas/_libs/groupby.pyx b/pandas/_libs/groupby.pyx index e6036654c71c3..950ba3f89ffb7 100644 --- a/pandas/_libs/groupby.pyx +++ b/pandas/_libs/groupby.pyx @@ -2,6 +2,7 @@ import cython from cython import Py_ssize_t +from cython cimport floating from libc.stdlib cimport malloc, free @@ -382,5 +383,55 @@ def group_any_all(uint8_t[:] out, out[lab] = flag_val +@cython.wraparound(False) +@cython.boundscheck(False) +def _group_add(floating[:, :] out, + int64_t[:] counts, + floating[:, :] values, + const int64_t[:] labels, + Py_ssize_t min_count=0): + """ + Only aggregates on axis=0 + """ + cdef: + Py_ssize_t i, j, N, K, lab, ncounts = len(counts) + floating val, count + ndarray[floating, ndim=2] sumx, nobs + + if not len(values) == len(labels): + raise AssertionError("len(index) != len(labels)") + + nobs = np.zeros_like(out) + sumx = np.zeros_like(out) + + N, K = (values).shape + + with nogil: + + for i in range(N): + lab = labels[i] + if lab < 0: + continue + + counts[lab] += 1 + for j in range(K): + val = values[i, j] + + # not nan + if val == val: + nobs[lab, j] += 1 + sumx[lab, j] += val + + for i in range(ncounts): + for j in range(K): + if nobs[i, j] < min_count: + out[i, j] = NAN + else: + out[i, j] = sumx[i, j] + + +group_add_float32 = _group_add['float'] +group_add_float64 = _group_add['double'] + # generated from template include "groupby_helper.pxi" diff --git a/pandas/_libs/groupby_helper.pxi.in b/pandas/_libs/groupby_helper.pxi.in index 858039f038d02..db7018e1a7254 100644 --- a/pandas/_libs/groupby_helper.pxi.in +++ b/pandas/_libs/groupby_helper.pxi.in @@ -9,7 +9,7 @@ cdef extern from "numpy/npy_math.h": _int64_max = np.iinfo(np.int64).max # ---------------------------------------------------------------------- -# group_add, group_prod, group_var, group_mean, group_ohlc +# group_prod, group_var, group_mean, group_ohlc # ---------------------------------------------------------------------- {{py: @@ -27,53 +27,6 @@ def get_dispatch(dtypes): {{for name, c_type in get_dispatch(dtypes)}} -@cython.wraparound(False) -@cython.boundscheck(False) -def group_add_{{name}}({{c_type}}[:, :] out, - int64_t[:] counts, - {{c_type}}[:, :] values, - const int64_t[:] labels, - Py_ssize_t min_count=0): - """ - Only aggregates on axis=0 - """ - cdef: - Py_ssize_t i, j, N, K, lab, ncounts = len(counts) - {{c_type}} val, count - ndarray[{{c_type}}, ndim=2] sumx, nobs - - if not len(values) == len(labels): - raise AssertionError("len(index) != len(labels)") - - nobs = np.zeros_like(out) - sumx = np.zeros_like(out) - - N, K = (values).shape - - with nogil: - - for i in range(N): - lab = labels[i] - if lab < 0: - continue - - counts[lab] += 1 - for j in range(K): - val = values[i, j] - - # not nan - if val == val: - nobs[lab, j] += 1 - sumx[lab, j] += val - - for i in range(ncounts): - for j in range(K): - if nobs[i, j] < min_count: - out[i, j] = NAN - else: - out[i, j] = sumx[i, j] - - @cython.wraparound(False) @cython.boundscheck(False) def group_prod_{{name}}({{c_type}}[:, :] out, diff --git a/pandas/core/groupby/ops.py b/pandas/core/groupby/ops.py index 87f48d5a40554..78c9aa9187135 100644 --- a/pandas/core/groupby/ops.py +++ b/pandas/core/groupby/ops.py @@ -380,7 +380,7 @@ def get_func(fname): # otherwise find dtype-specific version, falling back to object for dt in [dtype_str, 'object']: f = getattr(libgroupby, "{fname}_{dtype_str}".format( - fname=fname, dtype_str=dtype_str), None) + fname=fname, dtype_str=dt), None) if f is not None: return f From 5f73594d49ea06ada4c6779006c00aaf41935595 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Sat, 9 Feb 2019 17:38:55 -0600 Subject: [PATCH 092/215] CLN: Remove ipython 2.x compat (#25150) * CLN: Remove ipython 2.x compat * trivial change to trigger asv * Update v0.25.0.rst * revert whatsnew --- asv_bench/benchmarks/__init__.py | 1 + pandas/core/frame.py | 17 -------------- pandas/io/formats/console.py | 38 -------------------------------- 3 files changed, 1 insertion(+), 55 deletions(-) diff --git a/asv_bench/benchmarks/__init__.py b/asv_bench/benchmarks/__init__.py index e69de29bb2d1d..eada147852fe1 100644 --- a/asv_bench/benchmarks/__init__.py +++ b/asv_bench/benchmarks/__init__.py @@ -0,0 +1 @@ +"""Pandas benchmarks.""" diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 5ceb9db39f830..e89aeb29f1625 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -18,7 +18,6 @@ import itertools import sys import warnings -from distutils.version import LooseVersion from textwrap import dedent import numpy as np @@ -642,22 +641,6 @@ def _repr_html_(self): Mainly for IPython notebook. """ - # qtconsole doesn't report its line width, and also - # behaves badly when outputting an HTML table - # that doesn't fit the window, so disable it. - # XXX: In IPython 3.x and above, the Qt console will not attempt to - # display HTML, so this check can be removed when support for - # IPython 2.x is no longer needed. - try: - import IPython - except ImportError: - pass - else: - if LooseVersion(IPython.__version__) < LooseVersion('3.0'): - if console.in_qtconsole(): - # 'HTML output is disabled in QtConsole' - return None - if self._info_repr(): buf = StringIO(u("")) self.info(buf=buf) diff --git a/pandas/io/formats/console.py b/pandas/io/formats/console.py index d5ef9f61bc132..ad63b3efdd832 100644 --- a/pandas/io/formats/console.py +++ b/pandas/io/formats/console.py @@ -108,44 +108,6 @@ def check_main(): return check_main() -def in_qtconsole(): - """ - check if we're inside an IPython qtconsole - - .. deprecated:: 0.14.1 - This is no longer needed, or working, in IPython 3 and above. - """ - try: - ip = get_ipython() # noqa - front_end = ( - ip.config.get('KernelApp', {}).get('parent_appname', "") or - ip.config.get('IPKernelApp', {}).get('parent_appname', "")) - if 'qtconsole' in front_end.lower(): - return True - except NameError: - return False - return False - - -def in_ipnb(): - """ - check if we're inside an IPython Notebook - - .. deprecated:: 0.14.1 - This is no longer needed, or working, in IPython 3 and above. - """ - try: - ip = get_ipython() # noqa - front_end = ( - ip.config.get('KernelApp', {}).get('parent_appname', "") or - ip.config.get('IPKernelApp', {}).get('parent_appname', "")) - if 'notebook' in front_end.lower(): - return True - except NameError: - return False - return False - - def in_ipython_frontend(): """ check if we're inside an an IPython zmq frontend From ca5b1df94b81bd75e869a5c76e2eafc82f2759d7 Mon Sep 17 00:00:00 2001 From: Sterling Paramore Date: Mon, 11 Feb 2019 04:52:38 -0800 Subject: [PATCH 093/215] BUG: Duplicated returns boolean dataframe (#25234) --- doc/source/whatsnew/v0.24.2.rst | 2 ++ doc/source/whatsnew/v0.25.0.rst | 2 ++ pandas/core/frame.py | 2 +- pandas/tests/frame/test_duplicates.py | 11 +++++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index b0f287cf0b9f6..5ae777ca68eba 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -24,6 +24,8 @@ Fixed Regressions - Fixed issue in ``DataFrame`` construction with passing a mixed list of mixed types could segfault. (:issue:`25075`) - Fixed regression in :meth:`DataFrame.apply` causing ``RecursionError`` when ``dict``-like classes were passed as argument. (:issue:`25196`) +- Fixed regression in :meth:`DataFrame.duplicated()`, where empty dataframe was not returning a boolean dtyped Series. (:issue:`25184`) + .. _whatsnew_0242.enhancements: Enhancements diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 4032dc20b2e19..1055514cd0e09 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -68,6 +68,8 @@ Performance Improvements Bug Fixes ~~~~~~~~~ +- + Categorical ^^^^^^^^^^^ diff --git a/pandas/core/frame.py b/pandas/core/frame.py index e89aeb29f1625..5c28259e0cb63 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4619,7 +4619,7 @@ def duplicated(self, subset=None, keep='first'): from pandas._libs.hashtable import duplicated_int64, _SIZE_HINT_LIMIT if self.empty: - return Series() + return Series(dtype=bool) def f(vals): labels, shape = algorithms.factorize( diff --git a/pandas/tests/frame/test_duplicates.py b/pandas/tests/frame/test_duplicates.py index f61dbbdb989e4..3396670fb5879 100644 --- a/pandas/tests/frame/test_duplicates.py +++ b/pandas/tests/frame/test_duplicates.py @@ -182,6 +182,17 @@ def test_drop_duplicates(): assert df.duplicated(keep=keep).sum() == 0 +def test_duplicated_on_empty_frame(): + # GH 25184 + + df = DataFrame(columns=['a', 'b']) + dupes = df.duplicated('a') + + result = df[dupes] + expected = df.copy() + tm.assert_frame_equal(result, expected) + + def test_drop_duplicates_with_duplicate_column_names(): # GH17836 df = DataFrame([ From 2c2519761ee065f0529e995382a02e66ee4f446d Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Mon, 11 Feb 2019 12:55:04 +0000 Subject: [PATCH 094/215] REF/TST: resample/test_base.py (#25262) --- pandas/tests/resample/test_base.py | 34 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/pandas/tests/resample/test_base.py b/pandas/tests/resample/test_base.py index 48debfa2848e7..8f912ea5c524a 100644 --- a/pandas/tests/resample/test_base.py +++ b/pandas/tests/resample/test_base.py @@ -28,15 +28,10 @@ period_range, 'pi', datetime(2005, 1, 1), datetime(2005, 1, 10)) TIMEDELTA_RANGE = (timedelta_range, 'tdi', '1 day', '10 day') -ALL_TIMESERIES_INDEXES = [DATE_RANGE, PERIOD_RANGE, TIMEDELTA_RANGE] - - -def pytest_generate_tests(metafunc): - # called once per each test function - if metafunc.function.__name__.endswith('_all_ts'): - metafunc.parametrize( - '_index_factory,_series_name,_index_start,_index_end', - ALL_TIMESERIES_INDEXES) +all_ts = pytest.mark.parametrize( + '_index_factory,_series_name,_index_start,_index_end', + [DATE_RANGE, PERIOD_RANGE, TIMEDELTA_RANGE] +) @pytest.fixture @@ -84,7 +79,8 @@ def test_asfreq_fill_value(series, create_index): assert_frame_equal(result, expected) -def test_resample_interpolate_all_ts(frame): +@all_ts +def test_resample_interpolate(frame): # # 12925 df = frame assert_frame_equal( @@ -101,8 +97,9 @@ def test_raises_on_non_datetimelike_index(): xp.resample('A').mean() +@all_ts @pytest.mark.parametrize('freq', ['M', 'D', 'H']) -def test_resample_empty_series_all_ts(freq, empty_series, resample_method): +def test_resample_empty_series(freq, empty_series, resample_method): # GH12771 & GH12868 if resample_method == 'ohlc': @@ -121,8 +118,9 @@ def test_resample_empty_series_all_ts(freq, empty_series, resample_method): assert_series_equal(result, expected, check_dtype=False) +@all_ts @pytest.mark.parametrize('freq', ['M', 'D', 'H']) -def test_resample_empty_dataframe_all_ts(empty_frame, freq, resample_method): +def test_resample_empty_dataframe(empty_frame, freq, resample_method): # GH13212 df = empty_frame # count retains dimensions too @@ -162,7 +160,8 @@ def test_resample_empty_dtypes(index, dtype, resample_method): pass -def test_resample_loffset_arg_type_all_ts(frame, create_index): +@all_ts +def test_resample_loffset_arg_type(frame, create_index): # GH 13218, 15002 df = frame expected_means = [df.values[i:i + 2].mean() @@ -202,7 +201,8 @@ def test_resample_loffset_arg_type_all_ts(frame, create_index): assert_frame_equal(result_how, expected) -def test_apply_to_empty_series_all_ts(empty_series): +@all_ts +def test_apply_to_empty_series(empty_series): # GH 14313 s = empty_series for freq in ['M', 'D', 'H']: @@ -212,7 +212,8 @@ def test_apply_to_empty_series_all_ts(empty_series): assert_series_equal(result, expected, check_dtype=False) -def test_resampler_is_iterable_all_ts(series): +@all_ts +def test_resampler_is_iterable(series): # GH 15314 freq = 'H' tg = TimeGrouper(freq, convention='start') @@ -223,7 +224,8 @@ def test_resampler_is_iterable_all_ts(series): assert_series_equal(rv, gv) -def test_resample_quantile_all_ts(series): +@all_ts +def test_resample_quantile(series): # GH 15023 s = series q = 0.75 From 75d5b48221e8d5d871d3127c1110fc1b53e0ca26 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 11 Feb 2019 07:08:15 -0600 Subject: [PATCH 095/215] Revert "BLD: prevent asv from calling sys.stdin.close() by using different launch method (#25237)" (#25253) This reverts commit f67b7fd2c75db951e63513003b1c6d3d1161a4b2. --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c86d5c50705a8..f0567d76659b6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -104,7 +104,7 @@ jobs: if git diff upstream/master --name-only | grep -q "^asv_bench/"; then cd asv_bench asv machine --yes - ASV_OUTPUT="$(asv run --quick --show-stderr --python=same --launch-method=spawn)" + ASV_OUTPUT="$(asv dev)" if [[ $(echo "$ASV_OUTPUT" | grep "failed") ]]; then echo "##vso[task.logissue type=error]Benchmarks run with errors" echo "$ASV_OUTPUT" From 91992ad51557053094fad16f7cdab06d57d85cb5 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Mon, 11 Feb 2019 14:09:35 +0100 Subject: [PATCH 096/215] BUG: pandas Timestamp tz_localize and tz_convert do not preserve `freq` attribute (#25247) --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/_libs/tslibs/timestamps.pyx | 6 +++--- pandas/tests/indexes/datetimes/test_timezones.py | 7 +++++++ pandas/tests/scalar/timestamp/test_timestamp.py | 7 +++++++ pandas/tests/series/indexing/test_indexing.py | 3 ++- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 1055514cd0e09..95c8d6e2cf6a3 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -95,7 +95,7 @@ Timezones ^^^^^^^^^ - Bug in :func:`to_datetime` with ``utc=True`` and datetime strings that would apply previously parsed UTC offsets to subsequent arguments (:issue:`24992`) -- +- Bug in :func:`Timestamp.tz_localize` and :func:`Timestamp.tz_convert` does not propagate ``freq`` (:issue:`25241`) - Numeric diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 25b0b4069cf7c..8a95d2494dfa4 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -1187,12 +1187,12 @@ class Timestamp(_Timestamp): value = tz_localize_to_utc(np.array([self.value], dtype='i8'), tz, ambiguous=ambiguous, nonexistent=nonexistent)[0] - return Timestamp(value, tz=tz) + return Timestamp(value, tz=tz, freq=self.freq) else: if tz is None: # reset tz value = tz_convert_single(self.value, UTC, self.tz) - return Timestamp(value, tz=None) + return Timestamp(value, tz=tz, freq=self.freq) else: raise TypeError('Cannot localize tz-aware Timestamp, use ' 'tz_convert for conversions') @@ -1222,7 +1222,7 @@ class Timestamp(_Timestamp): 'tz_localize to localize') else: # Same UTC timestamp, different time zone - return Timestamp(self.value, tz=tz) + return Timestamp(self.value, tz=tz, freq=self.freq) astimezone = tz_convert diff --git a/pandas/tests/indexes/datetimes/test_timezones.py b/pandas/tests/indexes/datetimes/test_timezones.py index 12c1b15733895..b25918417efcd 100644 --- a/pandas/tests/indexes/datetimes/test_timezones.py +++ b/pandas/tests/indexes/datetimes/test_timezones.py @@ -825,6 +825,13 @@ def test_dti_drop_dont_lose_tz(self): assert ind.tz is not None + def test_dti_tz_conversion_freq(self, tz_naive_fixture): + # GH25241 + t3 = DatetimeIndex(['2019-01-01 10:00'], freq='H') + assert t3.tz_localize(tz=tz_naive_fixture).freq == t3.freq + t4 = DatetimeIndex(['2019-01-02 12:00'], tz='UTC', freq='T') + assert t4.tz_convert(tz='UTC').freq == t4.freq + def test_drop_dst_boundary(self): # see gh-18031 tz = "Europe/Brussels" diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index b2c05d1564a48..c27ef3d0662c8 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -780,6 +780,13 @@ def test_hash_equivalent(self): stamp = Timestamp(datetime(2011, 1, 1)) assert d[stamp] == 5 + def test_tz_conversion_freq(self, tz_naive_fixture): + # GH25241 + t1 = Timestamp('2019-01-01 10:00', freq='H') + assert t1.tz_localize(tz=tz_naive_fixture).freq == t1.freq + t2 = Timestamp('2019-01-02 12:00', tz='UTC', freq='T') + assert t2.tz_convert(tz='UTC').freq == t2.freq + class TestTimestampNsOperations(object): diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index a5855f68127f4..dbe667a166d0a 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -114,7 +114,8 @@ def test_getitem_get(test_data): # missing d = test_data.ts.index[0] - BDay() - with pytest.raises(KeyError, match=r"Timestamp\('1999-12-31 00:00:00'\)"): + msg = r"Timestamp\('1999-12-31 00:00:00', freq='B'\)" + with pytest.raises(KeyError, match=msg): test_data.ts[d] # None From 28025fd0cb625a37f1d46fa8272053cf67c2e8f0 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 11 Feb 2019 05:21:55 -0800 Subject: [PATCH 097/215] DEPR: remove assert_panel_equal (#25238) --- pandas/tests/generic/test_generic.py | 32 +- pandas/tests/generic/test_panel.py | 38 -- pandas/tests/indexing/common.py | 2 - .../tests/indexing/multiindex/test_panel.py | 46 --- pandas/tests/indexing/test_panel.py | 90 +---- pandas/tests/indexing/test_partial.py | 32 +- .../tests/io/generate_legacy_storage_files.py | 16 +- pandas/tests/io/test_packers.py | 27 +- pandas/tests/io/test_pickle.py | 4 + pandas/tests/test_expressions.py | 53 +-- pandas/tests/test_multilevel.py | 14 +- pandas/tests/test_panel.py | 358 +----------------- pandas/util/testing.py | 63 --- 13 files changed, 27 insertions(+), 748 deletions(-) delete mode 100644 pandas/tests/generic/test_panel.py diff --git a/pandas/tests/generic/test_generic.py b/pandas/tests/generic/test_generic.py index fb17b47948336..c2f6cbf4c564c 100644 --- a/pandas/tests/generic/test_generic.py +++ b/pandas/tests/generic/test_generic.py @@ -14,8 +14,7 @@ import pandas as pd from pandas import DataFrame, MultiIndex, Panel, Series, date_range import pandas.util.testing as tm -from pandas.util.testing import ( - assert_frame_equal, assert_panel_equal, assert_series_equal) +from pandas.util.testing import assert_frame_equal, assert_series_equal import pandas.io.formats.printing as printing @@ -701,16 +700,9 @@ def test_sample(sel): assert_frame_equal(sample1, df[['colString']]) # Test default axes - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - p = Panel(items=['a', 'b', 'c'], major_axis=[2, 4, 6], - minor_axis=[1, 3, 5]) - assert_panel_equal( - p.sample(n=3, random_state=42), p.sample(n=3, axis=1, - random_state=42)) - assert_frame_equal( - df.sample(n=3, random_state=42), df.sample(n=3, axis=0, - random_state=42)) + assert_frame_equal( + df.sample(n=3, random_state=42), df.sample(n=3, axis=0, + random_state=42)) # Test that function aligns weights with frame df = DataFrame( @@ -950,22 +942,6 @@ def test_pipe_tuple_error(self): with pytest.raises(ValueError): df.A.pipe((f, 'y'), x=1, y=0) - def test_pipe_panel(self): - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - wp = Panel({'r1': DataFrame({"A": [1, 2, 3]})}) - f = lambda x, y: x + y - result = wp.pipe(f, 2) - expected = wp + 2 - assert_panel_equal(result, expected) - - result = wp.pipe((f, 'y'), x=1) - expected = wp + 1 - assert_panel_equal(result, expected) - - with pytest.raises(ValueError): - wp.pipe((f, 'y'), x=1, y=1) - @pytest.mark.parametrize('box', [pd.Series, pd.DataFrame]) def test_axis_classmethods(self, box): obj = box() diff --git a/pandas/tests/generic/test_panel.py b/pandas/tests/generic/test_panel.py deleted file mode 100644 index 73b8798661011..0000000000000 --- a/pandas/tests/generic/test_panel.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# pylint: disable-msg=E1101,W0612 - -from warnings import catch_warnings, simplefilter - -from pandas import Panel -from pandas.util.testing import assert_panel_equal - -from .test_generic import Generic - - -class TestPanel(Generic): - _typ = Panel - _comparator = lambda self, x, y: assert_panel_equal(x, y, by_blocks=True) - - -# run all the tests, but wrap each in a warning catcher -for t in ['test_rename', 'test_get_numeric_data', - 'test_get_default', 'test_nonzero', - 'test_downcast', 'test_constructor_compound_dtypes', - 'test_head_tail', - 'test_size_compat', 'test_split_compat', - 'test_unexpected_keyword', - 'test_stat_unexpected_keyword', 'test_api_compat', - 'test_stat_non_defaults_args', - 'test_truncate_out_of_bounds', - 'test_metadata_propagation', 'test_copy_and_deepcopy', - 'test_pct_change', 'test_sample']: - - def f(): - def tester(self): - f = getattr(super(TestPanel, self), t) - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - f() - return tester - - setattr(TestPanel, t, f()) diff --git a/pandas/tests/indexing/common.py b/pandas/tests/indexing/common.py index f4d6fe428515e..91ea38920c702 100644 --- a/pandas/tests/indexing/common.py +++ b/pandas/tests/indexing/common.py @@ -233,8 +233,6 @@ def _print(result, error=None): tm.assert_series_equal(rs, xp) elif xp.ndim == 2: tm.assert_frame_equal(rs, xp) - elif xp.ndim == 3: - tm.assert_panel_equal(rs, xp) result = 'ok' except AssertionError as e: detail = str(e) diff --git a/pandas/tests/indexing/multiindex/test_panel.py b/pandas/tests/indexing/multiindex/test_panel.py index 68c8fadd2f0dd..314009146911a 100644 --- a/pandas/tests/indexing/multiindex/test_panel.py +++ b/pandas/tests/indexing/multiindex/test_panel.py @@ -55,49 +55,3 @@ def test_iloc_getitem_panel_multiindex(self): result = p.loc[:, (1, 'y'), 'u'] tm.assert_series_equal(result, expected) - - def test_panel_setitem_with_multiindex(self): - - # 10360 - # failing with a multi-index - arr = np.array([[[1, 2, 3], [0, 0, 0]], - [[0, 0, 0], [0, 0, 0]]], - dtype=np.float64) - - # reg index - axes = dict(items=['A', 'B'], major_axis=[0, 1], - minor_axis=['X', 'Y', 'Z']) - p1 = Panel(0., **axes) - p1.iloc[0, 0, :] = [1, 2, 3] - expected = Panel(arr, **axes) - tm.assert_panel_equal(p1, expected) - - # multi-indexes - axes['items'] = MultiIndex.from_tuples( - [('A', 'a'), ('B', 'b')]) - p2 = Panel(0., **axes) - p2.iloc[0, 0, :] = [1, 2, 3] - expected = Panel(arr, **axes) - tm.assert_panel_equal(p2, expected) - - axes['major_axis'] = MultiIndex.from_tuples( - [('A', 1), ('A', 2)]) - p3 = Panel(0., **axes) - p3.iloc[0, 0, :] = [1, 2, 3] - expected = Panel(arr, **axes) - tm.assert_panel_equal(p3, expected) - - axes['minor_axis'] = MultiIndex.from_product( - [['X'], range(3)]) - p4 = Panel(0., **axes) - p4.iloc[0, 0, :] = [1, 2, 3] - expected = Panel(arr, **axes) - tm.assert_panel_equal(p4, expected) - - arr = np.array( - [[[1, 0, 0], [2, 0, 0]], [[0, 0, 0], [0, 0, 0]]], - dtype=np.float64) - p5 = Panel(0., **axes) - p5.iloc[0, :, 0] = [1, 2] - expected = Panel(arr, **axes) - tm.assert_panel_equal(p5, expected) diff --git a/pandas/tests/indexing/test_panel.py b/pandas/tests/indexing/test_panel.py index 8530adec011be..8033d19f330b3 100644 --- a/pandas/tests/indexing/test_panel.py +++ b/pandas/tests/indexing/test_panel.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from pandas import DataFrame, Panel, date_range +from pandas import Panel, date_range from pandas.util import testing as tm @@ -31,30 +31,6 @@ def test_iloc_getitem_panel(self): expected = p.loc['B', 'b', 'two'] assert result == expected - # slice - result = p.iloc[1:3] - expected = p.loc[['B', 'C']] - tm.assert_panel_equal(result, expected) - - result = p.iloc[:, 0:2] - expected = p.loc[:, ['a', 'b']] - tm.assert_panel_equal(result, expected) - - # list of integers - result = p.iloc[[0, 2]] - expected = p.loc[['A', 'C']] - tm.assert_panel_equal(result, expected) - - # neg indices - result = p.iloc[[-1, 1], [-1, 1]] - expected = p.loc[['D', 'B'], ['c', 'b']] - tm.assert_panel_equal(result, expected) - - # dups indices - result = p.iloc[[-1, -1, 1], [-1, 1]] - expected = p.loc[['D', 'D', 'B'], ['c', 'b']] - tm.assert_panel_equal(result, expected) - # combined result = p.iloc[0, [True, True], [0, 1]] expected = p.loc['A', ['a', 'b'], ['one', 'two']] @@ -110,18 +86,6 @@ def test_iloc_panel_issue(self): def test_panel_getitem(self): with catch_warnings(record=True): - # GH4016, date selection returns a frame when a partial string - # selection - ind = date_range(start="2000", freq="D", periods=1000) - df = DataFrame( - np.random.randn( - len(ind), 5), index=ind, columns=list('ABCDE')) - panel = Panel({'frame_' + c: df for c in list('ABC')}) - - test2 = panel.loc[:, "2002":"2002-12-31"] - test1 = panel.loc[:, "2002"] - tm.assert_panel_equal(test1, test2) - # with an object-like # GH 9140 class TestObject(object): @@ -138,55 +102,3 @@ def __str__(self): expected = p.iloc[0] result = p[obj] tm.assert_frame_equal(result, expected) - - def test_panel_setitem(self): - - with catch_warnings(record=True): - # GH 7763 - # loc and setitem have setting differences - np.random.seed(0) - index = range(3) - columns = list('abc') - - panel = Panel({'A': DataFrame(np.random.randn(3, 3), - index=index, columns=columns), - 'B': DataFrame(np.random.randn(3, 3), - index=index, columns=columns), - 'C': DataFrame(np.random.randn(3, 3), - index=index, columns=columns)}) - - replace = DataFrame(np.eye(3, 3), index=range(3), columns=columns) - expected = Panel({'A': replace, 'B': replace, 'C': replace}) - - p = panel.copy() - for idx in list('ABC'): - p[idx] = replace - tm.assert_panel_equal(p, expected) - - p = panel.copy() - for idx in list('ABC'): - p.loc[idx, :, :] = replace - tm.assert_panel_equal(p, expected) - - def test_panel_assignment(self): - - with catch_warnings(record=True): - # GH3777 - wp = Panel(np.random.randn(2, 5, 4), items=['Item1', 'Item2'], - major_axis=date_range('1/1/2000', periods=5), - minor_axis=['A', 'B', 'C', 'D']) - wp2 = Panel(np.random.randn(2, 5, 4), items=['Item1', 'Item2'], - major_axis=date_range('1/1/2000', periods=5), - minor_axis=['A', 'B', 'C', 'D']) - - # TODO: unused? - # expected = wp.loc[['Item1', 'Item2'], :, ['A', 'B']] - - with pytest.raises(NotImplementedError): - wp.loc[['Item1', 'Item2'], :, ['A', 'B']] = wp2.loc[ - ['Item1', 'Item2'], :, ['A', 'B']] - - # to_assign = wp2.loc[['Item1', 'Item2'], :, ['A', 'B']] - # wp.loc[['Item1', 'Item2'], :, ['A', 'B']] = to_assign - # result = wp.loc[['Item1', 'Item2'], :, ['A', 'B']] - # tm.assert_panel_equal(result,expected) diff --git a/pandas/tests/indexing/test_partial.py b/pandas/tests/indexing/test_partial.py index 5b6a5ab9ecf7b..e8ce5bc4c36ef 100644 --- a/pandas/tests/indexing/test_partial.py +++ b/pandas/tests/indexing/test_partial.py @@ -10,13 +10,12 @@ import pytest import pandas as pd -from pandas import DataFrame, Index, Panel, Series, date_range +from pandas import DataFrame, Index, Series, date_range from pandas.util import testing as tm class TestPartialSetting(object): - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") @pytest.mark.filterwarnings("ignore:\\n.ix:DeprecationWarning") def test_partial_setting(self): @@ -116,35 +115,6 @@ def test_partial_setting(self): df.ix[:, 'C'] = df.ix[:, 'A'] tm.assert_frame_equal(df, expected) - with catch_warnings(record=True): - # ## panel ## - p_orig = Panel(np.arange(16).reshape(2, 4, 2), - items=['Item1', 'Item2'], - major_axis=pd.date_range('2001/1/12', periods=4), - minor_axis=['A', 'B'], dtype='float64') - - # panel setting via item - p_orig = Panel(np.arange(16).reshape(2, 4, 2), - items=['Item1', 'Item2'], - major_axis=pd.date_range('2001/1/12', periods=4), - minor_axis=['A', 'B'], dtype='float64') - expected = p_orig.copy() - expected['Item3'] = expected['Item1'] - p = p_orig.copy() - p.loc['Item3'] = p['Item1'] - tm.assert_panel_equal(p, expected) - - # panel with aligned series - expected = p_orig.copy() - expected = expected.transpose(2, 1, 0) - expected['C'] = DataFrame({'Item1': [30, 30, 30, 30], - 'Item2': [32, 32, 32, 32]}, - index=p_orig.major_axis) - expected = expected.transpose(2, 1, 0) - p = p_orig.copy() - p.loc[:, :, 'C'] = Series([30, 32], index=p_orig.items) - tm.assert_panel_equal(p, expected) - # GH 8473 dates = date_range('1/1/2000', periods=8) df_orig = DataFrame(np.random.randn(8, 4), index=dates, diff --git a/pandas/tests/io/generate_legacy_storage_files.py b/pandas/tests/io/generate_legacy_storage_files.py index 6774eac6d6c1a..6c6e28cb1c090 100755 --- a/pandas/tests/io/generate_legacy_storage_files.py +++ b/pandas/tests/io/generate_legacy_storage_files.py @@ -41,7 +41,6 @@ import os import platform as pl import sys -from warnings import catch_warnings, filterwarnings import numpy as np @@ -49,7 +48,7 @@ import pandas from pandas import ( - Categorical, DataFrame, Index, MultiIndex, NaT, Panel, Period, Series, + Categorical, DataFrame, Index, MultiIndex, NaT, Period, Series, SparseDataFrame, SparseSeries, Timestamp, bdate_range, date_range, period_range, timedelta_range, to_msgpack) @@ -187,18 +186,6 @@ def create_data(): u'C': Timestamp('20130603', tz='UTC')}, index=range(5)) ) - with catch_warnings(record=True): - filterwarnings("ignore", "\\nPanel", FutureWarning) - mixed_dup_panel = Panel({u'ItemA': frame[u'float'], - u'ItemB': frame[u'int']}) - mixed_dup_panel.items = [u'ItemA', u'ItemA'] - panel = dict(float=Panel({u'ItemA': frame[u'float'], - u'ItemB': frame[u'float'] + 1}), - dup=Panel( - np.arange(30).reshape(3, 5, 2).astype(np.float64), - items=[u'A', u'B', u'A']), - mixed_dup=mixed_dup_panel) - cat = dict(int8=Categorical(list('abcdefg')), int16=Categorical(np.arange(1000)), int32=Categorical(np.arange(10000))) @@ -241,7 +228,6 @@ def create_data(): return dict(series=series, frame=frame, - panel=panel, index=index, scalars=scalars, mi=mi, diff --git a/pandas/tests/io/test_packers.py b/pandas/tests/io/test_packers.py index 9eb6d327be025..375557c43a3ae 100644 --- a/pandas/tests/io/test_packers.py +++ b/pandas/tests/io/test_packers.py @@ -13,9 +13,8 @@ import pandas from pandas import ( - Categorical, DataFrame, Index, Interval, MultiIndex, NaT, Panel, Period, - Series, Timestamp, bdate_range, compat, date_range, period_range) -from pandas.tests.test_panel import assert_panel_equal + Categorical, DataFrame, Index, Interval, MultiIndex, NaT, Period, Series, + Timestamp, bdate_range, compat, date_range, period_range) import pandas.util.testing as tm from pandas.util.testing import ( assert_categorical_equal, assert_frame_equal, assert_index_equal, @@ -62,8 +61,6 @@ def check_arbitrary(a, b): assert(len(a) == len(b)) for a_, b_ in zip(a, b): check_arbitrary(a_, b_) - elif isinstance(a, Panel): - assert_panel_equal(a, b) elif isinstance(a, DataFrame): assert_frame_equal(a, b) elif isinstance(a, Series): @@ -490,23 +487,12 @@ def setup_method(self, method): 'int': DataFrame(dict(A=data['B'], B=Series(data['B']) + 1)), 'mixed': DataFrame(data)} - self.panel = { - 'float': Panel(dict(ItemA=self.frame['float'], - ItemB=self.frame['float'] + 1))} - def test_basic_frame(self): for s, i in self.frame.items(): i_rec = self.encode_decode(i) assert_frame_equal(i, i_rec) - def test_basic_panel(self): - - with catch_warnings(record=True): - for s, i in self.panel.items(): - i_rec = self.encode_decode(i) - assert_panel_equal(i, i_rec) - def test_multi(self): i_rec = self.encode_decode(self.frame) @@ -876,6 +862,10 @@ class TestMsgpack(object): def check_min_structure(self, data, version): for typ, v in self.minimum_structure.items(): + if typ == "panel": + # FIXME: kludge; get this key out of the legacy file + continue + assert typ in data, '"{0}" not found in unpacked data'.format(typ) for kind in v: msg = '"{0}" not found in data["{1}"]'.format(kind, typ) @@ -887,6 +877,11 @@ def compare(self, current_data, all_data, vf, version): data = read_msgpack(vf, encoding='latin-1') else: data = read_msgpack(vf) + + if "panel" in data: + # FIXME: kludge; get the key out of the stored file + del data["panel"] + self.check_min_structure(data, version) for typ, dv in data.items(): assert typ in all_data, ('unpacked data contains ' diff --git a/pandas/tests/io/test_pickle.py b/pandas/tests/io/test_pickle.py index 7f3fe1aa401ea..b4befadaddc42 100644 --- a/pandas/tests/io/test_pickle.py +++ b/pandas/tests/io/test_pickle.py @@ -75,6 +75,10 @@ def compare(data, vf, version): m = globals() for typ, dv in data.items(): + if typ == "panel": + # FIXME: kludge; get this key out of the legacy file + continue + for dt, result in dv.items(): try: expected = data[typ][dt] diff --git a/pandas/tests/test_expressions.py b/pandas/tests/test_expressions.py index f5aa0b0b3c9c8..7a2680135ea80 100644 --- a/pandas/tests/test_expressions.py +++ b/pandas/tests/test_expressions.py @@ -3,19 +3,17 @@ import operator import re -from warnings import catch_warnings, simplefilter import numpy as np from numpy.random import randn import pytest from pandas import _np_version_under1p13, compat -from pandas.core.api import DataFrame, Panel +from pandas.core.api import DataFrame from pandas.core.computation import expressions as expr import pandas.util.testing as tm from pandas.util.testing import ( - assert_almost_equal, assert_frame_equal, assert_panel_equal, - assert_series_equal) + assert_almost_equal, assert_frame_equal, assert_series_equal) from pandas.io.formats.printing import pprint_thing @@ -39,23 +37,6 @@ _integer2 = DataFrame(np.random.randint(1, 100, size=(101, 4)), columns=list('ABCD'), dtype='int64') -with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - _frame_panel = Panel(dict(ItemA=_frame.copy(), - ItemB=(_frame.copy() + 3), - ItemC=_frame.copy(), - ItemD=_frame.copy())) - _frame2_panel = Panel(dict(ItemA=_frame2.copy(), - ItemB=(_frame2.copy() + 3), - ItemC=_frame2.copy(), - ItemD=_frame2.copy())) - _integer_panel = Panel(dict(ItemA=_integer, - ItemB=(_integer + 34).astype('int64'))) - _integer2_panel = Panel(dict(ItemA=_integer2, - ItemB=(_integer2 + 34).astype('int64'))) - _mixed_panel = Panel(dict(ItemA=_mixed, ItemB=(_mixed + 3))) - _mixed2_panel = Panel(dict(ItemA=_mixed2, ItemB=(_mixed2 + 3))) - @pytest.mark.skipif(not expr._USE_NUMEXPR, reason='not using numexpr') class TestExpressions(object): @@ -173,42 +154,18 @@ def run_series(self, ser, other, binary_comp=None, **kwargs): # self.run_binary(ser, binary_comp, assert_frame_equal, # test_flex=True, **kwargs) - def run_panel(self, panel, other, binary_comp=None, run_binary=True, - assert_func=assert_panel_equal, **kwargs): - self.run_arithmetic(panel, other, assert_func, test_flex=False, - **kwargs) - self.run_arithmetic(panel, other, assert_func, test_flex=True, - **kwargs) - if run_binary: - if binary_comp is None: - binary_comp = other + 1 - self.run_binary(panel, binary_comp, assert_func, - test_flex=False, **kwargs) - self.run_binary(panel, binary_comp, assert_func, - test_flex=True, **kwargs) - def test_integer_arithmetic_frame(self): self.run_frame(self.integer, self.integer) def test_integer_arithmetic_series(self): self.run_series(self.integer.iloc[:, 0], self.integer.iloc[:, 0]) - @pytest.mark.slow - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") - def test_integer_panel(self): - self.run_panel(_integer2_panel, np.random.randint(1, 100)) - def test_float_arithemtic_frame(self): self.run_frame(self.frame2, self.frame2) def test_float_arithmetic_series(self): self.run_series(self.frame2.iloc[:, 0], self.frame2.iloc[:, 0]) - @pytest.mark.slow - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") - def test_float_panel(self): - self.run_panel(_frame2_panel, np.random.randn() + 0.1, binary_comp=0.8) - def test_mixed_arithmetic_frame(self): # TODO: FIGURE OUT HOW TO GET IT TO WORK... # can't do arithmetic because comparison methods try to do *entire* @@ -219,12 +176,6 @@ def test_mixed_arithmetic_series(self): for col in self.mixed2.columns: self.run_series(self.mixed2[col], self.mixed2[col], binary_comp=4) - @pytest.mark.slow - @pytest.mark.filterwarnings("ignore:\\nPanel:FutureWarning") - def test_mixed_panel(self): - self.run_panel(_mixed2_panel, np.random.randint(1, 100), - binary_comp=-2) - def test_float_arithemtic(self): self.run_arithmetic(self.frame, self.frame, assert_frame_equal) self.run_arithmetic(self.frame.iloc[:, 0], self.frame.iloc[:, 0], diff --git a/pandas/tests/test_multilevel.py b/pandas/tests/test_multilevel.py index a7bbbbb5033ac..4ea7e9b8ec9a4 100644 --- a/pandas/tests/test_multilevel.py +++ b/pandas/tests/test_multilevel.py @@ -15,7 +15,7 @@ from pandas.core.dtypes.common import is_float_dtype, is_integer_dtype import pandas as pd -from pandas import DataFrame, Panel, Series, Timestamp, isna +from pandas import DataFrame, Series, Timestamp, isna from pandas.core.index import Index, MultiIndex import pandas.util.testing as tm @@ -818,18 +818,6 @@ def test_swaplevel(self): exp = self.frame.swaplevel('first', 'second').T tm.assert_frame_equal(swapped, exp) - def test_swaplevel_panel(self): - with catch_warnings(record=True): - simplefilter("ignore", FutureWarning) - panel = Panel({'ItemA': self.frame, 'ItemB': self.frame * 2}) - expected = panel.copy() - expected.major_axis = expected.major_axis.swaplevel(0, 1) - - for result in (panel.swaplevel(axis='major'), - panel.swaplevel(0, axis='major'), - panel.swaplevel(0, 1, axis='major')): - tm.assert_panel_equal(result, expected) - def test_reorder_levels(self): result = self.ymd.reorder_levels(['month', 'day', 'year']) expected = self.ymd.swaplevel(0, 1).swaplevel(1, 2) diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index bfcafda1dc783..b418091de8d7f 100644 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -13,10 +13,9 @@ from pandas.core.panel import Panel import pandas.util.testing as tm from pandas.util.testing import ( - assert_almost_equal, assert_frame_equal, assert_panel_equal, - assert_series_equal, makeCustomDataframe as mkdf, makeMixedDataFrame) + assert_almost_equal, assert_frame_equal, assert_series_equal, + makeCustomDataframe as mkdf, makeMixedDataFrame) -from pandas.io.formats.printing import pprint_thing from pandas.tseries.offsets import MonthEnd @@ -295,25 +294,6 @@ def test_constructor_error_msgs(self): Panel(np.random.randn(3, 4, 5), lrange(5), lrange(5), lrange(4)) - def test_convert_objects(self): - # GH 4937 - p = Panel(dict(A=dict(a=['1', '1.0']))) - expected = Panel(dict(A=dict(a=[1, 1.0]))) - result = p._convert(numeric=True, coerce=True) - assert_panel_equal(result, expected) - - def test_astype(self): - # GH7271 - data = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) - panel = Panel(data, ['a', 'b'], ['c', 'd'], ['e', 'f']) - - str_data = np.array([[['1', '2'], ['3', '4']], - [['5', '6'], ['7', '8']]]) - expected = Panel(str_data, ['a', 'b'], ['c', 'd'], ['e', 'f']) - assert_panel_equal(panel.astype(str), expected) - - pytest.raises(NotImplementedError, panel.astype, {0: str}) - def test_apply_slabs(self): # with multi-indexes # GH7469 @@ -350,54 +330,6 @@ def test_apply_no_or_zero_ndim(self): assert_series_equal(result_float, expected_float) assert_series_equal(result_float64, expected_float64) - def test_reindex_axis_style(self): - panel = Panel(np.random.rand(5, 5, 5)) - expected0 = Panel(panel.values).iloc[[0, 1]] - expected1 = Panel(panel.values).iloc[:, [0, 1]] - expected2 = Panel(panel.values).iloc[:, :, [0, 1]] - - result = panel.reindex([0, 1], axis=0) - assert_panel_equal(result, expected0) - - result = panel.reindex([0, 1], axis=1) - assert_panel_equal(result, expected1) - - result = panel.reindex([0, 1], axis=2) - assert_panel_equal(result, expected2) - - result = panel.reindex([0, 1], axis=2) - assert_panel_equal(result, expected2) - - def test_reindex_multi(self): - - # multi-axis indexing consistency - # GH 5900 - df = DataFrame(np.random.randn(4, 3)) - p = Panel({'Item1': df}) - expected = Panel({'Item1': df}) - expected['Item2'] = np.nan - - items = ['Item1', 'Item2'] - major_axis = np.arange(4) - minor_axis = np.arange(3) - - results = [] - results.append(p.reindex(items=items, major_axis=major_axis, - copy=True)) - results.append(p.reindex(items=items, major_axis=major_axis, - copy=False)) - results.append(p.reindex(items=items, minor_axis=minor_axis, - copy=True)) - results.append(p.reindex(items=items, minor_axis=minor_axis, - copy=False)) - results.append(p.reindex(items=items, major_axis=major_axis, - minor_axis=minor_axis, copy=True)) - results.append(p.reindex(items=items, major_axis=major_axis, - minor_axis=minor_axis, copy=False)) - - for i, r in enumerate(results): - assert_panel_equal(expected, r) - def test_fillna(self): # limit not implemented when only value is specified p = Panel(np.random.randn(3, 4, 5)) @@ -405,25 +337,6 @@ def test_fillna(self): pytest.raises(NotImplementedError, lambda: p.fillna(999, limit=1)) - # Test in place fillNA - # Expected result - expected = Panel([[[0, 1], [2, 1]], [[10, 11], [12, 11]]], - items=['a', 'b'], minor_axis=['x', 'y'], - dtype=np.float64) - # method='ffill' - p1 = Panel([[[0, 1], [2, np.nan]], [[10, 11], [12, np.nan]]], - items=['a', 'b'], minor_axis=['x', 'y'], - dtype=np.float64) - p1.fillna(method='ffill', inplace=True) - assert_panel_equal(p1, expected) - - # method='bfill' - p2 = Panel([[[0, np.nan], [2, 1]], [[10, np.nan], [12, 11]]], - items=['a', 'b'], minor_axis=['x', 'y'], - dtype=np.float64) - p2.fillna(method='bfill', inplace=True) - assert_panel_equal(p2, expected) - def test_to_frame_multi_major(self): idx = MultiIndex.from_tuples( [(1, 'one'), (1, 'two'), (2, 'one'), (2, 'two')]) @@ -542,11 +455,6 @@ def test_panel_dups(self): result = panel.loc['E'] assert_frame_equal(result, expected) - expected = no_dup_panel.loc[['A', 'B']] - expected.items = ['A', 'A'] - result = panel.loc['A'] - assert_panel_equal(result, expected) - # major data = np.random.randn(5, 5, 5) no_dup_panel = Panel(data, major_axis=list("ABCDE")) @@ -560,11 +468,6 @@ def test_panel_dups(self): result = panel.loc[:, 'E'] assert_frame_equal(result, expected) - expected = no_dup_panel.loc[:, ['A', 'B']] - expected.major_axis = ['A', 'A'] - result = panel.loc[:, 'A'] - assert_panel_equal(result, expected) - # minor data = np.random.randn(5, 100, 5) no_dup_panel = Panel(data, minor_axis=list("ABCDE")) @@ -578,11 +481,6 @@ def test_panel_dups(self): result = panel.loc[:, :, 'E'] assert_frame_equal(result, expected) - expected = no_dup_panel.loc[:, :, ['A', 'B']] - expected.minor_axis = ['A', 'A'] - result = panel.loc[:, :, 'A'] - assert_panel_equal(result, expected) - def test_filter(self): pass @@ -595,93 +493,14 @@ def test_shift(self): shifted = mixed_panel.shift(1) assert_series_equal(mixed_panel.dtypes, shifted.dtypes) - def test_pct_change(self): - df1 = DataFrame({'c1': [1, 2, 5], 'c2': [3, 4, 6]}) - df2 = df1 + 1 - df3 = DataFrame({'c1': [3, 4, 7], 'c2': [5, 6, 8]}) - wp = Panel({'i1': df1, 'i2': df2, 'i3': df3}) - # major, 1 - result = wp.pct_change() # axis='major' - expected = Panel({'i1': df1.pct_change(), - 'i2': df2.pct_change(), - 'i3': df3.pct_change()}) - assert_panel_equal(result, expected) - result = wp.pct_change(axis=1) - assert_panel_equal(result, expected) - # major, 2 - result = wp.pct_change(periods=2) - expected = Panel({'i1': df1.pct_change(2), - 'i2': df2.pct_change(2), - 'i3': df3.pct_change(2)}) - assert_panel_equal(result, expected) - # minor, 1 - result = wp.pct_change(axis='minor') - expected = Panel({'i1': df1.pct_change(axis=1), - 'i2': df2.pct_change(axis=1), - 'i3': df3.pct_change(axis=1)}) - assert_panel_equal(result, expected) - result = wp.pct_change(axis=2) - assert_panel_equal(result, expected) - # minor, 2 - result = wp.pct_change(periods=2, axis='minor') - expected = Panel({'i1': df1.pct_change(periods=2, axis=1), - 'i2': df2.pct_change(periods=2, axis=1), - 'i3': df3.pct_change(periods=2, axis=1)}) - assert_panel_equal(result, expected) - # items, 1 - result = wp.pct_change(axis='items') - expected = Panel( - {'i1': DataFrame({'c1': [np.nan, np.nan, np.nan], - 'c2': [np.nan, np.nan, np.nan]}), - 'i2': DataFrame({'c1': [1, 0.5, .2], - 'c2': [1. / 3, 0.25, 1. / 6]}), - 'i3': DataFrame({'c1': [.5, 1. / 3, 1. / 6], - 'c2': [.25, .2, 1. / 7]})}) - assert_panel_equal(result, expected) - result = wp.pct_change(axis=0) - assert_panel_equal(result, expected) - # items, 2 - result = wp.pct_change(periods=2, axis='items') - expected = Panel( - {'i1': DataFrame({'c1': [np.nan, np.nan, np.nan], - 'c2': [np.nan, np.nan, np.nan]}), - 'i2': DataFrame({'c1': [np.nan, np.nan, np.nan], - 'c2': [np.nan, np.nan, np.nan]}), - 'i3': DataFrame({'c1': [2, 1, .4], - 'c2': [2. / 3, .5, 1. / 3]})}) - assert_panel_equal(result, expected) - - def test_round(self): - values = [[[-3.2, 2.2], [0, -4.8213], [3.123, 123.12], - [-1566.213, 88.88], [-12, 94.5]], - [[-5.82, 3.5], [6.21, -73.272], [-9.087, 23.12], - [272.212, -99.99], [23, -76.5]]] - evalues = [[[float(np.around(i)) for i in j] for j in k] - for k in values] - p = Panel(values, items=['Item1', 'Item2'], - major_axis=date_range('1/1/2000', periods=5), - minor_axis=['A', 'B']) - expected = Panel(evalues, items=['Item1', 'Item2'], - major_axis=date_range('1/1/2000', periods=5), - minor_axis=['A', 'B']) - result = p.round() - assert_panel_equal(expected, result) - def test_numpy_round(self): values = [[[-3.2, 2.2], [0, -4.8213], [3.123, 123.12], [-1566.213, 88.88], [-12, 94.5]], [[-5.82, 3.5], [6.21, -73.272], [-9.087, 23.12], [272.212, -99.99], [23, -76.5]]] - evalues = [[[float(np.around(i)) for i in j] for j in k] - for k in values] p = Panel(values, items=['Item1', 'Item2'], major_axis=date_range('1/1/2000', periods=5), minor_axis=['A', 'B']) - expected = Panel(evalues, items=['Item1', 'Item2'], - major_axis=date_range('1/1/2000', periods=5), - minor_axis=['A', 'B']) - result = np.round(p) - assert_panel_equal(expected, result) msg = "the 'out' parameter is not supported" with pytest.raises(ValueError, match=msg): @@ -699,7 +518,6 @@ def test_multiindex_get(self): minor_axis=np.arange(5)) f1 = wp['a'] f2 = wp.loc['a'] - assert_panel_equal(f1, f2) assert (f1.items == [1, 2]).all() assert (f2.items == [1, 2]).all() @@ -711,178 +529,6 @@ def test_repr_empty(self): empty = Panel() repr(empty) - @pytest.mark.filterwarnings("ignore:'.reindex:FutureWarning") - def test_dropna(self): - p = Panel(np.random.randn(4, 5, 6), major_axis=list('abcde')) - p.loc[:, ['b', 'd'], 0] = np.nan - - result = p.dropna(axis=1) - exp = p.loc[:, ['a', 'c', 'e'], :] - assert_panel_equal(result, exp) - inp = p.copy() - inp.dropna(axis=1, inplace=True) - assert_panel_equal(inp, exp) - - result = p.dropna(axis=1, how='all') - assert_panel_equal(result, p) - - p.loc[:, ['b', 'd'], :] = np.nan - result = p.dropna(axis=1, how='all') - exp = p.loc[:, ['a', 'c', 'e'], :] - assert_panel_equal(result, exp) - - p = Panel(np.random.randn(4, 5, 6), items=list('abcd')) - p.loc[['b'], :, 0] = np.nan - - result = p.dropna() - exp = p.loc[['a', 'c', 'd']] - assert_panel_equal(result, exp) - - result = p.dropna(how='all') - assert_panel_equal(result, p) - - p.loc['b'] = np.nan - result = p.dropna(how='all') - exp = p.loc[['a', 'c', 'd']] - assert_panel_equal(result, exp) - - def test_drop(self): - df = DataFrame({"A": [1, 2], "B": [3, 4]}) - panel = Panel({"One": df, "Two": df}) - - def check_drop(drop_val, axis_number, aliases, expected): - try: - actual = panel.drop(drop_val, axis=axis_number) - assert_panel_equal(actual, expected) - for alias in aliases: - actual = panel.drop(drop_val, axis=alias) - assert_panel_equal(actual, expected) - except AssertionError: - pprint_thing("Failed with axis_number %d and aliases: %s" % - (axis_number, aliases)) - raise - # Items - expected = Panel({"One": df}) - check_drop('Two', 0, ['items'], expected) - - pytest.raises(KeyError, panel.drop, 'Three') - - # errors = 'ignore' - dropped = panel.drop('Three', errors='ignore') - assert_panel_equal(dropped, panel) - dropped = panel.drop(['Two', 'Three'], errors='ignore') - expected = Panel({"One": df}) - assert_panel_equal(dropped, expected) - - # Major - exp_df = DataFrame({"A": [2], "B": [4]}, index=[1]) - expected = Panel({"One": exp_df, "Two": exp_df}) - check_drop(0, 1, ['major_axis', 'major'], expected) - - exp_df = DataFrame({"A": [1], "B": [3]}, index=[0]) - expected = Panel({"One": exp_df, "Two": exp_df}) - check_drop([1], 1, ['major_axis', 'major'], expected) - - # Minor - exp_df = df[['B']] - expected = Panel({"One": exp_df, "Two": exp_df}) - check_drop(["A"], 2, ['minor_axis', 'minor'], expected) - - exp_df = df[['A']] - expected = Panel({"One": exp_df, "Two": exp_df}) - check_drop("B", 2, ['minor_axis', 'minor'], expected) - - def test_update(self): - pan = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]], - [[1.5, np.nan, 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]]]) - - other = Panel( - [[[3.6, 2., np.nan], [np.nan, np.nan, 7]]], items=[1]) - - pan.update(other) - - expected = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], [1.5, np.nan, 3.]], - [[3.6, 2., 3], [1.5, np.nan, 7], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]]]) - - assert_panel_equal(pan, expected) - - def test_update_from_dict(self): - pan = Panel({'one': DataFrame([[1.5, np.nan, 3], - [1.5, np.nan, 3], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]]), - 'two': DataFrame([[1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]])}) - - other = {'two': DataFrame( - [[3.6, 2., np.nan], [np.nan, np.nan, 7]])} - - pan.update(other) - - expected = Panel( - {'one': DataFrame([[1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]]), - 'two': DataFrame([[3.6, 2., 3], - [1.5, np.nan, 7], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]]) - } - ) - - assert_panel_equal(pan, expected) - - def test_update_nooverwrite(self): - pan = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]], - [[1.5, np.nan, 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]]]) - - other = Panel( - [[[3.6, 2., np.nan], [np.nan, np.nan, 7]]], items=[1]) - - pan.update(other, overwrite=False) - - expected = Panel([[[1.5, np.nan, 3], [1.5, np.nan, 3], - [1.5, np.nan, 3.], [1.5, np.nan, 3.]], - [[1.5, 2., 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]]]) - - assert_panel_equal(pan, expected) - - def test_update_filtered(self): - pan = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]], - [[1.5, np.nan, 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], - [1.5, np.nan, 3.]]]) - - other = Panel( - [[[3.6, 2., np.nan], [np.nan, np.nan, 7]]], items=[1]) - - pan.update(other, filter_func=lambda x: x > 2) - - expected = Panel([[[1.5, np.nan, 3.], [1.5, np.nan, 3.], - [1.5, np.nan, 3.], [1.5, np.nan, 3.]], - [[1.5, np.nan, 3], [1.5, np.nan, 7], - [1.5, np.nan, 3.], [1.5, np.nan, 3.]]]) - - assert_panel_equal(pan, expected) - @pytest.mark.parametrize('bad_kwarg, exception, msg', [ # errors must be 'ignore' or 'raise' ({'errors': 'something'}, ValueError, 'The parameter errors must.*'), diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 387f402348513..a5ae1f6a4d960 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -1503,69 +1503,6 @@ def assert_frame_equal(left, right, check_dtype=True, obj='DataFrame.iloc[:, {idx}]'.format(idx=i)) -def assert_panel_equal(left, right, - check_dtype=True, - check_panel_type=False, - check_less_precise=False, - check_names=False, - by_blocks=False, - obj='Panel'): - """Check that left and right Panels are equal. - - Parameters - ---------- - left : Panel (or nd) - right : Panel (or nd) - check_dtype : bool, default True - Whether to check the Panel dtype is identical. - check_panel_type : bool, default False - Whether to check the Panel class is identical. - check_less_precise : bool or int, default False - Specify comparison precision. Only used when check_exact is False. - 5 digits (False) or 3 digits (True) after decimal points are compared. - If int, then specify the digits to compare - check_names : bool, default True - Whether to check the Index names attribute. - by_blocks : bool, default False - Specify how to compare internal data. If False, compare by columns. - If True, compare by blocks. - obj : str, default 'Panel' - Specify the object name being compared, internally used to show - the appropriate assertion message. - """ - - if check_panel_type: - assert_class_equal(left, right, obj=obj) - - for axis in left._AXIS_ORDERS: - left_ind = getattr(left, axis) - right_ind = getattr(right, axis) - assert_index_equal(left_ind, right_ind, check_names=check_names) - - if by_blocks: - rblocks = right._to_dict_of_blocks() - lblocks = left._to_dict_of_blocks() - for dtype in list(set(list(lblocks.keys()) + list(rblocks.keys()))): - assert dtype in lblocks - assert dtype in rblocks - array_equivalent(lblocks[dtype].values, rblocks[dtype].values) - else: - - # can potentially be slow - for i, item in enumerate(left._get_axis(0)): - msg = "non-matching item (right) '{item}'".format(item=item) - assert item in right, msg - litem = left.iloc[i] - ritem = right.iloc[i] - assert_frame_equal(litem, ritem, - check_less_precise=check_less_precise, - check_names=check_names) - - for i, item in enumerate(right._get_axis(0)): - msg = "non-matching item (left) '{item}'".format(item=item) - assert item in left, msg - - def assert_equal(left, right, **kwargs): """ Wrapper for tm.assert_*_equal to dispatch to the appropriate test function. From 147b92381412c206a05c8e2a4e6c0f1d85e9b890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Wo=C5=9B?= Date: Mon, 11 Feb 2019 14:24:57 +0100 Subject: [PATCH 098/215] PR04 errors fix (#25157) --- ci/code_checks.sh | 2 +- pandas/_libs/tslibs/timedeltas.pyx | 7 ++++--- pandas/core/algorithms.py | 2 +- pandas/core/generic.py | 2 ++ pandas/core/groupby/groupby.py | 2 +- pandas/core/groupby/grouper.py | 17 ++++++++++------- pandas/core/panel.py | 5 +++-- pandas/core/resample.py | 2 +- pandas/io/formats/style.py | 8 +++++--- pandas/io/parquet.py | 11 ++++++----- 10 files changed, 34 insertions(+), 24 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index 5c9d20e483ce4..eacab199cc0be 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -242,7 +242,7 @@ fi if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, PR10, EX04, RT04, SS05, SA05)' ; echo $MSG - $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR05,EX04,RT04,SS05,SA05 + $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR04,PR05,EX04,RT04,SS05,SA05 RET=$(($RET + $?)) ; echo $MSG "DONE" fi diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index f08a57375a301..58b2faac8b06b 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -1127,10 +1127,11 @@ class Timedelta(_Timedelta): 'ms', 'milliseconds', 'millisecond', 'milli', 'millis', 'L', 'us', 'microseconds', 'microsecond', 'micro', 'micros', 'U', 'ns', 'nanoseconds', 'nano', 'nanos', 'nanosecond', 'N'} - days, seconds, microseconds, - milliseconds, minutes, hours, weeks : numeric, optional + **kwargs + Available kwargs: {days, seconds, microseconds, + milliseconds, minutes, hours, weeks}. Values for construction in compat with datetime.timedelta. - np ints and floats will be coerced to python ints and floats. + Numpy ints and floats will be coerced to python ints and floats. Notes ----- diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index a70a3ff06f202..77681f6ac3f93 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -563,7 +563,7 @@ def _factorize_array(values, na_sentinel=-1, size_hint=None, coerced to ndarrays before factorization. """), order=dedent("""\ - order + order : None .. deprecated:: 0.23.0 This parameter has no effect and is deprecated. diff --git a/pandas/core/generic.py b/pandas/core/generic.py index ef629361c291a..c886493f90eaf 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -648,6 +648,8 @@ def transpose(self, *args, **kwargs): copy : boolean, default False Make a copy of the underlying data. Mixed-dtype data will always result in a copy + **kwargs + Additional keyword arguments will be passed to the function. Returns ------- diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index c7f1aa697c2e8..c63bc5164e25b 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -1835,7 +1835,7 @@ def rank(self, method='average', ascending=True, na_option='keep', The axis of the object over which to compute the rank. Returns - ----- + ------- DataFrame with ranking of values within each group """ if na_option not in {'keep', 'top', 'bottom'}: diff --git a/pandas/core/groupby/grouper.py b/pandas/core/groupby/grouper.py index edba9439a675e..d1ebb9cbe8ac4 100644 --- a/pandas/core/groupby/grouper.py +++ b/pandas/core/groupby/grouper.py @@ -54,14 +54,17 @@ class Grouper(object): axis : number/name of the axis, defaults to 0 sort : boolean, default to False whether to sort the resulting labels - - additional kwargs to control time-like groupers (when `freq` is passed) - - closed : closed end of interval; 'left' or 'right' - label : interval boundary to use for labeling; 'left' or 'right' + closed : {'left' or 'right'} + Closed end of interval. Only when `freq` parameter is passed. + label : {'left' or 'right'} + Interval boundary to use for labeling. + Only when `freq` parameter is passed. convention : {'start', 'end', 'e', 's'} - If grouper is PeriodIndex - base, loffset + If grouper is PeriodIndex and `freq` parameter is passed. + base : int, default 0 + Only when `freq` parameter is passed. + loffset : string, DateOffset, timedelta object + Only when `freq` parameter is passed. Returns ------- diff --git a/pandas/core/panel.py b/pandas/core/panel.py index 16bcc17a6b4ea..dda5533f1ea7b 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -43,7 +43,7 @@ axes_single_arg="{0, 1, 2, 'items', 'major_axis', 'minor_axis'}", optional_mapper='', optional_axis='', optional_labels='') _shared_doc_kwargs['args_transpose'] = ( - "three positional arguments: each one of\n{ax_single}".format( + "{ax_single}\n\tThree positional arguments from given options.".format( ax_single=_shared_doc_kwargs['axes_single_arg'])) @@ -1009,7 +1009,8 @@ def apply(self, func, axis='major', **kwargs): DataFrames of items & major axis will be passed axis : {'items', 'minor', 'major'}, or {0, 1, 2}, or a tuple with two axes - Additional keyword arguments will be passed as keywords to the function + **kwargs + Additional keyword arguments will be passed to the function. Returns ------- diff --git a/pandas/core/resample.py b/pandas/core/resample.py index ff4dd7da15bd1..b3b28d7772713 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -795,7 +795,7 @@ def std(self, ddof=1, *args, **kwargs): Parameters ---------- ddof : integer, default 1 - degrees of freedom + Degrees of freedom. """ nv.validate_resampler_func('std', args, kwargs) return self._downsample('std', ddof=ddof) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index d241528d9779b..cd6e3505d71db 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -424,9 +424,11 @@ def render(self, **kwargs): Parameters ---------- - `**kwargs` : Any additional keyword arguments are passed through - to ``self.template.render``. This is useful when you need to provide - additional variables for a custom template. + **kwargs + Any additional keyword arguments are passed + through to ``self.template.render``. + This is useful when you need to provide + additional variables for a custom template. .. versionadded:: 0.20 diff --git a/pandas/io/parquet.py b/pandas/io/parquet.py index dada9000d901a..ba322f42c07c1 100644 --- a/pandas/io/parquet.py +++ b/pandas/io/parquet.py @@ -262,16 +262,17 @@ def read_parquet(path, engine='auto', columns=None, **kwargs): ---------- path : string File path - columns : list, default=None - If not None, only these columns will be read from the file. - - .. versionadded 0.21.1 engine : {'auto', 'pyarrow', 'fastparquet'}, default 'auto' Parquet library to use. If 'auto', then the option ``io.parquet.engine`` is used. The default ``io.parquet.engine`` behavior is to try 'pyarrow', falling back to 'fastparquet' if 'pyarrow' is unavailable. - kwargs are passed to the engine + columns : list, default=None + If not None, only these columns will be read from the file. + + .. versionadded 0.21.1 + **kwargs + Any additional kwargs are passed to the engine. Returns ------- From 6359bbc4c9ce6dd05bc8b422641cda74871cde43 Mon Sep 17 00:00:00 2001 From: William Ayd Date: Mon, 11 Feb 2019 05:26:30 -0800 Subject: [PATCH 099/215] Split Excel IO Into Sub-Directory (#25153) --- pandas/io/excel.py | 2028 -------------------------------- pandas/io/excel/__init__.py | 16 + pandas/io/excel/_base.py | 853 ++++++++++++++ pandas/io/excel/_openpyxl.py | 453 +++++++ pandas/io/excel/_util.py | 260 ++++ pandas/io/excel/_xlrd.py | 126 ++ pandas/io/excel/_xlsxwriter.py | 218 ++++ pandas/io/excel/_xlwt.py | 132 +++ 8 files changed, 2058 insertions(+), 2028 deletions(-) delete mode 100644 pandas/io/excel.py create mode 100644 pandas/io/excel/__init__.py create mode 100644 pandas/io/excel/_base.py create mode 100644 pandas/io/excel/_openpyxl.py create mode 100644 pandas/io/excel/_util.py create mode 100644 pandas/io/excel/_xlrd.py create mode 100644 pandas/io/excel/_xlsxwriter.py create mode 100644 pandas/io/excel/_xlwt.py diff --git a/pandas/io/excel.py b/pandas/io/excel.py deleted file mode 100644 index 9e5e9f4f0d4f6..0000000000000 --- a/pandas/io/excel.py +++ /dev/null @@ -1,2028 +0,0 @@ -""" -Module parse to/from Excel -""" - -# --------------------------------------------------------------------- -# ExcelFile class -import abc -from collections import OrderedDict -from datetime import date, datetime, time, timedelta -from distutils.version import LooseVersion -from io import UnsupportedOperation -import os -from textwrap import fill -import warnings - -import numpy as np - -import pandas._libs.json as json -import pandas.compat as compat -from pandas.compat import ( - add_metaclass, lrange, map, range, string_types, u, zip) -from pandas.errors import EmptyDataError -from pandas.util._decorators import Appender, deprecate_kwarg - -from pandas.core.dtypes.common import ( - is_bool, is_float, is_integer, is_list_like) - -from pandas.core import config -from pandas.core.frame import DataFrame - -from pandas.io.common import ( - _NA_VALUES, _is_url, _stringify_path, _urlopen, _validate_header_arg, - get_filepath_or_buffer) -from pandas.io.formats.printing import pprint_thing -from pandas.io.parsers import TextParser - -__all__ = ["read_excel", "ExcelWriter", "ExcelFile"] - -_writer_extensions = ["xlsx", "xls", "xlsm"] -_writers = {} - -_read_excel_doc = """ -Read an Excel file into a pandas DataFrame. - -Support both `xls` and `xlsx` file extensions from a local filesystem or URL. -Support an option to read a single sheet or a list of sheets. - -Parameters ----------- -io : str, file descriptor, pathlib.Path, ExcelFile or xlrd.Book - The string could be a URL. Valid URL schemes include http, ftp, s3, - gcs, and file. For file URLs, a host is expected. For instance, a local - file could be /path/to/workbook.xlsx. -sheet_name : str, int, list, or None, default 0 - Strings are used for sheet names. Integers are used in zero-indexed - sheet positions. Lists of strings/integers are used to request - multiple sheets. Specify None to get all sheets. - - Available cases: - - * Defaults to ``0``: 1st sheet as a `DataFrame` - * ``1``: 2nd sheet as a `DataFrame` - * ``"Sheet1"``: Load sheet with name "Sheet1" - * ``[0, 1, "Sheet5"]``: Load first, second and sheet named "Sheet5" - as a dict of `DataFrame` - * None: All sheets. - -header : int, list of int, default 0 - Row (0-indexed) to use for the column labels of the parsed - DataFrame. If a list of integers is passed those row positions will - be combined into a ``MultiIndex``. Use None if there is no header. -names : array-like, default None - List of column names to use. If file contains no header row, - then you should explicitly pass header=None. -index_col : int, list of int, default None - Column (0-indexed) to use as the row labels of the DataFrame. - Pass None if there is no such column. If a list is passed, - those columns will be combined into a ``MultiIndex``. If a - subset of data is selected with ``usecols``, index_col - is based on the subset. -parse_cols : int or list, default None - Alias of `usecols`. - - .. deprecated:: 0.21.0 - Use `usecols` instead. - -usecols : int, str, list-like, or callable default None - Return a subset of the columns. - * If None, then parse all columns. - * If int, then indicates last column to be parsed. - - .. deprecated:: 0.24.0 - Pass in a list of int instead from 0 to `usecols` inclusive. - - * If str, then indicates comma separated list of Excel column letters - and column ranges (e.g. "A:E" or "A,C,E:F"). Ranges are inclusive of - both sides. - * If list of int, then indicates list of column numbers to be parsed. - * If list of string, then indicates list of column names to be parsed. - - .. versionadded:: 0.24.0 - - * If callable, then evaluate each column name against it and parse the - column if the callable returns ``True``. - - .. versionadded:: 0.24.0 - -squeeze : bool, default False - If the parsed data only contains one column then return a Series. -dtype : Type name or dict of column -> type, default None - Data type for data or columns. E.g. {'a': np.float64, 'b': np.int32} - Use `object` to preserve data as stored in Excel and not interpret dtype. - If converters are specified, they will be applied INSTEAD - of dtype conversion. - - .. versionadded:: 0.20.0 - -engine : str, default None - If io is not a buffer or path, this must be set to identify io. - Acceptable values are None or xlrd. -converters : dict, default None - Dict of functions for converting values in certain columns. Keys can - either be integers or column labels, values are functions that take one - input argument, the Excel cell content, and return the transformed - content. -true_values : list, default None - Values to consider as True. - - .. versionadded:: 0.19.0 - -false_values : list, default None - Values to consider as False. - - .. versionadded:: 0.19.0 - -skiprows : list-like - Rows to skip at the beginning (0-indexed). -nrows : int, default None - Number of rows to parse. - - .. versionadded:: 0.23.0 - -na_values : scalar, str, list-like, or dict, default None - Additional strings to recognize as NA/NaN. If dict passed, specific - per-column NA values. By default the following values are interpreted - as NaN: '""" + fill("', '".join(sorted(_NA_VALUES)), 70, subsequent_indent=" ") + """'. -keep_default_na : bool, default True - If na_values are specified and keep_default_na is False the default NaN - values are overridden, otherwise they're appended to. -verbose : bool, default False - Indicate number of NA values placed in non-numeric columns. -parse_dates : bool, list-like, or dict, default False - The behavior is as follows: - - * bool. If True -> try parsing the index. - * list of int or names. e.g. If [1, 2, 3] -> try parsing columns 1, 2, 3 - each as a separate date column. - * list of lists. e.g. If [[1, 3]] -> combine columns 1 and 3 and parse as - a single date column. - * dict, e.g. {{'foo' : [1, 3]}} -> parse columns 1, 3 as date and call - result 'foo' - - If a column or index contains an unparseable date, the entire column or - index will be returned unaltered as an object data type. For non-standard - datetime parsing, use ``pd.to_datetime`` after ``pd.read_csv`` - - Note: A fast-path exists for iso8601-formatted dates. -date_parser : function, optional - Function to use for converting a sequence of string columns to an array of - datetime instances. The default uses ``dateutil.parser.parser`` to do the - conversion. Pandas will try to call `date_parser` in three different ways, - advancing to the next if an exception occurs: 1) Pass one or more arrays - (as defined by `parse_dates`) as arguments; 2) concatenate (row-wise) the - string values from the columns defined by `parse_dates` into a single array - and pass that; and 3) call `date_parser` once for each row using one or - more strings (corresponding to the columns defined by `parse_dates`) as - arguments. -thousands : str, default None - Thousands separator for parsing string columns to numeric. Note that - this parameter is only necessary for columns stored as TEXT in Excel, - any numeric columns will automatically be parsed, regardless of display - format. -comment : str, default None - Comments out remainder of line. Pass a character or characters to this - argument to indicate comments in the input file. Any data between the - comment string and the end of the current line is ignored. -skip_footer : int, default 0 - Alias of `skipfooter`. - - .. deprecated:: 0.23.0 - Use `skipfooter` instead. -skipfooter : int, default 0 - Rows at the end to skip (0-indexed). -convert_float : bool, default True - Convert integral floats to int (i.e., 1.0 --> 1). If False, all numeric - data will be read in as floats: Excel stores all numbers as floats - internally. -mangle_dupe_cols : bool, default True - Duplicate columns will be specified as 'X', 'X.1', ...'X.N', rather than - 'X'...'X'. Passing in False will cause data to be overwritten if there - are duplicate names in the columns. -**kwds : optional - Optional keyword arguments can be passed to ``TextFileReader``. - -Returns -------- -DataFrame or dict of DataFrames - DataFrame from the passed in Excel file. See notes in sheet_name - argument for more information on when a dict of DataFrames is returned. - -See Also --------- -to_excel : Write DataFrame to an Excel file. -to_csv : Write DataFrame to a comma-separated values (csv) file. -read_csv : Read a comma-separated values (csv) file into DataFrame. -read_fwf : Read a table of fixed-width formatted lines into DataFrame. - -Examples --------- -The file can be read using the file name as string or an open file object: - ->>> pd.read_excel('tmp.xlsx', index_col=0) # doctest: +SKIP - Name Value -0 string1 1 -1 string2 2 -2 #Comment 3 - ->>> pd.read_excel(open('tmp.xlsx', 'rb'), -... sheet_name='Sheet3') # doctest: +SKIP - Unnamed: 0 Name Value -0 0 string1 1 -1 1 string2 2 -2 2 #Comment 3 - -Index and header can be specified via the `index_col` and `header` arguments - ->>> pd.read_excel('tmp.xlsx', index_col=None, header=None) # doctest: +SKIP - 0 1 2 -0 NaN Name Value -1 0.0 string1 1 -2 1.0 string2 2 -3 2.0 #Comment 3 - -Column types are inferred but can be explicitly specified - ->>> pd.read_excel('tmp.xlsx', index_col=0, -... dtype={'Name': str, 'Value': float}) # doctest: +SKIP - Name Value -0 string1 1.0 -1 string2 2.0 -2 #Comment 3.0 - -True, False, and NA values, and thousands separators have defaults, -but can be explicitly specified, too. Supply the values you would like -as strings or lists of strings! - ->>> pd.read_excel('tmp.xlsx', index_col=0, -... na_values=['string1', 'string2']) # doctest: +SKIP - Name Value -0 NaN 1 -1 NaN 2 -2 #Comment 3 - -Comment lines in the excel input file can be skipped using the `comment` kwarg - ->>> pd.read_excel('tmp.xlsx', index_col=0, comment='#') # doctest: +SKIP - Name Value -0 string1 1.0 -1 string2 2.0 -2 None NaN -""" - - -def register_writer(klass): - """Adds engine to the excel writer registry. You must use this method to - integrate with ``to_excel``. Also adds config options for any new - ``supported_extensions`` defined on the writer.""" - if not callable(klass): - raise ValueError("Can only register callables as engines") - engine_name = klass.engine - _writers[engine_name] = klass - for ext in klass.supported_extensions: - if ext.startswith('.'): - ext = ext[1:] - if ext not in _writer_extensions: - config.register_option("io.excel.{ext}.writer".format(ext=ext), - engine_name, validator=str) - _writer_extensions.append(ext) - - -def _get_default_writer(ext): - _default_writers = {'xlsx': 'openpyxl', 'xlsm': 'openpyxl', 'xls': 'xlwt'} - try: - import xlsxwriter # noqa - _default_writers['xlsx'] = 'xlsxwriter' - except ImportError: - pass - return _default_writers[ext] - - -def get_writer(engine_name): - try: - return _writers[engine_name] - except KeyError: - raise ValueError("No Excel writer '{engine}'" - .format(engine=engine_name)) - - -@Appender(_read_excel_doc) -@deprecate_kwarg("parse_cols", "usecols") -@deprecate_kwarg("skip_footer", "skipfooter") -def read_excel(io, - sheet_name=0, - header=0, - names=None, - index_col=None, - parse_cols=None, - usecols=None, - squeeze=False, - dtype=None, - engine=None, - converters=None, - true_values=None, - false_values=None, - skiprows=None, - nrows=None, - na_values=None, - keep_default_na=True, - verbose=False, - parse_dates=False, - date_parser=None, - thousands=None, - comment=None, - skip_footer=0, - skipfooter=0, - convert_float=True, - mangle_dupe_cols=True, - **kwds): - - # Can't use _deprecate_kwarg since sheetname=None has a special meaning - if is_integer(sheet_name) and sheet_name == 0 and 'sheetname' in kwds: - warnings.warn("The `sheetname` keyword is deprecated, use " - "`sheet_name` instead", FutureWarning, stacklevel=2) - sheet_name = kwds.pop("sheetname") - - if 'sheet' in kwds: - raise TypeError("read_excel() got an unexpected keyword argument " - "`sheet`") - - if not isinstance(io, ExcelFile): - io = ExcelFile(io, engine=engine) - - return io.parse( - sheet_name=sheet_name, - header=header, - names=names, - index_col=index_col, - usecols=usecols, - squeeze=squeeze, - dtype=dtype, - converters=converters, - true_values=true_values, - false_values=false_values, - skiprows=skiprows, - nrows=nrows, - na_values=na_values, - keep_default_na=keep_default_na, - verbose=verbose, - parse_dates=parse_dates, - date_parser=date_parser, - thousands=thousands, - comment=comment, - skipfooter=skipfooter, - convert_float=convert_float, - mangle_dupe_cols=mangle_dupe_cols, - **kwds) - - -@add_metaclass(abc.ABCMeta) -class _BaseExcelReader(object): - - @property - @abc.abstractmethod - def sheet_names(self): - pass - - @abc.abstractmethod - def get_sheet_by_name(self, name): - pass - - @abc.abstractmethod - def get_sheet_by_index(self, index): - pass - - @abc.abstractmethod - def get_sheet_data(self, sheet, convert_float): - pass - - def parse(self, - sheet_name=0, - header=0, - names=None, - index_col=None, - usecols=None, - squeeze=False, - dtype=None, - true_values=None, - false_values=None, - skiprows=None, - nrows=None, - na_values=None, - verbose=False, - parse_dates=False, - date_parser=None, - thousands=None, - comment=None, - skipfooter=0, - convert_float=True, - mangle_dupe_cols=True, - **kwds): - - _validate_header_arg(header) - - ret_dict = False - - # Keep sheetname to maintain backwards compatibility. - if isinstance(sheet_name, list): - sheets = sheet_name - ret_dict = True - elif sheet_name is None: - sheets = self.sheet_names - ret_dict = True - else: - sheets = [sheet_name] - - # handle same-type duplicates. - sheets = list(OrderedDict.fromkeys(sheets).keys()) - - output = OrderedDict() - - for asheetname in sheets: - if verbose: - print("Reading sheet {sheet}".format(sheet=asheetname)) - - if isinstance(asheetname, compat.string_types): - sheet = self.get_sheet_by_name(asheetname) - else: # assume an integer if not a string - sheet = self.get_sheet_by_index(asheetname) - - data = self.get_sheet_data(sheet, convert_float) - usecols = _maybe_convert_usecols(usecols) - - if sheet.nrows == 0: - output[asheetname] = DataFrame() - continue - - if is_list_like(header) and len(header) == 1: - header = header[0] - - # forward fill and pull out names for MultiIndex column - header_names = None - if header is not None and is_list_like(header): - header_names = [] - control_row = [True] * len(data[0]) - - for row in header: - if is_integer(skiprows): - row += skiprows - - data[row], control_row = _fill_mi_header(data[row], - control_row) - - if index_col is not None: - header_name, _ = _pop_header_name(data[row], index_col) - header_names.append(header_name) - - if is_list_like(index_col): - # Forward fill values for MultiIndex index. - if not is_list_like(header): - offset = 1 + header - else: - offset = 1 + max(header) - - # Check if we have an empty dataset - # before trying to collect data. - if offset < len(data): - for col in index_col: - last = data[offset][col] - - for row in range(offset + 1, len(data)): - if data[row][col] == '' or data[row][col] is None: - data[row][col] = last - else: - last = data[row][col] - - has_index_names = is_list_like(header) and len(header) > 1 - - # GH 12292 : error when read one empty column from excel file - try: - parser = TextParser(data, - names=names, - header=header, - index_col=index_col, - has_index_names=has_index_names, - squeeze=squeeze, - dtype=dtype, - true_values=true_values, - false_values=false_values, - skiprows=skiprows, - nrows=nrows, - na_values=na_values, - parse_dates=parse_dates, - date_parser=date_parser, - thousands=thousands, - comment=comment, - skipfooter=skipfooter, - usecols=usecols, - mangle_dupe_cols=mangle_dupe_cols, - **kwds) - - output[asheetname] = parser.read(nrows=nrows) - - if not squeeze or isinstance(output[asheetname], DataFrame): - if header_names: - output[asheetname].columns = output[ - asheetname].columns.set_names(header_names) - elif compat.PY2: - output[asheetname].columns = _maybe_convert_to_string( - output[asheetname].columns) - - except EmptyDataError: - # No Data, return an empty DataFrame - output[asheetname] = DataFrame() - - if ret_dict: - return output - else: - return output[asheetname] - - -class _XlrdReader(_BaseExcelReader): - - def __init__(self, filepath_or_buffer): - """Reader using xlrd engine. - - Parameters - ---------- - filepath_or_buffer : string, path object or Workbook - Object to be parsed. - """ - err_msg = "Install xlrd >= 1.0.0 for Excel support" - - try: - import xlrd - except ImportError: - raise ImportError(err_msg) - else: - if xlrd.__VERSION__ < LooseVersion("1.0.0"): - raise ImportError(err_msg + - ". Current version " + xlrd.__VERSION__) - - # If filepath_or_buffer is a url, want to keep the data as bytes so - # can't pass to get_filepath_or_buffer() - if _is_url(filepath_or_buffer): - filepath_or_buffer = _urlopen(filepath_or_buffer) - elif not isinstance(filepath_or_buffer, (ExcelFile, xlrd.Book)): - filepath_or_buffer, _, _, _ = get_filepath_or_buffer( - filepath_or_buffer) - - if isinstance(filepath_or_buffer, xlrd.Book): - self.book = filepath_or_buffer - elif hasattr(filepath_or_buffer, "read"): - # N.B. xlrd.Book has a read attribute too - if hasattr(filepath_or_buffer, 'seek'): - try: - # GH 19779 - filepath_or_buffer.seek(0) - except UnsupportedOperation: - # HTTPResponse does not support seek() - # GH 20434 - pass - - data = filepath_or_buffer.read() - self.book = xlrd.open_workbook(file_contents=data) - elif isinstance(filepath_or_buffer, compat.string_types): - self.book = xlrd.open_workbook(filepath_or_buffer) - else: - raise ValueError('Must explicitly set engine if not passing in' - ' buffer or path for io.') - - @property - def sheet_names(self): - return self.book.sheet_names() - - def get_sheet_by_name(self, name): - return self.book.sheet_by_name(name) - - def get_sheet_by_index(self, index): - return self.book.sheet_by_index(index) - - def get_sheet_data(self, sheet, convert_float): - from xlrd import (xldate, XL_CELL_DATE, - XL_CELL_ERROR, XL_CELL_BOOLEAN, - XL_CELL_NUMBER) - - epoch1904 = self.book.datemode - - def _parse_cell(cell_contents, cell_typ): - """converts the contents of the cell into a pandas - appropriate object""" - - if cell_typ == XL_CELL_DATE: - - # Use the newer xlrd datetime handling. - try: - cell_contents = xldate.xldate_as_datetime( - cell_contents, epoch1904) - except OverflowError: - return cell_contents - - # Excel doesn't distinguish between dates and time, - # so we treat dates on the epoch as times only. - # Also, Excel supports 1900 and 1904 epochs. - year = (cell_contents.timetuple())[0:3] - if ((not epoch1904 and year == (1899, 12, 31)) or - (epoch1904 and year == (1904, 1, 1))): - cell_contents = time(cell_contents.hour, - cell_contents.minute, - cell_contents.second, - cell_contents.microsecond) - - elif cell_typ == XL_CELL_ERROR: - cell_contents = np.nan - elif cell_typ == XL_CELL_BOOLEAN: - cell_contents = bool(cell_contents) - elif convert_float and cell_typ == XL_CELL_NUMBER: - # GH5394 - Excel 'numbers' are always floats - # it's a minimal perf hit and less surprising - val = int(cell_contents) - if val == cell_contents: - cell_contents = val - return cell_contents - - data = [] - - for i in range(sheet.nrows): - row = [_parse_cell(value, typ) - for value, typ in zip(sheet.row_values(i), - sheet.row_types(i))] - data.append(row) - - return data - - -class ExcelFile(object): - """ - Class for parsing tabular excel sheets into DataFrame objects. - Uses xlrd. See read_excel for more documentation - - Parameters - ---------- - io : string, path object (pathlib.Path or py._path.local.LocalPath), - file-like object or xlrd workbook - If a string or path object, expected to be a path to xls or xlsx file. - engine : string, default None - If io is not a buffer or path, this must be set to identify io. - Acceptable values are None or ``xlrd``. - """ - - _engines = { - 'xlrd': _XlrdReader, - } - - def __init__(self, io, engine=None): - if engine is None: - engine = 'xlrd' - if engine not in self._engines: - raise ValueError("Unknown engine: {engine}".format(engine=engine)) - - # could be a str, ExcelFile, Book, etc. - self.io = io - # Always a string - self._io = _stringify_path(io) - - self._reader = self._engines[engine](self._io) - - def __fspath__(self): - return self._io - - def parse(self, - sheet_name=0, - header=0, - names=None, - index_col=None, - usecols=None, - squeeze=False, - converters=None, - true_values=None, - false_values=None, - skiprows=None, - nrows=None, - na_values=None, - parse_dates=False, - date_parser=None, - thousands=None, - comment=None, - skipfooter=0, - convert_float=True, - mangle_dupe_cols=True, - **kwds): - """ - Parse specified sheet(s) into a DataFrame - - Equivalent to read_excel(ExcelFile, ...) See the read_excel - docstring for more info on accepted parameters - """ - - # Can't use _deprecate_kwarg since sheetname=None has a special meaning - if is_integer(sheet_name) and sheet_name == 0 and 'sheetname' in kwds: - warnings.warn("The `sheetname` keyword is deprecated, use " - "`sheet_name` instead", FutureWarning, stacklevel=2) - sheet_name = kwds.pop("sheetname") - elif 'sheetname' in kwds: - raise TypeError("Cannot specify both `sheet_name` " - "and `sheetname`. Use just `sheet_name`") - - if 'chunksize' in kwds: - raise NotImplementedError("chunksize keyword of read_excel " - "is not implemented") - - return self._reader.parse(sheet_name=sheet_name, - header=header, - names=names, - index_col=index_col, - usecols=usecols, - squeeze=squeeze, - converters=converters, - true_values=true_values, - false_values=false_values, - skiprows=skiprows, - nrows=nrows, - na_values=na_values, - parse_dates=parse_dates, - date_parser=date_parser, - thousands=thousands, - comment=comment, - skipfooter=skipfooter, - convert_float=convert_float, - mangle_dupe_cols=mangle_dupe_cols, - **kwds) - - @property - def book(self): - return self._reader.book - - @property - def sheet_names(self): - return self._reader.sheet_names - - def close(self): - """close io if necessary""" - if hasattr(self.io, 'close'): - self.io.close() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - -def _excel2num(x): - """ - Convert Excel column name like 'AB' to 0-based column index. - - Parameters - ---------- - x : str - The Excel column name to convert to a 0-based column index. - - Returns - ------- - num : int - The column index corresponding to the name. - - Raises - ------ - ValueError - Part of the Excel column name was invalid. - """ - index = 0 - - for c in x.upper().strip(): - cp = ord(c) - - if cp < ord("A") or cp > ord("Z"): - raise ValueError("Invalid column name: {x}".format(x=x)) - - index = index * 26 + cp - ord("A") + 1 - - return index - 1 - - -def _range2cols(areas): - """ - Convert comma separated list of column names and ranges to indices. - - Parameters - ---------- - areas : str - A string containing a sequence of column ranges (or areas). - - Returns - ------- - cols : list - A list of 0-based column indices. - - Examples - -------- - >>> _range2cols('A:E') - [0, 1, 2, 3, 4] - >>> _range2cols('A,C,Z:AB') - [0, 2, 25, 26, 27] - """ - cols = [] - - for rng in areas.split(","): - if ":" in rng: - rng = rng.split(":") - cols.extend(lrange(_excel2num(rng[0]), _excel2num(rng[1]) + 1)) - else: - cols.append(_excel2num(rng)) - - return cols - - -def _maybe_convert_usecols(usecols): - """ - Convert `usecols` into a compatible format for parsing in `parsers.py`. - - Parameters - ---------- - usecols : object - The use-columns object to potentially convert. - - Returns - ------- - converted : object - The compatible format of `usecols`. - """ - if usecols is None: - return usecols - - if is_integer(usecols): - warnings.warn(("Passing in an integer for `usecols` has been " - "deprecated. Please pass in a list of int from " - "0 to `usecols` inclusive instead."), - FutureWarning, stacklevel=2) - return lrange(usecols + 1) - - if isinstance(usecols, compat.string_types): - return _range2cols(usecols) - - return usecols - - -def _validate_freeze_panes(freeze_panes): - if freeze_panes is not None: - if ( - len(freeze_panes) == 2 and - all(isinstance(item, int) for item in freeze_panes) - ): - return True - - raise ValueError("freeze_panes must be of form (row, column)" - " where row and column are integers") - - # freeze_panes wasn't specified, return False so it won't be applied - # to output sheet - return False - - -def _trim_excel_header(row): - # trim header row so auto-index inference works - # xlrd uses '' , openpyxl None - while len(row) > 0 and (row[0] == '' or row[0] is None): - row = row[1:] - return row - - -def _maybe_convert_to_string(row): - """ - Convert elements in a row to string from Unicode. - - This is purely a Python 2.x patch and is performed ONLY when all - elements of the row are string-like. - - Parameters - ---------- - row : array-like - The row of data to convert. - - Returns - ------- - converted : array-like - """ - if compat.PY2: - converted = [] - - for i in range(len(row)): - if isinstance(row[i], compat.string_types): - try: - converted.append(str(row[i])) - except UnicodeEncodeError: - break - else: - break - else: - row = converted - - return row - - -def _fill_mi_header(row, control_row): - """Forward fills blank entries in row, but only inside the same parent index - - Used for creating headers in Multiindex. - Parameters - ---------- - row : list - List of items in a single row. - control_row : list of bool - Helps to determine if particular column is in same parent index as the - previous value. Used to stop propagation of empty cells between - different indexes. - - Returns - ---------- - Returns changed row and control_row - """ - last = row[0] - for i in range(1, len(row)): - if not control_row[i]: - last = row[i] - - if row[i] == '' or row[i] is None: - row[i] = last - else: - control_row[i] = False - last = row[i] - - return _maybe_convert_to_string(row), control_row - -# fill blank if index_col not None - - -def _pop_header_name(row, index_col): - """ - Pop the header name for MultiIndex parsing. - - Parameters - ---------- - row : list - The data row to parse for the header name. - index_col : int, list - The index columns for our data. Assumed to be non-null. - - Returns - ------- - header_name : str - The extracted header name. - trimmed_row : list - The original data row with the header name removed. - """ - # Pop out header name and fill w/blank. - i = index_col if not is_list_like(index_col) else max(index_col) - - header_name = row[i] - header_name = None if header_name == "" else header_name - - return header_name, row[:i] + [''] + row[i + 1:] - - -@add_metaclass(abc.ABCMeta) -class ExcelWriter(object): - """ - Class for writing DataFrame objects into excel sheets, default is to use - xlwt for xls, openpyxl for xlsx. See DataFrame.to_excel for typical usage. - - Parameters - ---------- - path : string - Path to xls or xlsx file. - engine : string (optional) - Engine to use for writing. If None, defaults to - ``io.excel..writer``. NOTE: can only be passed as a keyword - argument. - date_format : string, default None - Format string for dates written into Excel files (e.g. 'YYYY-MM-DD') - datetime_format : string, default None - Format string for datetime objects written into Excel files - (e.g. 'YYYY-MM-DD HH:MM:SS') - mode : {'w' or 'a'}, default 'w' - File mode to use (write or append). - - .. versionadded:: 0.24.0 - - Attributes - ---------- - None - - Methods - ------- - None - - Notes - ----- - None of the methods and properties are considered public. - - For compatibility with CSV writers, ExcelWriter serializes lists - and dicts to strings before writing. - - Examples - -------- - Default usage: - - >>> with ExcelWriter('path_to_file.xlsx') as writer: - ... df.to_excel(writer) - - To write to separate sheets in a single file: - - >>> with ExcelWriter('path_to_file.xlsx') as writer: - ... df1.to_excel(writer, sheet_name='Sheet1') - ... df2.to_excel(writer, sheet_name='Sheet2') - - You can set the date format or datetime format: - - >>> with ExcelWriter('path_to_file.xlsx', - date_format='YYYY-MM-DD', - datetime_format='YYYY-MM-DD HH:MM:SS') as writer: - ... df.to_excel(writer) - - You can also append to an existing Excel file: - - >>> with ExcelWriter('path_to_file.xlsx', mode='a') as writer: - ... df.to_excel(writer, sheet_name='Sheet3') - """ - # Defining an ExcelWriter implementation (see abstract methods for more...) - - # - Mandatory - # - ``write_cells(self, cells, sheet_name=None, startrow=0, startcol=0)`` - # --> called to write additional DataFrames to disk - # - ``supported_extensions`` (tuple of supported extensions), used to - # check that engine supports the given extension. - # - ``engine`` - string that gives the engine name. Necessary to - # instantiate class directly and bypass ``ExcelWriterMeta`` engine - # lookup. - # - ``save(self)`` --> called to save file to disk - # - Mostly mandatory (i.e. should at least exist) - # - book, cur_sheet, path - - # - Optional: - # - ``__init__(self, path, engine=None, **kwargs)`` --> always called - # with path as first argument. - - # You also need to register the class with ``register_writer()``. - # Technically, ExcelWriter implementations don't need to subclass - # ExcelWriter. - def __new__(cls, path, engine=None, **kwargs): - # only switch class if generic(ExcelWriter) - - if issubclass(cls, ExcelWriter): - if engine is None or (isinstance(engine, string_types) and - engine == 'auto'): - if isinstance(path, string_types): - ext = os.path.splitext(path)[-1][1:] - else: - ext = 'xlsx' - - try: - engine = config.get_option('io.excel.{ext}.writer' - .format(ext=ext)) - if engine == 'auto': - engine = _get_default_writer(ext) - except KeyError: - error = ValueError("No engine for filetype: '{ext}'" - .format(ext=ext)) - raise error - cls = get_writer(engine) - - return object.__new__(cls) - - # declare external properties you can count on - book = None - curr_sheet = None - path = None - - @abc.abstractproperty - def supported_extensions(self): - "extensions that writer engine supports" - pass - - @abc.abstractproperty - def engine(self): - "name of engine" - pass - - @abc.abstractmethod - def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, - freeze_panes=None): - """ - Write given formatted cells into Excel an excel sheet - - Parameters - ---------- - cells : generator - cell of formatted data to save to Excel sheet - sheet_name : string, default None - Name of Excel sheet, if None, then use self.cur_sheet - startrow : upper left cell row to dump data frame - startcol : upper left cell column to dump data frame - freeze_panes: integer tuple of length 2 - contains the bottom-most row and right-most column to freeze - """ - pass - - @abc.abstractmethod - def save(self): - """ - Save workbook to disk. - """ - pass - - def __init__(self, path, engine=None, - date_format=None, datetime_format=None, mode='w', - **engine_kwargs): - # validate that this engine can handle the extension - if isinstance(path, string_types): - ext = os.path.splitext(path)[-1] - else: - ext = 'xls' if engine == 'xlwt' else 'xlsx' - - self.check_extension(ext) - - self.path = path - self.sheets = {} - self.cur_sheet = None - - if date_format is None: - self.date_format = 'YYYY-MM-DD' - else: - self.date_format = date_format - if datetime_format is None: - self.datetime_format = 'YYYY-MM-DD HH:MM:SS' - else: - self.datetime_format = datetime_format - - self.mode = mode - - def __fspath__(self): - return _stringify_path(self.path) - - def _get_sheet_name(self, sheet_name): - if sheet_name is None: - sheet_name = self.cur_sheet - if sheet_name is None: # pragma: no cover - raise ValueError('Must pass explicit sheet_name or set ' - 'cur_sheet property') - return sheet_name - - def _value_with_fmt(self, val): - """Convert numpy types to Python types for the Excel writers. - - Parameters - ---------- - val : object - Value to be written into cells - - Returns - ------- - Tuple with the first element being the converted value and the second - being an optional format - """ - fmt = None - - if is_integer(val): - val = int(val) - elif is_float(val): - val = float(val) - elif is_bool(val): - val = bool(val) - elif isinstance(val, datetime): - fmt = self.datetime_format - elif isinstance(val, date): - fmt = self.date_format - elif isinstance(val, timedelta): - val = val.total_seconds() / float(86400) - fmt = '0' - else: - val = compat.to_str(val) - - return val, fmt - - @classmethod - def check_extension(cls, ext): - """checks that path's extension against the Writer's supported - extensions. If it isn't supported, raises UnsupportedFiletypeError.""" - if ext.startswith('.'): - ext = ext[1:] - if not any(ext in extension for extension in cls.supported_extensions): - msg = (u("Invalid extension for engine '{engine}': '{ext}'") - .format(engine=pprint_thing(cls.engine), - ext=pprint_thing(ext))) - raise ValueError(msg) - else: - return True - - # Allow use as a contextmanager - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - def close(self): - """synonym for save, to make it more file-like""" - return self.save() - - -class _OpenpyxlWriter(ExcelWriter): - engine = 'openpyxl' - supported_extensions = ('.xlsx', '.xlsm') - - def __init__(self, path, engine=None, mode='w', **engine_kwargs): - # Use the openpyxl module as the Excel writer. - from openpyxl.workbook import Workbook - - super(_OpenpyxlWriter, self).__init__(path, mode=mode, **engine_kwargs) - - if self.mode == 'a': # Load from existing workbook - from openpyxl import load_workbook - book = load_workbook(self.path) - self.book = book - else: - # Create workbook object with default optimized_write=True. - self.book = Workbook() - - if self.book.worksheets: - try: - self.book.remove(self.book.worksheets[0]) - except AttributeError: - - # compat - for openpyxl <= 2.4 - self.book.remove_sheet(self.book.worksheets[0]) - - def save(self): - """ - Save workbook to disk. - """ - return self.book.save(self.path) - - @classmethod - def _convert_to_style(cls, style_dict): - """ - converts a style_dict to an openpyxl style object - Parameters - ---------- - style_dict : style dictionary to convert - """ - - from openpyxl.style import Style - xls_style = Style() - for key, value in style_dict.items(): - for nk, nv in value.items(): - if key == "borders": - (xls_style.borders.__getattribute__(nk) - .__setattr__('border_style', nv)) - else: - xls_style.__getattribute__(key).__setattr__(nk, nv) - - return xls_style - - @classmethod - def _convert_to_style_kwargs(cls, style_dict): - """ - Convert a style_dict to a set of kwargs suitable for initializing - or updating-on-copy an openpyxl v2 style object - Parameters - ---------- - style_dict : dict - A dict with zero or more of the following keys (or their synonyms). - 'font' - 'fill' - 'border' ('borders') - 'alignment' - 'number_format' - 'protection' - Returns - ------- - style_kwargs : dict - A dict with the same, normalized keys as ``style_dict`` but each - value has been replaced with a native openpyxl style object of the - appropriate class. - """ - - _style_key_map = { - 'borders': 'border', - } - - style_kwargs = {} - for k, v in style_dict.items(): - if k in _style_key_map: - k = _style_key_map[k] - _conv_to_x = getattr(cls, '_convert_to_{k}'.format(k=k), - lambda x: None) - new_v = _conv_to_x(v) - if new_v: - style_kwargs[k] = new_v - - return style_kwargs - - @classmethod - def _convert_to_color(cls, color_spec): - """ - Convert ``color_spec`` to an openpyxl v2 Color object - Parameters - ---------- - color_spec : str, dict - A 32-bit ARGB hex string, or a dict with zero or more of the - following keys. - 'rgb' - 'indexed' - 'auto' - 'theme' - 'tint' - 'index' - 'type' - Returns - ------- - color : openpyxl.styles.Color - """ - - from openpyxl.styles import Color - - if isinstance(color_spec, str): - return Color(color_spec) - else: - return Color(**color_spec) - - @classmethod - def _convert_to_font(cls, font_dict): - """ - Convert ``font_dict`` to an openpyxl v2 Font object - Parameters - ---------- - font_dict : dict - A dict with zero or more of the following keys (or their synonyms). - 'name' - 'size' ('sz') - 'bold' ('b') - 'italic' ('i') - 'underline' ('u') - 'strikethrough' ('strike') - 'color' - 'vertAlign' ('vertalign') - 'charset' - 'scheme' - 'family' - 'outline' - 'shadow' - 'condense' - Returns - ------- - font : openpyxl.styles.Font - """ - - from openpyxl.styles import Font - - _font_key_map = { - 'sz': 'size', - 'b': 'bold', - 'i': 'italic', - 'u': 'underline', - 'strike': 'strikethrough', - 'vertalign': 'vertAlign', - } - - font_kwargs = {} - for k, v in font_dict.items(): - if k in _font_key_map: - k = _font_key_map[k] - if k == 'color': - v = cls._convert_to_color(v) - font_kwargs[k] = v - - return Font(**font_kwargs) - - @classmethod - def _convert_to_stop(cls, stop_seq): - """ - Convert ``stop_seq`` to a list of openpyxl v2 Color objects, - suitable for initializing the ``GradientFill`` ``stop`` parameter. - Parameters - ---------- - stop_seq : iterable - An iterable that yields objects suitable for consumption by - ``_convert_to_color``. - Returns - ------- - stop : list of openpyxl.styles.Color - """ - - return map(cls._convert_to_color, stop_seq) - - @classmethod - def _convert_to_fill(cls, fill_dict): - """ - Convert ``fill_dict`` to an openpyxl v2 Fill object - Parameters - ---------- - fill_dict : dict - A dict with one or more of the following keys (or their synonyms), - 'fill_type' ('patternType', 'patterntype') - 'start_color' ('fgColor', 'fgcolor') - 'end_color' ('bgColor', 'bgcolor') - or one or more of the following keys (or their synonyms). - 'type' ('fill_type') - 'degree' - 'left' - 'right' - 'top' - 'bottom' - 'stop' - Returns - ------- - fill : openpyxl.styles.Fill - """ - - from openpyxl.styles import PatternFill, GradientFill - - _pattern_fill_key_map = { - 'patternType': 'fill_type', - 'patterntype': 'fill_type', - 'fgColor': 'start_color', - 'fgcolor': 'start_color', - 'bgColor': 'end_color', - 'bgcolor': 'end_color', - } - - _gradient_fill_key_map = { - 'fill_type': 'type', - } - - pfill_kwargs = {} - gfill_kwargs = {} - for k, v in fill_dict.items(): - pk = gk = None - if k in _pattern_fill_key_map: - pk = _pattern_fill_key_map[k] - if k in _gradient_fill_key_map: - gk = _gradient_fill_key_map[k] - if pk in ['start_color', 'end_color']: - v = cls._convert_to_color(v) - if gk == 'stop': - v = cls._convert_to_stop(v) - if pk: - pfill_kwargs[pk] = v - elif gk: - gfill_kwargs[gk] = v - else: - pfill_kwargs[k] = v - gfill_kwargs[k] = v - - try: - return PatternFill(**pfill_kwargs) - except TypeError: - return GradientFill(**gfill_kwargs) - - @classmethod - def _convert_to_side(cls, side_spec): - """ - Convert ``side_spec`` to an openpyxl v2 Side object - Parameters - ---------- - side_spec : str, dict - A string specifying the border style, or a dict with zero or more - of the following keys (or their synonyms). - 'style' ('border_style') - 'color' - Returns - ------- - side : openpyxl.styles.Side - """ - - from openpyxl.styles import Side - - _side_key_map = { - 'border_style': 'style', - } - - if isinstance(side_spec, str): - return Side(style=side_spec) - - side_kwargs = {} - for k, v in side_spec.items(): - if k in _side_key_map: - k = _side_key_map[k] - if k == 'color': - v = cls._convert_to_color(v) - side_kwargs[k] = v - - return Side(**side_kwargs) - - @classmethod - def _convert_to_border(cls, border_dict): - """ - Convert ``border_dict`` to an openpyxl v2 Border object - Parameters - ---------- - border_dict : dict - A dict with zero or more of the following keys (or their synonyms). - 'left' - 'right' - 'top' - 'bottom' - 'diagonal' - 'diagonal_direction' - 'vertical' - 'horizontal' - 'diagonalUp' ('diagonalup') - 'diagonalDown' ('diagonaldown') - 'outline' - Returns - ------- - border : openpyxl.styles.Border - """ - - from openpyxl.styles import Border - - _border_key_map = { - 'diagonalup': 'diagonalUp', - 'diagonaldown': 'diagonalDown', - } - - border_kwargs = {} - for k, v in border_dict.items(): - if k in _border_key_map: - k = _border_key_map[k] - if k == 'color': - v = cls._convert_to_color(v) - if k in ['left', 'right', 'top', 'bottom', 'diagonal']: - v = cls._convert_to_side(v) - border_kwargs[k] = v - - return Border(**border_kwargs) - - @classmethod - def _convert_to_alignment(cls, alignment_dict): - """ - Convert ``alignment_dict`` to an openpyxl v2 Alignment object - Parameters - ---------- - alignment_dict : dict - A dict with zero or more of the following keys (or their synonyms). - 'horizontal' - 'vertical' - 'text_rotation' - 'wrap_text' - 'shrink_to_fit' - 'indent' - Returns - ------- - alignment : openpyxl.styles.Alignment - """ - - from openpyxl.styles import Alignment - - return Alignment(**alignment_dict) - - @classmethod - def _convert_to_number_format(cls, number_format_dict): - """ - Convert ``number_format_dict`` to an openpyxl v2.1.0 number format - initializer. - Parameters - ---------- - number_format_dict : dict - A dict with zero or more of the following keys. - 'format_code' : str - Returns - ------- - number_format : str - """ - return number_format_dict['format_code'] - - @classmethod - def _convert_to_protection(cls, protection_dict): - """ - Convert ``protection_dict`` to an openpyxl v2 Protection object. - Parameters - ---------- - protection_dict : dict - A dict with zero or more of the following keys. - 'locked' - 'hidden' - Returns - ------- - """ - - from openpyxl.styles import Protection - - return Protection(**protection_dict) - - def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, - freeze_panes=None): - # Write the frame cells using openpyxl. - sheet_name = self._get_sheet_name(sheet_name) - - _style_cache = {} - - if sheet_name in self.sheets: - wks = self.sheets[sheet_name] - else: - wks = self.book.create_sheet() - wks.title = sheet_name - self.sheets[sheet_name] = wks - - if _validate_freeze_panes(freeze_panes): - wks.freeze_panes = wks.cell(row=freeze_panes[0] + 1, - column=freeze_panes[1] + 1) - - for cell in cells: - xcell = wks.cell( - row=startrow + cell.row + 1, - column=startcol + cell.col + 1 - ) - xcell.value, fmt = self._value_with_fmt(cell.val) - if fmt: - xcell.number_format = fmt - - style_kwargs = {} - if cell.style: - key = str(cell.style) - style_kwargs = _style_cache.get(key) - if style_kwargs is None: - style_kwargs = self._convert_to_style_kwargs(cell.style) - _style_cache[key] = style_kwargs - - if style_kwargs: - for k, v in style_kwargs.items(): - setattr(xcell, k, v) - - if cell.mergestart is not None and cell.mergeend is not None: - - wks.merge_cells( - start_row=startrow + cell.row + 1, - start_column=startcol + cell.col + 1, - end_column=startcol + cell.mergeend + 1, - end_row=startrow + cell.mergestart + 1 - ) - - # When cells are merged only the top-left cell is preserved - # The behaviour of the other cells in a merged range is - # undefined - if style_kwargs: - first_row = startrow + cell.row + 1 - last_row = startrow + cell.mergestart + 1 - first_col = startcol + cell.col + 1 - last_col = startcol + cell.mergeend + 1 - - for row in range(first_row, last_row + 1): - for col in range(first_col, last_col + 1): - if row == first_row and col == first_col: - # Ignore first cell. It is already handled. - continue - xcell = wks.cell(column=col, row=row) - for k, v in style_kwargs.items(): - setattr(xcell, k, v) - - -register_writer(_OpenpyxlWriter) - - -class _XlwtWriter(ExcelWriter): - engine = 'xlwt' - supported_extensions = ('.xls',) - - def __init__(self, path, engine=None, encoding=None, mode='w', - **engine_kwargs): - # Use the xlwt module as the Excel writer. - import xlwt - engine_kwargs['engine'] = engine - - if mode == 'a': - raise ValueError('Append mode is not supported with xlwt!') - - super(_XlwtWriter, self).__init__(path, mode=mode, **engine_kwargs) - - if encoding is None: - encoding = 'ascii' - self.book = xlwt.Workbook(encoding=encoding) - self.fm_datetime = xlwt.easyxf(num_format_str=self.datetime_format) - self.fm_date = xlwt.easyxf(num_format_str=self.date_format) - - def save(self): - """ - Save workbook to disk. - """ - return self.book.save(self.path) - - def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, - freeze_panes=None): - # Write the frame cells using xlwt. - - sheet_name = self._get_sheet_name(sheet_name) - - if sheet_name in self.sheets: - wks = self.sheets[sheet_name] - else: - wks = self.book.add_sheet(sheet_name) - self.sheets[sheet_name] = wks - - if _validate_freeze_panes(freeze_panes): - wks.set_panes_frozen(True) - wks.set_horz_split_pos(freeze_panes[0]) - wks.set_vert_split_pos(freeze_panes[1]) - - style_dict = {} - - for cell in cells: - val, fmt = self._value_with_fmt(cell.val) - - stylekey = json.dumps(cell.style) - if fmt: - stylekey += fmt - - if stylekey in style_dict: - style = style_dict[stylekey] - else: - style = self._convert_to_style(cell.style, fmt) - style_dict[stylekey] = style - - if cell.mergestart is not None and cell.mergeend is not None: - wks.write_merge(startrow + cell.row, - startrow + cell.mergestart, - startcol + cell.col, - startcol + cell.mergeend, - val, style) - else: - wks.write(startrow + cell.row, - startcol + cell.col, - val, style) - - @classmethod - def _style_to_xlwt(cls, item, firstlevel=True, field_sep=',', - line_sep=';'): - """helper which recursively generate an xlwt easy style string - for example: - - hstyle = {"font": {"bold": True}, - "border": {"top": "thin", - "right": "thin", - "bottom": "thin", - "left": "thin"}, - "align": {"horiz": "center"}} - will be converted to - font: bold on; \ - border: top thin, right thin, bottom thin, left thin; \ - align: horiz center; - """ - if hasattr(item, 'items'): - if firstlevel: - it = ["{key}: {val}" - .format(key=key, val=cls._style_to_xlwt(value, False)) - for key, value in item.items()] - out = "{sep} ".format(sep=(line_sep).join(it)) - return out - else: - it = ["{key} {val}" - .format(key=key, val=cls._style_to_xlwt(value, False)) - for key, value in item.items()] - out = "{sep} ".format(sep=(field_sep).join(it)) - return out - else: - item = "{item}".format(item=item) - item = item.replace("True", "on") - item = item.replace("False", "off") - return item - - @classmethod - def _convert_to_style(cls, style_dict, num_format_str=None): - """ - converts a style_dict to an xlwt style object - Parameters - ---------- - style_dict : style dictionary to convert - num_format_str : optional number format string - """ - import xlwt - - if style_dict: - xlwt_stylestr = cls._style_to_xlwt(style_dict) - style = xlwt.easyxf(xlwt_stylestr, field_sep=',', line_sep=';') - else: - style = xlwt.XFStyle() - if num_format_str is not None: - style.num_format_str = num_format_str - - return style - - -register_writer(_XlwtWriter) - - -class _XlsxStyler(object): - # Map from openpyxl-oriented styles to flatter xlsxwriter representation - # Ordering necessary for both determinism and because some are keyed by - # prefixes of others. - STYLE_MAPPING = { - 'font': [ - (('name',), 'font_name'), - (('sz',), 'font_size'), - (('size',), 'font_size'), - (('color', 'rgb',), 'font_color'), - (('color',), 'font_color'), - (('b',), 'bold'), - (('bold',), 'bold'), - (('i',), 'italic'), - (('italic',), 'italic'), - (('u',), 'underline'), - (('underline',), 'underline'), - (('strike',), 'font_strikeout'), - (('vertAlign',), 'font_script'), - (('vertalign',), 'font_script'), - ], - 'number_format': [ - (('format_code',), 'num_format'), - ((), 'num_format',), - ], - 'protection': [ - (('locked',), 'locked'), - (('hidden',), 'hidden'), - ], - 'alignment': [ - (('horizontal',), 'align'), - (('vertical',), 'valign'), - (('text_rotation',), 'rotation'), - (('wrap_text',), 'text_wrap'), - (('indent',), 'indent'), - (('shrink_to_fit',), 'shrink'), - ], - 'fill': [ - (('patternType',), 'pattern'), - (('patterntype',), 'pattern'), - (('fill_type',), 'pattern'), - (('start_color', 'rgb',), 'fg_color'), - (('fgColor', 'rgb',), 'fg_color'), - (('fgcolor', 'rgb',), 'fg_color'), - (('start_color',), 'fg_color'), - (('fgColor',), 'fg_color'), - (('fgcolor',), 'fg_color'), - (('end_color', 'rgb',), 'bg_color'), - (('bgColor', 'rgb',), 'bg_color'), - (('bgcolor', 'rgb',), 'bg_color'), - (('end_color',), 'bg_color'), - (('bgColor',), 'bg_color'), - (('bgcolor',), 'bg_color'), - ], - 'border': [ - (('color', 'rgb',), 'border_color'), - (('color',), 'border_color'), - (('style',), 'border'), - (('top', 'color', 'rgb',), 'top_color'), - (('top', 'color',), 'top_color'), - (('top', 'style',), 'top'), - (('top',), 'top'), - (('right', 'color', 'rgb',), 'right_color'), - (('right', 'color',), 'right_color'), - (('right', 'style',), 'right'), - (('right',), 'right'), - (('bottom', 'color', 'rgb',), 'bottom_color'), - (('bottom', 'color',), 'bottom_color'), - (('bottom', 'style',), 'bottom'), - (('bottom',), 'bottom'), - (('left', 'color', 'rgb',), 'left_color'), - (('left', 'color',), 'left_color'), - (('left', 'style',), 'left'), - (('left',), 'left'), - ], - } - - @classmethod - def convert(cls, style_dict, num_format_str=None): - """ - converts a style_dict to an xlsxwriter format dict - - Parameters - ---------- - style_dict : style dictionary to convert - num_format_str : optional number format string - """ - - # Create a XlsxWriter format object. - props = {} - - if num_format_str is not None: - props['num_format'] = num_format_str - - if style_dict is None: - return props - - if 'borders' in style_dict: - style_dict = style_dict.copy() - style_dict['border'] = style_dict.pop('borders') - - for style_group_key, style_group in style_dict.items(): - for src, dst in cls.STYLE_MAPPING.get(style_group_key, []): - # src is a sequence of keys into a nested dict - # dst is a flat key - if dst in props: - continue - v = style_group - for k in src: - try: - v = v[k] - except (KeyError, TypeError): - break - else: - props[dst] = v - - if isinstance(props.get('pattern'), string_types): - # TODO: support other fill patterns - props['pattern'] = 0 if props['pattern'] == 'none' else 1 - - for k in ['border', 'top', 'right', 'bottom', 'left']: - if isinstance(props.get(k), string_types): - try: - props[k] = ['none', 'thin', 'medium', 'dashed', 'dotted', - 'thick', 'double', 'hair', 'mediumDashed', - 'dashDot', 'mediumDashDot', 'dashDotDot', - 'mediumDashDotDot', - 'slantDashDot'].index(props[k]) - except ValueError: - props[k] = 2 - - if isinstance(props.get('font_script'), string_types): - props['font_script'] = ['baseline', 'superscript', - 'subscript'].index(props['font_script']) - - if isinstance(props.get('underline'), string_types): - props['underline'] = {'none': 0, 'single': 1, 'double': 2, - 'singleAccounting': 33, - 'doubleAccounting': 34}[props['underline']] - - return props - - -class _XlsxWriter(ExcelWriter): - engine = 'xlsxwriter' - supported_extensions = ('.xlsx',) - - def __init__(self, path, engine=None, - date_format=None, datetime_format=None, mode='w', - **engine_kwargs): - # Use the xlsxwriter module as the Excel writer. - import xlsxwriter - - if mode == 'a': - raise ValueError('Append mode is not supported with xlsxwriter!') - - super(_XlsxWriter, self).__init__(path, engine=engine, - date_format=date_format, - datetime_format=datetime_format, - mode=mode, - **engine_kwargs) - - self.book = xlsxwriter.Workbook(path, **engine_kwargs) - - def save(self): - """ - Save workbook to disk. - """ - - return self.book.close() - - def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, - freeze_panes=None): - # Write the frame cells using xlsxwriter. - sheet_name = self._get_sheet_name(sheet_name) - - if sheet_name in self.sheets: - wks = self.sheets[sheet_name] - else: - wks = self.book.add_worksheet(sheet_name) - self.sheets[sheet_name] = wks - - style_dict = {'null': None} - - if _validate_freeze_panes(freeze_panes): - wks.freeze_panes(*(freeze_panes)) - - for cell in cells: - val, fmt = self._value_with_fmt(cell.val) - - stylekey = json.dumps(cell.style) - if fmt: - stylekey += fmt - - if stylekey in style_dict: - style = style_dict[stylekey] - else: - style = self.book.add_format( - _XlsxStyler.convert(cell.style, fmt)) - style_dict[stylekey] = style - - if cell.mergestart is not None and cell.mergeend is not None: - wks.merge_range(startrow + cell.row, - startcol + cell.col, - startrow + cell.mergestart, - startcol + cell.mergeend, - cell.val, style) - else: - wks.write(startrow + cell.row, - startcol + cell.col, - val, style) - - -register_writer(_XlsxWriter) diff --git a/pandas/io/excel/__init__.py b/pandas/io/excel/__init__.py new file mode 100644 index 0000000000000..704789cb6061e --- /dev/null +++ b/pandas/io/excel/__init__.py @@ -0,0 +1,16 @@ +from pandas.io.excel._base import read_excel, ExcelWriter, ExcelFile +from pandas.io.excel._openpyxl import _OpenpyxlWriter +from pandas.io.excel._util import register_writer +from pandas.io.excel._xlsxwriter import _XlsxWriter +from pandas.io.excel._xlwt import _XlwtWriter + +__all__ = ["read_excel", "ExcelWriter", "ExcelFile"] + + +register_writer(_OpenpyxlWriter) + + +register_writer(_XlwtWriter) + + +register_writer(_XlsxWriter) diff --git a/pandas/io/excel/_base.py b/pandas/io/excel/_base.py new file mode 100644 index 0000000000000..ed5943e9a1698 --- /dev/null +++ b/pandas/io/excel/_base.py @@ -0,0 +1,853 @@ +import abc +from collections import OrderedDict +from datetime import date, datetime, timedelta +import os +from textwrap import fill +import warnings + +import pandas.compat as compat +from pandas.compat import add_metaclass, range, string_types, u +from pandas.errors import EmptyDataError +from pandas.util._decorators import Appender, deprecate_kwarg + +from pandas.core.dtypes.common import ( + is_bool, is_float, is_integer, is_list_like) + +from pandas.core import config +from pandas.core.frame import DataFrame + +from pandas.io.common import _NA_VALUES, _stringify_path, _validate_header_arg +from pandas.io.excel._util import ( + _fill_mi_header, _get_default_writer, _maybe_convert_to_string, + _maybe_convert_usecols, _pop_header_name, get_writer) +from pandas.io.formats.printing import pprint_thing +from pandas.io.parsers import TextParser + +_read_excel_doc = """ +Read an Excel file into a pandas DataFrame. + +Support both `xls` and `xlsx` file extensions from a local filesystem or URL. +Support an option to read a single sheet or a list of sheets. + +Parameters +---------- +io : str, file descriptor, pathlib.Path, ExcelFile or xlrd.Book + The string could be a URL. Valid URL schemes include http, ftp, s3, + gcs, and file. For file URLs, a host is expected. For instance, a local + file could be /path/to/workbook.xlsx. +sheet_name : str, int, list, or None, default 0 + Strings are used for sheet names. Integers are used in zero-indexed + sheet positions. Lists of strings/integers are used to request + multiple sheets. Specify None to get all sheets. + + Available cases: + + * Defaults to ``0``: 1st sheet as a `DataFrame` + * ``1``: 2nd sheet as a `DataFrame` + * ``"Sheet1"``: Load sheet with name "Sheet1" + * ``[0, 1, "Sheet5"]``: Load first, second and sheet named "Sheet5" + as a dict of `DataFrame` + * None: All sheets. + +header : int, list of int, default 0 + Row (0-indexed) to use for the column labels of the parsed + DataFrame. If a list of integers is passed those row positions will + be combined into a ``MultiIndex``. Use None if there is no header. +names : array-like, default None + List of column names to use. If file contains no header row, + then you should explicitly pass header=None. +index_col : int, list of int, default None + Column (0-indexed) to use as the row labels of the DataFrame. + Pass None if there is no such column. If a list is passed, + those columns will be combined into a ``MultiIndex``. If a + subset of data is selected with ``usecols``, index_col + is based on the subset. +parse_cols : int or list, default None + Alias of `usecols`. + + .. deprecated:: 0.21.0 + Use `usecols` instead. + +usecols : int, str, list-like, or callable default None + Return a subset of the columns. + * If None, then parse all columns. + * If int, then indicates last column to be parsed. + + .. deprecated:: 0.24.0 + Pass in a list of int instead from 0 to `usecols` inclusive. + + * If str, then indicates comma separated list of Excel column letters + and column ranges (e.g. "A:E" or "A,C,E:F"). Ranges are inclusive of + both sides. + * If list of int, then indicates list of column numbers to be parsed. + * If list of string, then indicates list of column names to be parsed. + + .. versionadded:: 0.24.0 + + * If callable, then evaluate each column name against it and parse the + column if the callable returns ``True``. + + .. versionadded:: 0.24.0 + +squeeze : bool, default False + If the parsed data only contains one column then return a Series. +dtype : Type name or dict of column -> type, default None + Data type for data or columns. E.g. {'a': np.float64, 'b': np.int32} + Use `object` to preserve data as stored in Excel and not interpret dtype. + If converters are specified, they will be applied INSTEAD + of dtype conversion. + + .. versionadded:: 0.20.0 + +engine : str, default None + If io is not a buffer or path, this must be set to identify io. + Acceptable values are None or xlrd. +converters : dict, default None + Dict of functions for converting values in certain columns. Keys can + either be integers or column labels, values are functions that take one + input argument, the Excel cell content, and return the transformed + content. +true_values : list, default None + Values to consider as True. + + .. versionadded:: 0.19.0 + +false_values : list, default None + Values to consider as False. + + .. versionadded:: 0.19.0 + +skiprows : list-like + Rows to skip at the beginning (0-indexed). +nrows : int, default None + Number of rows to parse. + + .. versionadded:: 0.23.0 + +na_values : scalar, str, list-like, or dict, default None + Additional strings to recognize as NA/NaN. If dict passed, specific + per-column NA values. By default the following values are interpreted + as NaN: '""" + fill("', '".join( + sorted(_NA_VALUES)), 70, subsequent_indent=" ") + """'. +keep_default_na : bool, default True + If na_values are specified and keep_default_na is False the default NaN + values are overridden, otherwise they're appended to. +verbose : bool, default False + Indicate number of NA values placed in non-numeric columns. +parse_dates : bool, list-like, or dict, default False + The behavior is as follows: + + * bool. If True -> try parsing the index. + * list of int or names. e.g. If [1, 2, 3] -> try parsing columns 1, 2, 3 + each as a separate date column. + * list of lists. e.g. If [[1, 3]] -> combine columns 1 and 3 and parse as + a single date column. + * dict, e.g. {{'foo' : [1, 3]}} -> parse columns 1, 3 as date and call + result 'foo' + + If a column or index contains an unparseable date, the entire column or + index will be returned unaltered as an object data type. For non-standard + datetime parsing, use ``pd.to_datetime`` after ``pd.read_csv`` + + Note: A fast-path exists for iso8601-formatted dates. +date_parser : function, optional + Function to use for converting a sequence of string columns to an array of + datetime instances. The default uses ``dateutil.parser.parser`` to do the + conversion. Pandas will try to call `date_parser` in three different ways, + advancing to the next if an exception occurs: 1) Pass one or more arrays + (as defined by `parse_dates`) as arguments; 2) concatenate (row-wise) the + string values from the columns defined by `parse_dates` into a single array + and pass that; and 3) call `date_parser` once for each row using one or + more strings (corresponding to the columns defined by `parse_dates`) as + arguments. +thousands : str, default None + Thousands separator for parsing string columns to numeric. Note that + this parameter is only necessary for columns stored as TEXT in Excel, + any numeric columns will automatically be parsed, regardless of display + format. +comment : str, default None + Comments out remainder of line. Pass a character or characters to this + argument to indicate comments in the input file. Any data between the + comment string and the end of the current line is ignored. +skip_footer : int, default 0 + Alias of `skipfooter`. + + .. deprecated:: 0.23.0 + Use `skipfooter` instead. +skipfooter : int, default 0 + Rows at the end to skip (0-indexed). +convert_float : bool, default True + Convert integral floats to int (i.e., 1.0 --> 1). If False, all numeric + data will be read in as floats: Excel stores all numbers as floats + internally. +mangle_dupe_cols : bool, default True + Duplicate columns will be specified as 'X', 'X.1', ...'X.N', rather than + 'X'...'X'. Passing in False will cause data to be overwritten if there + are duplicate names in the columns. +**kwds : optional + Optional keyword arguments can be passed to ``TextFileReader``. + +Returns +------- +DataFrame or dict of DataFrames + DataFrame from the passed in Excel file. See notes in sheet_name + argument for more information on when a dict of DataFrames is returned. + +See Also +-------- +to_excel : Write DataFrame to an Excel file. +to_csv : Write DataFrame to a comma-separated values (csv) file. +read_csv : Read a comma-separated values (csv) file into DataFrame. +read_fwf : Read a table of fixed-width formatted lines into DataFrame. + +Examples +-------- +The file can be read using the file name as string or an open file object: + +>>> pd.read_excel('tmp.xlsx', index_col=0) # doctest: +SKIP + Name Value +0 string1 1 +1 string2 2 +2 #Comment 3 + +>>> pd.read_excel(open('tmp.xlsx', 'rb'), +... sheet_name='Sheet3') # doctest: +SKIP + Unnamed: 0 Name Value +0 0 string1 1 +1 1 string2 2 +2 2 #Comment 3 + +Index and header can be specified via the `index_col` and `header` arguments + +>>> pd.read_excel('tmp.xlsx', index_col=None, header=None) # doctest: +SKIP + 0 1 2 +0 NaN Name Value +1 0.0 string1 1 +2 1.0 string2 2 +3 2.0 #Comment 3 + +Column types are inferred but can be explicitly specified + +>>> pd.read_excel('tmp.xlsx', index_col=0, +... dtype={'Name': str, 'Value': float}) # doctest: +SKIP + Name Value +0 string1 1.0 +1 string2 2.0 +2 #Comment 3.0 + +True, False, and NA values, and thousands separators have defaults, +but can be explicitly specified, too. Supply the values you would like +as strings or lists of strings! + +>>> pd.read_excel('tmp.xlsx', index_col=0, +... na_values=['string1', 'string2']) # doctest: +SKIP + Name Value +0 NaN 1 +1 NaN 2 +2 #Comment 3 + +Comment lines in the excel input file can be skipped using the `comment` kwarg + +>>> pd.read_excel('tmp.xlsx', index_col=0, comment='#') # doctest: +SKIP + Name Value +0 string1 1.0 +1 string2 2.0 +2 None NaN +""" + + +@Appender(_read_excel_doc) +@deprecate_kwarg("parse_cols", "usecols") +@deprecate_kwarg("skip_footer", "skipfooter") +def read_excel(io, + sheet_name=0, + header=0, + names=None, + index_col=None, + parse_cols=None, + usecols=None, + squeeze=False, + dtype=None, + engine=None, + converters=None, + true_values=None, + false_values=None, + skiprows=None, + nrows=None, + na_values=None, + keep_default_na=True, + verbose=False, + parse_dates=False, + date_parser=None, + thousands=None, + comment=None, + skip_footer=0, + skipfooter=0, + convert_float=True, + mangle_dupe_cols=True, + **kwds): + + # Can't use _deprecate_kwarg since sheetname=None has a special meaning + if is_integer(sheet_name) and sheet_name == 0 and 'sheetname' in kwds: + warnings.warn("The `sheetname` keyword is deprecated, use " + "`sheet_name` instead", FutureWarning, stacklevel=2) + sheet_name = kwds.pop("sheetname") + + if 'sheet' in kwds: + raise TypeError("read_excel() got an unexpected keyword argument " + "`sheet`") + + if not isinstance(io, ExcelFile): + io = ExcelFile(io, engine=engine) + + return io.parse( + sheet_name=sheet_name, + header=header, + names=names, + index_col=index_col, + usecols=usecols, + squeeze=squeeze, + dtype=dtype, + converters=converters, + true_values=true_values, + false_values=false_values, + skiprows=skiprows, + nrows=nrows, + na_values=na_values, + keep_default_na=keep_default_na, + verbose=verbose, + parse_dates=parse_dates, + date_parser=date_parser, + thousands=thousands, + comment=comment, + skipfooter=skipfooter, + convert_float=convert_float, + mangle_dupe_cols=mangle_dupe_cols, + **kwds) + + +@add_metaclass(abc.ABCMeta) +class _BaseExcelReader(object): + + @property + @abc.abstractmethod + def sheet_names(self): + pass + + @abc.abstractmethod + def get_sheet_by_name(self, name): + pass + + @abc.abstractmethod + def get_sheet_by_index(self, index): + pass + + @abc.abstractmethod + def get_sheet_data(self, sheet, convert_float): + pass + + def parse(self, + sheet_name=0, + header=0, + names=None, + index_col=None, + usecols=None, + squeeze=False, + dtype=None, + true_values=None, + false_values=None, + skiprows=None, + nrows=None, + na_values=None, + verbose=False, + parse_dates=False, + date_parser=None, + thousands=None, + comment=None, + skipfooter=0, + convert_float=True, + mangle_dupe_cols=True, + **kwds): + + _validate_header_arg(header) + + ret_dict = False + + # Keep sheetname to maintain backwards compatibility. + if isinstance(sheet_name, list): + sheets = sheet_name + ret_dict = True + elif sheet_name is None: + sheets = self.sheet_names + ret_dict = True + else: + sheets = [sheet_name] + + # handle same-type duplicates. + sheets = list(OrderedDict.fromkeys(sheets).keys()) + + output = OrderedDict() + + for asheetname in sheets: + if verbose: + print("Reading sheet {sheet}".format(sheet=asheetname)) + + if isinstance(asheetname, compat.string_types): + sheet = self.get_sheet_by_name(asheetname) + else: # assume an integer if not a string + sheet = self.get_sheet_by_index(asheetname) + + data = self.get_sheet_data(sheet, convert_float) + usecols = _maybe_convert_usecols(usecols) + + if sheet.nrows == 0: + output[asheetname] = DataFrame() + continue + + if is_list_like(header) and len(header) == 1: + header = header[0] + + # forward fill and pull out names for MultiIndex column + header_names = None + if header is not None and is_list_like(header): + header_names = [] + control_row = [True] * len(data[0]) + + for row in header: + if is_integer(skiprows): + row += skiprows + + data[row], control_row = _fill_mi_header(data[row], + control_row) + + if index_col is not None: + header_name, _ = _pop_header_name(data[row], index_col) + header_names.append(header_name) + + if is_list_like(index_col): + # Forward fill values for MultiIndex index. + if not is_list_like(header): + offset = 1 + header + else: + offset = 1 + max(header) + + # Check if we have an empty dataset + # before trying to collect data. + if offset < len(data): + for col in index_col: + last = data[offset][col] + + for row in range(offset + 1, len(data)): + if data[row][col] == '' or data[row][col] is None: + data[row][col] = last + else: + last = data[row][col] + + has_index_names = is_list_like(header) and len(header) > 1 + + # GH 12292 : error when read one empty column from excel file + try: + parser = TextParser(data, + names=names, + header=header, + index_col=index_col, + has_index_names=has_index_names, + squeeze=squeeze, + dtype=dtype, + true_values=true_values, + false_values=false_values, + skiprows=skiprows, + nrows=nrows, + na_values=na_values, + parse_dates=parse_dates, + date_parser=date_parser, + thousands=thousands, + comment=comment, + skipfooter=skipfooter, + usecols=usecols, + mangle_dupe_cols=mangle_dupe_cols, + **kwds) + + output[asheetname] = parser.read(nrows=nrows) + + if not squeeze or isinstance(output[asheetname], DataFrame): + if header_names: + output[asheetname].columns = output[ + asheetname].columns.set_names(header_names) + elif compat.PY2: + output[asheetname].columns = _maybe_convert_to_string( + output[asheetname].columns) + + except EmptyDataError: + # No Data, return an empty DataFrame + output[asheetname] = DataFrame() + + if ret_dict: + return output + else: + return output[asheetname] + + +@add_metaclass(abc.ABCMeta) +class ExcelWriter(object): + """ + Class for writing DataFrame objects into excel sheets, default is to use + xlwt for xls, openpyxl for xlsx. See DataFrame.to_excel for typical usage. + + Parameters + ---------- + path : string + Path to xls or xlsx file. + engine : string (optional) + Engine to use for writing. If None, defaults to + ``io.excel..writer``. NOTE: can only be passed as a keyword + argument. + date_format : string, default None + Format string for dates written into Excel files (e.g. 'YYYY-MM-DD') + datetime_format : string, default None + Format string for datetime objects written into Excel files + (e.g. 'YYYY-MM-DD HH:MM:SS') + mode : {'w' or 'a'}, default 'w' + File mode to use (write or append). + + .. versionadded:: 0.24.0 + + Attributes + ---------- + None + + Methods + ------- + None + + Notes + ----- + None of the methods and properties are considered public. + + For compatibility with CSV writers, ExcelWriter serializes lists + and dicts to strings before writing. + + Examples + -------- + Default usage: + + >>> with ExcelWriter('path_to_file.xlsx') as writer: + ... df.to_excel(writer) + + To write to separate sheets in a single file: + + >>> with ExcelWriter('path_to_file.xlsx') as writer: + ... df1.to_excel(writer, sheet_name='Sheet1') + ... df2.to_excel(writer, sheet_name='Sheet2') + + You can set the date format or datetime format: + + >>> with ExcelWriter('path_to_file.xlsx', + date_format='YYYY-MM-DD', + datetime_format='YYYY-MM-DD HH:MM:SS') as writer: + ... df.to_excel(writer) + + You can also append to an existing Excel file: + + >>> with ExcelWriter('path_to_file.xlsx', mode='a') as writer: + ... df.to_excel(writer, sheet_name='Sheet3') + """ + # Defining an ExcelWriter implementation (see abstract methods for more...) + + # - Mandatory + # - ``write_cells(self, cells, sheet_name=None, startrow=0, startcol=0)`` + # --> called to write additional DataFrames to disk + # - ``supported_extensions`` (tuple of supported extensions), used to + # check that engine supports the given extension. + # - ``engine`` - string that gives the engine name. Necessary to + # instantiate class directly and bypass ``ExcelWriterMeta`` engine + # lookup. + # - ``save(self)`` --> called to save file to disk + # - Mostly mandatory (i.e. should at least exist) + # - book, cur_sheet, path + + # - Optional: + # - ``__init__(self, path, engine=None, **kwargs)`` --> always called + # with path as first argument. + + # You also need to register the class with ``register_writer()``. + # Technically, ExcelWriter implementations don't need to subclass + # ExcelWriter. + def __new__(cls, path, engine=None, **kwargs): + # only switch class if generic(ExcelWriter) + + if issubclass(cls, ExcelWriter): + if engine is None or (isinstance(engine, string_types) and + engine == 'auto'): + if isinstance(path, string_types): + ext = os.path.splitext(path)[-1][1:] + else: + ext = 'xlsx' + + try: + engine = config.get_option('io.excel.{ext}.writer' + .format(ext=ext)) + if engine == 'auto': + engine = _get_default_writer(ext) + except KeyError: + error = ValueError("No engine for filetype: '{ext}'" + .format(ext=ext)) + raise error + cls = get_writer(engine) + + return object.__new__(cls) + + # declare external properties you can count on + book = None + curr_sheet = None + path = None + + @abc.abstractproperty + def supported_extensions(self): + "extensions that writer engine supports" + pass + + @abc.abstractproperty + def engine(self): + "name of engine" + pass + + @abc.abstractmethod + def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, + freeze_panes=None): + """ + Write given formatted cells into Excel an excel sheet + + Parameters + ---------- + cells : generator + cell of formatted data to save to Excel sheet + sheet_name : string, default None + Name of Excel sheet, if None, then use self.cur_sheet + startrow : upper left cell row to dump data frame + startcol : upper left cell column to dump data frame + freeze_panes: integer tuple of length 2 + contains the bottom-most row and right-most column to freeze + """ + pass + + @abc.abstractmethod + def save(self): + """ + Save workbook to disk. + """ + pass + + def __init__(self, path, engine=None, + date_format=None, datetime_format=None, mode='w', + **engine_kwargs): + # validate that this engine can handle the extension + if isinstance(path, string_types): + ext = os.path.splitext(path)[-1] + else: + ext = 'xls' if engine == 'xlwt' else 'xlsx' + + self.check_extension(ext) + + self.path = path + self.sheets = {} + self.cur_sheet = None + + if date_format is None: + self.date_format = 'YYYY-MM-DD' + else: + self.date_format = date_format + if datetime_format is None: + self.datetime_format = 'YYYY-MM-DD HH:MM:SS' + else: + self.datetime_format = datetime_format + + self.mode = mode + + def __fspath__(self): + return _stringify_path(self.path) + + def _get_sheet_name(self, sheet_name): + if sheet_name is None: + sheet_name = self.cur_sheet + if sheet_name is None: # pragma: no cover + raise ValueError('Must pass explicit sheet_name or set ' + 'cur_sheet property') + return sheet_name + + def _value_with_fmt(self, val): + """Convert numpy types to Python types for the Excel writers. + + Parameters + ---------- + val : object + Value to be written into cells + + Returns + ------- + Tuple with the first element being the converted value and the second + being an optional format + """ + fmt = None + + if is_integer(val): + val = int(val) + elif is_float(val): + val = float(val) + elif is_bool(val): + val = bool(val) + elif isinstance(val, datetime): + fmt = self.datetime_format + elif isinstance(val, date): + fmt = self.date_format + elif isinstance(val, timedelta): + val = val.total_seconds() / float(86400) + fmt = '0' + else: + val = compat.to_str(val) + + return val, fmt + + @classmethod + def check_extension(cls, ext): + """checks that path's extension against the Writer's supported + extensions. If it isn't supported, raises UnsupportedFiletypeError.""" + if ext.startswith('.'): + ext = ext[1:] + if not any(ext in extension for extension in cls.supported_extensions): + msg = (u("Invalid extension for engine '{engine}': '{ext}'") + .format(engine=pprint_thing(cls.engine), + ext=pprint_thing(ext))) + raise ValueError(msg) + else: + return True + + # Allow use as a contextmanager + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def close(self): + """synonym for save, to make it more file-like""" + return self.save() + + +class ExcelFile(object): + """ + Class for parsing tabular excel sheets into DataFrame objects. + Uses xlrd. See read_excel for more documentation + + Parameters + ---------- + io : string, path object (pathlib.Path or py._path.local.LocalPath), + file-like object or xlrd workbook + If a string or path object, expected to be a path to xls or xlsx file. + engine : string, default None + If io is not a buffer or path, this must be set to identify io. + Acceptable values are None or ``xlrd``. + """ + + from pandas.io.excel._xlrd import _XlrdReader + + _engines = { + 'xlrd': _XlrdReader, + } + + def __init__(self, io, engine=None): + if engine is None: + engine = 'xlrd' + if engine not in self._engines: + raise ValueError("Unknown engine: {engine}".format(engine=engine)) + + # could be a str, ExcelFile, Book, etc. + self.io = io + # Always a string + self._io = _stringify_path(io) + + self._reader = self._engines[engine](self._io) + + def __fspath__(self): + return self._io + + def parse(self, + sheet_name=0, + header=0, + names=None, + index_col=None, + usecols=None, + squeeze=False, + converters=None, + true_values=None, + false_values=None, + skiprows=None, + nrows=None, + na_values=None, + parse_dates=False, + date_parser=None, + thousands=None, + comment=None, + skipfooter=0, + convert_float=True, + mangle_dupe_cols=True, + **kwds): + """ + Parse specified sheet(s) into a DataFrame + + Equivalent to read_excel(ExcelFile, ...) See the read_excel + docstring for more info on accepted parameters + """ + + # Can't use _deprecate_kwarg since sheetname=None has a special meaning + if is_integer(sheet_name) and sheet_name == 0 and 'sheetname' in kwds: + warnings.warn("The `sheetname` keyword is deprecated, use " + "`sheet_name` instead", FutureWarning, stacklevel=2) + sheet_name = kwds.pop("sheetname") + elif 'sheetname' in kwds: + raise TypeError("Cannot specify both `sheet_name` " + "and `sheetname`. Use just `sheet_name`") + + if 'chunksize' in kwds: + raise NotImplementedError("chunksize keyword of read_excel " + "is not implemented") + + return self._reader.parse(sheet_name=sheet_name, + header=header, + names=names, + index_col=index_col, + usecols=usecols, + squeeze=squeeze, + converters=converters, + true_values=true_values, + false_values=false_values, + skiprows=skiprows, + nrows=nrows, + na_values=na_values, + parse_dates=parse_dates, + date_parser=date_parser, + thousands=thousands, + comment=comment, + skipfooter=skipfooter, + convert_float=convert_float, + mangle_dupe_cols=mangle_dupe_cols, + **kwds) + + @property + def book(self): + return self._reader.book + + @property + def sheet_names(self): + return self._reader.sheet_names + + def close(self): + """close io if necessary""" + if hasattr(self.io, 'close'): + self.io.close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() diff --git a/pandas/io/excel/_openpyxl.py b/pandas/io/excel/_openpyxl.py new file mode 100644 index 0000000000000..8d79c13a65c97 --- /dev/null +++ b/pandas/io/excel/_openpyxl.py @@ -0,0 +1,453 @@ +from pandas.io.excel._base import ExcelWriter +from pandas.io.excel._util import _validate_freeze_panes + + +class _OpenpyxlWriter(ExcelWriter): + engine = 'openpyxl' + supported_extensions = ('.xlsx', '.xlsm') + + def __init__(self, path, engine=None, mode='w', **engine_kwargs): + # Use the openpyxl module as the Excel writer. + from openpyxl.workbook import Workbook + + super(_OpenpyxlWriter, self).__init__(path, mode=mode, **engine_kwargs) + + if self.mode == 'a': # Load from existing workbook + from openpyxl import load_workbook + book = load_workbook(self.path) + self.book = book + else: + # Create workbook object with default optimized_write=True. + self.book = Workbook() + + if self.book.worksheets: + try: + self.book.remove(self.book.worksheets[0]) + except AttributeError: + + # compat - for openpyxl <= 2.4 + self.book.remove_sheet(self.book.worksheets[0]) + + def save(self): + """ + Save workbook to disk. + """ + return self.book.save(self.path) + + @classmethod + def _convert_to_style(cls, style_dict): + """ + converts a style_dict to an openpyxl style object + Parameters + ---------- + style_dict : style dictionary to convert + """ + + from openpyxl.style import Style + xls_style = Style() + for key, value in style_dict.items(): + for nk, nv in value.items(): + if key == "borders": + (xls_style.borders.__getattribute__(nk) + .__setattr__('border_style', nv)) + else: + xls_style.__getattribute__(key).__setattr__(nk, nv) + + return xls_style + + @classmethod + def _convert_to_style_kwargs(cls, style_dict): + """ + Convert a style_dict to a set of kwargs suitable for initializing + or updating-on-copy an openpyxl v2 style object + Parameters + ---------- + style_dict : dict + A dict with zero or more of the following keys (or their synonyms). + 'font' + 'fill' + 'border' ('borders') + 'alignment' + 'number_format' + 'protection' + Returns + ------- + style_kwargs : dict + A dict with the same, normalized keys as ``style_dict`` but each + value has been replaced with a native openpyxl style object of the + appropriate class. + """ + + _style_key_map = { + 'borders': 'border', + } + + style_kwargs = {} + for k, v in style_dict.items(): + if k in _style_key_map: + k = _style_key_map[k] + _conv_to_x = getattr(cls, '_convert_to_{k}'.format(k=k), + lambda x: None) + new_v = _conv_to_x(v) + if new_v: + style_kwargs[k] = new_v + + return style_kwargs + + @classmethod + def _convert_to_color(cls, color_spec): + """ + Convert ``color_spec`` to an openpyxl v2 Color object + Parameters + ---------- + color_spec : str, dict + A 32-bit ARGB hex string, or a dict with zero or more of the + following keys. + 'rgb' + 'indexed' + 'auto' + 'theme' + 'tint' + 'index' + 'type' + Returns + ------- + color : openpyxl.styles.Color + """ + + from openpyxl.styles import Color + + if isinstance(color_spec, str): + return Color(color_spec) + else: + return Color(**color_spec) + + @classmethod + def _convert_to_font(cls, font_dict): + """ + Convert ``font_dict`` to an openpyxl v2 Font object + Parameters + ---------- + font_dict : dict + A dict with zero or more of the following keys (or their synonyms). + 'name' + 'size' ('sz') + 'bold' ('b') + 'italic' ('i') + 'underline' ('u') + 'strikethrough' ('strike') + 'color' + 'vertAlign' ('vertalign') + 'charset' + 'scheme' + 'family' + 'outline' + 'shadow' + 'condense' + Returns + ------- + font : openpyxl.styles.Font + """ + + from openpyxl.styles import Font + + _font_key_map = { + 'sz': 'size', + 'b': 'bold', + 'i': 'italic', + 'u': 'underline', + 'strike': 'strikethrough', + 'vertalign': 'vertAlign', + } + + font_kwargs = {} + for k, v in font_dict.items(): + if k in _font_key_map: + k = _font_key_map[k] + if k == 'color': + v = cls._convert_to_color(v) + font_kwargs[k] = v + + return Font(**font_kwargs) + + @classmethod + def _convert_to_stop(cls, stop_seq): + """ + Convert ``stop_seq`` to a list of openpyxl v2 Color objects, + suitable for initializing the ``GradientFill`` ``stop`` parameter. + Parameters + ---------- + stop_seq : iterable + An iterable that yields objects suitable for consumption by + ``_convert_to_color``. + Returns + ------- + stop : list of openpyxl.styles.Color + """ + + return map(cls._convert_to_color, stop_seq) + + @classmethod + def _convert_to_fill(cls, fill_dict): + """ + Convert ``fill_dict`` to an openpyxl v2 Fill object + Parameters + ---------- + fill_dict : dict + A dict with one or more of the following keys (or their synonyms), + 'fill_type' ('patternType', 'patterntype') + 'start_color' ('fgColor', 'fgcolor') + 'end_color' ('bgColor', 'bgcolor') + or one or more of the following keys (or their synonyms). + 'type' ('fill_type') + 'degree' + 'left' + 'right' + 'top' + 'bottom' + 'stop' + Returns + ------- + fill : openpyxl.styles.Fill + """ + + from openpyxl.styles import PatternFill, GradientFill + + _pattern_fill_key_map = { + 'patternType': 'fill_type', + 'patterntype': 'fill_type', + 'fgColor': 'start_color', + 'fgcolor': 'start_color', + 'bgColor': 'end_color', + 'bgcolor': 'end_color', + } + + _gradient_fill_key_map = { + 'fill_type': 'type', + } + + pfill_kwargs = {} + gfill_kwargs = {} + for k, v in fill_dict.items(): + pk = gk = None + if k in _pattern_fill_key_map: + pk = _pattern_fill_key_map[k] + if k in _gradient_fill_key_map: + gk = _gradient_fill_key_map[k] + if pk in ['start_color', 'end_color']: + v = cls._convert_to_color(v) + if gk == 'stop': + v = cls._convert_to_stop(v) + if pk: + pfill_kwargs[pk] = v + elif gk: + gfill_kwargs[gk] = v + else: + pfill_kwargs[k] = v + gfill_kwargs[k] = v + + try: + return PatternFill(**pfill_kwargs) + except TypeError: + return GradientFill(**gfill_kwargs) + + @classmethod + def _convert_to_side(cls, side_spec): + """ + Convert ``side_spec`` to an openpyxl v2 Side object + Parameters + ---------- + side_spec : str, dict + A string specifying the border style, or a dict with zero or more + of the following keys (or their synonyms). + 'style' ('border_style') + 'color' + Returns + ------- + side : openpyxl.styles.Side + """ + + from openpyxl.styles import Side + + _side_key_map = { + 'border_style': 'style', + } + + if isinstance(side_spec, str): + return Side(style=side_spec) + + side_kwargs = {} + for k, v in side_spec.items(): + if k in _side_key_map: + k = _side_key_map[k] + if k == 'color': + v = cls._convert_to_color(v) + side_kwargs[k] = v + + return Side(**side_kwargs) + + @classmethod + def _convert_to_border(cls, border_dict): + """ + Convert ``border_dict`` to an openpyxl v2 Border object + Parameters + ---------- + border_dict : dict + A dict with zero or more of the following keys (or their synonyms). + 'left' + 'right' + 'top' + 'bottom' + 'diagonal' + 'diagonal_direction' + 'vertical' + 'horizontal' + 'diagonalUp' ('diagonalup') + 'diagonalDown' ('diagonaldown') + 'outline' + Returns + ------- + border : openpyxl.styles.Border + """ + + from openpyxl.styles import Border + + _border_key_map = { + 'diagonalup': 'diagonalUp', + 'diagonaldown': 'diagonalDown', + } + + border_kwargs = {} + for k, v in border_dict.items(): + if k in _border_key_map: + k = _border_key_map[k] + if k == 'color': + v = cls._convert_to_color(v) + if k in ['left', 'right', 'top', 'bottom', 'diagonal']: + v = cls._convert_to_side(v) + border_kwargs[k] = v + + return Border(**border_kwargs) + + @classmethod + def _convert_to_alignment(cls, alignment_dict): + """ + Convert ``alignment_dict`` to an openpyxl v2 Alignment object + Parameters + ---------- + alignment_dict : dict + A dict with zero or more of the following keys (or their synonyms). + 'horizontal' + 'vertical' + 'text_rotation' + 'wrap_text' + 'shrink_to_fit' + 'indent' + Returns + ------- + alignment : openpyxl.styles.Alignment + """ + + from openpyxl.styles import Alignment + + return Alignment(**alignment_dict) + + @classmethod + def _convert_to_number_format(cls, number_format_dict): + """ + Convert ``number_format_dict`` to an openpyxl v2.1.0 number format + initializer. + Parameters + ---------- + number_format_dict : dict + A dict with zero or more of the following keys. + 'format_code' : str + Returns + ------- + number_format : str + """ + return number_format_dict['format_code'] + + @classmethod + def _convert_to_protection(cls, protection_dict): + """ + Convert ``protection_dict`` to an openpyxl v2 Protection object. + Parameters + ---------- + protection_dict : dict + A dict with zero or more of the following keys. + 'locked' + 'hidden' + Returns + ------- + """ + + from openpyxl.styles import Protection + + return Protection(**protection_dict) + + def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, + freeze_panes=None): + # Write the frame cells using openpyxl. + sheet_name = self._get_sheet_name(sheet_name) + + _style_cache = {} + + if sheet_name in self.sheets: + wks = self.sheets[sheet_name] + else: + wks = self.book.create_sheet() + wks.title = sheet_name + self.sheets[sheet_name] = wks + + if _validate_freeze_panes(freeze_panes): + wks.freeze_panes = wks.cell(row=freeze_panes[0] + 1, + column=freeze_panes[1] + 1) + + for cell in cells: + xcell = wks.cell( + row=startrow + cell.row + 1, + column=startcol + cell.col + 1 + ) + xcell.value, fmt = self._value_with_fmt(cell.val) + if fmt: + xcell.number_format = fmt + + style_kwargs = {} + if cell.style: + key = str(cell.style) + style_kwargs = _style_cache.get(key) + if style_kwargs is None: + style_kwargs = self._convert_to_style_kwargs(cell.style) + _style_cache[key] = style_kwargs + + if style_kwargs: + for k, v in style_kwargs.items(): + setattr(xcell, k, v) + + if cell.mergestart is not None and cell.mergeend is not None: + + wks.merge_cells( + start_row=startrow + cell.row + 1, + start_column=startcol + cell.col + 1, + end_column=startcol + cell.mergeend + 1, + end_row=startrow + cell.mergestart + 1 + ) + + # When cells are merged only the top-left cell is preserved + # The behaviour of the other cells in a merged range is + # undefined + if style_kwargs: + first_row = startrow + cell.row + 1 + last_row = startrow + cell.mergestart + 1 + first_col = startcol + cell.col + 1 + last_col = startcol + cell.mergeend + 1 + + for row in range(first_row, last_row + 1): + for col in range(first_col, last_col + 1): + if row == first_row and col == first_col: + # Ignore first cell. It is already handled. + continue + xcell = wks.cell(column=col, row=row) + for k, v in style_kwargs.items(): + setattr(xcell, k, v) diff --git a/pandas/io/excel/_util.py b/pandas/io/excel/_util.py new file mode 100644 index 0000000000000..1aeaf70f0832e --- /dev/null +++ b/pandas/io/excel/_util.py @@ -0,0 +1,260 @@ +import warnings + +import pandas.compat as compat +from pandas.compat import lrange, range + +from pandas.core.dtypes.common import is_integer, is_list_like + +from pandas.core import config + +_writer_extensions = ["xlsx", "xls", "xlsm"] + + +_writers = {} + + +def register_writer(klass): + """Adds engine to the excel writer registry. You must use this method to + integrate with ``to_excel``. Also adds config options for any new + ``supported_extensions`` defined on the writer.""" + if not callable(klass): + raise ValueError("Can only register callables as engines") + engine_name = klass.engine + _writers[engine_name] = klass + for ext in klass.supported_extensions: + if ext.startswith('.'): + ext = ext[1:] + if ext not in _writer_extensions: + config.register_option("io.excel.{ext}.writer".format(ext=ext), + engine_name, validator=str) + _writer_extensions.append(ext) + + +def _get_default_writer(ext): + _default_writers = {'xlsx': 'openpyxl', 'xlsm': 'openpyxl', 'xls': 'xlwt'} + try: + import xlsxwriter # noqa + _default_writers['xlsx'] = 'xlsxwriter' + except ImportError: + pass + return _default_writers[ext] + + +def get_writer(engine_name): + try: + return _writers[engine_name] + except KeyError: + raise ValueError("No Excel writer '{engine}'" + .format(engine=engine_name)) + + +def _excel2num(x): + """ + Convert Excel column name like 'AB' to 0-based column index. + + Parameters + ---------- + x : str + The Excel column name to convert to a 0-based column index. + + Returns + ------- + num : int + The column index corresponding to the name. + + Raises + ------ + ValueError + Part of the Excel column name was invalid. + """ + index = 0 + + for c in x.upper().strip(): + cp = ord(c) + + if cp < ord("A") or cp > ord("Z"): + raise ValueError("Invalid column name: {x}".format(x=x)) + + index = index * 26 + cp - ord("A") + 1 + + return index - 1 + + +def _range2cols(areas): + """ + Convert comma separated list of column names and ranges to indices. + + Parameters + ---------- + areas : str + A string containing a sequence of column ranges (or areas). + + Returns + ------- + cols : list + A list of 0-based column indices. + + Examples + -------- + >>> _range2cols('A:E') + [0, 1, 2, 3, 4] + >>> _range2cols('A,C,Z:AB') + [0, 2, 25, 26, 27] + """ + cols = [] + + for rng in areas.split(","): + if ":" in rng: + rng = rng.split(":") + cols.extend(lrange(_excel2num(rng[0]), _excel2num(rng[1]) + 1)) + else: + cols.append(_excel2num(rng)) + + return cols + + +def _maybe_convert_usecols(usecols): + """ + Convert `usecols` into a compatible format for parsing in `parsers.py`. + + Parameters + ---------- + usecols : object + The use-columns object to potentially convert. + + Returns + ------- + converted : object + The compatible format of `usecols`. + """ + if usecols is None: + return usecols + + if is_integer(usecols): + warnings.warn(("Passing in an integer for `usecols` has been " + "deprecated. Please pass in a list of int from " + "0 to `usecols` inclusive instead."), + FutureWarning, stacklevel=2) + return lrange(usecols + 1) + + if isinstance(usecols, compat.string_types): + return _range2cols(usecols) + + return usecols + + +def _validate_freeze_panes(freeze_panes): + if freeze_panes is not None: + if ( + len(freeze_panes) == 2 and + all(isinstance(item, int) for item in freeze_panes) + ): + return True + + raise ValueError("freeze_panes must be of form (row, column)" + " where row and column are integers") + + # freeze_panes wasn't specified, return False so it won't be applied + # to output sheet + return False + + +def _trim_excel_header(row): + # trim header row so auto-index inference works + # xlrd uses '' , openpyxl None + while len(row) > 0 and (row[0] == '' or row[0] is None): + row = row[1:] + return row + + +def _maybe_convert_to_string(row): + """ + Convert elements in a row to string from Unicode. + + This is purely a Python 2.x patch and is performed ONLY when all + elements of the row are string-like. + + Parameters + ---------- + row : array-like + The row of data to convert. + + Returns + ------- + converted : array-like + """ + if compat.PY2: + converted = [] + + for i in range(len(row)): + if isinstance(row[i], compat.string_types): + try: + converted.append(str(row[i])) + except UnicodeEncodeError: + break + else: + break + else: + row = converted + + return row + + +def _fill_mi_header(row, control_row): + """Forward fill blank entries in row but only inside the same parent index. + + Used for creating headers in Multiindex. + Parameters + ---------- + row : list + List of items in a single row. + control_row : list of bool + Helps to determine if particular column is in same parent index as the + previous value. Used to stop propagation of empty cells between + different indexes. + + Returns + ---------- + Returns changed row and control_row + """ + last = row[0] + for i in range(1, len(row)): + if not control_row[i]: + last = row[i] + + if row[i] == '' or row[i] is None: + row[i] = last + else: + control_row[i] = False + last = row[i] + + return _maybe_convert_to_string(row), control_row + +# fill blank if index_col not None + + +def _pop_header_name(row, index_col): + """ + Pop the header name for MultiIndex parsing. + + Parameters + ---------- + row : list + The data row to parse for the header name. + index_col : int, list + The index columns for our data. Assumed to be non-null. + + Returns + ------- + header_name : str + The extracted header name. + trimmed_row : list + The original data row with the header name removed. + """ + # Pop out header name and fill w/blank. + i = index_col if not is_list_like(index_col) else max(index_col) + + header_name = row[i] + header_name = None if header_name == "" else header_name + + return header_name, row[:i] + [''] + row[i + 1:] diff --git a/pandas/io/excel/_xlrd.py b/pandas/io/excel/_xlrd.py new file mode 100644 index 0000000000000..60f7d8f94a399 --- /dev/null +++ b/pandas/io/excel/_xlrd.py @@ -0,0 +1,126 @@ +from datetime import time +from distutils.version import LooseVersion +from io import UnsupportedOperation + +import numpy as np + +import pandas.compat as compat +from pandas.compat import range, zip + +from pandas.io.common import _is_url, _urlopen, get_filepath_or_buffer +from pandas.io.excel._base import _BaseExcelReader + + +class _XlrdReader(_BaseExcelReader): + + def __init__(self, filepath_or_buffer): + """Reader using xlrd engine. + + Parameters + ---------- + filepath_or_buffer : string, path object or Workbook + Object to be parsed. + """ + err_msg = "Install xlrd >= 1.0.0 for Excel support" + + try: + import xlrd + except ImportError: + raise ImportError(err_msg) + else: + if xlrd.__VERSION__ < LooseVersion("1.0.0"): + raise ImportError(err_msg + + ". Current version " + xlrd.__VERSION__) + + from pandas.io.excel._base import ExcelFile + # If filepath_or_buffer is a url, want to keep the data as bytes so + # can't pass to get_filepath_or_buffer() + if _is_url(filepath_or_buffer): + filepath_or_buffer = _urlopen(filepath_or_buffer) + elif not isinstance(filepath_or_buffer, (ExcelFile, xlrd.Book)): + filepath_or_buffer, _, _, _ = get_filepath_or_buffer( + filepath_or_buffer) + + if isinstance(filepath_or_buffer, xlrd.Book): + self.book = filepath_or_buffer + elif hasattr(filepath_or_buffer, "read"): + # N.B. xlrd.Book has a read attribute too + if hasattr(filepath_or_buffer, 'seek'): + try: + # GH 19779 + filepath_or_buffer.seek(0) + except UnsupportedOperation: + # HTTPResponse does not support seek() + # GH 20434 + pass + + data = filepath_or_buffer.read() + self.book = xlrd.open_workbook(file_contents=data) + elif isinstance(filepath_or_buffer, compat.string_types): + self.book = xlrd.open_workbook(filepath_or_buffer) + else: + raise ValueError('Must explicitly set engine if not passing in' + ' buffer or path for io.') + + @property + def sheet_names(self): + return self.book.sheet_names() + + def get_sheet_by_name(self, name): + return self.book.sheet_by_name(name) + + def get_sheet_by_index(self, index): + return self.book.sheet_by_index(index) + + def get_sheet_data(self, sheet, convert_float): + from xlrd import (xldate, XL_CELL_DATE, + XL_CELL_ERROR, XL_CELL_BOOLEAN, + XL_CELL_NUMBER) + + epoch1904 = self.book.datemode + + def _parse_cell(cell_contents, cell_typ): + """converts the contents of the cell into a pandas + appropriate object""" + + if cell_typ == XL_CELL_DATE: + + # Use the newer xlrd datetime handling. + try: + cell_contents = xldate.xldate_as_datetime( + cell_contents, epoch1904) + except OverflowError: + return cell_contents + + # Excel doesn't distinguish between dates and time, + # so we treat dates on the epoch as times only. + # Also, Excel supports 1900 and 1904 epochs. + year = (cell_contents.timetuple())[0:3] + if ((not epoch1904 and year == (1899, 12, 31)) or + (epoch1904 and year == (1904, 1, 1))): + cell_contents = time(cell_contents.hour, + cell_contents.minute, + cell_contents.second, + cell_contents.microsecond) + + elif cell_typ == XL_CELL_ERROR: + cell_contents = np.nan + elif cell_typ == XL_CELL_BOOLEAN: + cell_contents = bool(cell_contents) + elif convert_float and cell_typ == XL_CELL_NUMBER: + # GH5394 - Excel 'numbers' are always floats + # it's a minimal perf hit and less surprising + val = int(cell_contents) + if val == cell_contents: + cell_contents = val + return cell_contents + + data = [] + + for i in range(sheet.nrows): + row = [_parse_cell(value, typ) + for value, typ in zip(sheet.row_values(i), + sheet.row_types(i))] + data.append(row) + + return data diff --git a/pandas/io/excel/_xlsxwriter.py b/pandas/io/excel/_xlsxwriter.py new file mode 100644 index 0000000000000..531a3657cac6f --- /dev/null +++ b/pandas/io/excel/_xlsxwriter.py @@ -0,0 +1,218 @@ +import pandas._libs.json as json +from pandas.compat import string_types + +from pandas.io.excel._base import ExcelWriter +from pandas.io.excel._util import _validate_freeze_panes + + +class _XlsxStyler(object): + # Map from openpyxl-oriented styles to flatter xlsxwriter representation + # Ordering necessary for both determinism and because some are keyed by + # prefixes of others. + STYLE_MAPPING = { + 'font': [ + (('name',), 'font_name'), + (('sz',), 'font_size'), + (('size',), 'font_size'), + (('color', 'rgb',), 'font_color'), + (('color',), 'font_color'), + (('b',), 'bold'), + (('bold',), 'bold'), + (('i',), 'italic'), + (('italic',), 'italic'), + (('u',), 'underline'), + (('underline',), 'underline'), + (('strike',), 'font_strikeout'), + (('vertAlign',), 'font_script'), + (('vertalign',), 'font_script'), + ], + 'number_format': [ + (('format_code',), 'num_format'), + ((), 'num_format',), + ], + 'protection': [ + (('locked',), 'locked'), + (('hidden',), 'hidden'), + ], + 'alignment': [ + (('horizontal',), 'align'), + (('vertical',), 'valign'), + (('text_rotation',), 'rotation'), + (('wrap_text',), 'text_wrap'), + (('indent',), 'indent'), + (('shrink_to_fit',), 'shrink'), + ], + 'fill': [ + (('patternType',), 'pattern'), + (('patterntype',), 'pattern'), + (('fill_type',), 'pattern'), + (('start_color', 'rgb',), 'fg_color'), + (('fgColor', 'rgb',), 'fg_color'), + (('fgcolor', 'rgb',), 'fg_color'), + (('start_color',), 'fg_color'), + (('fgColor',), 'fg_color'), + (('fgcolor',), 'fg_color'), + (('end_color', 'rgb',), 'bg_color'), + (('bgColor', 'rgb',), 'bg_color'), + (('bgcolor', 'rgb',), 'bg_color'), + (('end_color',), 'bg_color'), + (('bgColor',), 'bg_color'), + (('bgcolor',), 'bg_color'), + ], + 'border': [ + (('color', 'rgb',), 'border_color'), + (('color',), 'border_color'), + (('style',), 'border'), + (('top', 'color', 'rgb',), 'top_color'), + (('top', 'color',), 'top_color'), + (('top', 'style',), 'top'), + (('top',), 'top'), + (('right', 'color', 'rgb',), 'right_color'), + (('right', 'color',), 'right_color'), + (('right', 'style',), 'right'), + (('right',), 'right'), + (('bottom', 'color', 'rgb',), 'bottom_color'), + (('bottom', 'color',), 'bottom_color'), + (('bottom', 'style',), 'bottom'), + (('bottom',), 'bottom'), + (('left', 'color', 'rgb',), 'left_color'), + (('left', 'color',), 'left_color'), + (('left', 'style',), 'left'), + (('left',), 'left'), + ], + } + + @classmethod + def convert(cls, style_dict, num_format_str=None): + """ + converts a style_dict to an xlsxwriter format dict + + Parameters + ---------- + style_dict : style dictionary to convert + num_format_str : optional number format string + """ + + # Create a XlsxWriter format object. + props = {} + + if num_format_str is not None: + props['num_format'] = num_format_str + + if style_dict is None: + return props + + if 'borders' in style_dict: + style_dict = style_dict.copy() + style_dict['border'] = style_dict.pop('borders') + + for style_group_key, style_group in style_dict.items(): + for src, dst in cls.STYLE_MAPPING.get(style_group_key, []): + # src is a sequence of keys into a nested dict + # dst is a flat key + if dst in props: + continue + v = style_group + for k in src: + try: + v = v[k] + except (KeyError, TypeError): + break + else: + props[dst] = v + + if isinstance(props.get('pattern'), string_types): + # TODO: support other fill patterns + props['pattern'] = 0 if props['pattern'] == 'none' else 1 + + for k in ['border', 'top', 'right', 'bottom', 'left']: + if isinstance(props.get(k), string_types): + try: + props[k] = ['none', 'thin', 'medium', 'dashed', 'dotted', + 'thick', 'double', 'hair', 'mediumDashed', + 'dashDot', 'mediumDashDot', 'dashDotDot', + 'mediumDashDotDot', + 'slantDashDot'].index(props[k]) + except ValueError: + props[k] = 2 + + if isinstance(props.get('font_script'), string_types): + props['font_script'] = ['baseline', 'superscript', + 'subscript'].index(props['font_script']) + + if isinstance(props.get('underline'), string_types): + props['underline'] = {'none': 0, 'single': 1, 'double': 2, + 'singleAccounting': 33, + 'doubleAccounting': 34}[props['underline']] + + return props + + +class _XlsxWriter(ExcelWriter): + engine = 'xlsxwriter' + supported_extensions = ('.xlsx',) + + def __init__(self, path, engine=None, + date_format=None, datetime_format=None, mode='w', + **engine_kwargs): + # Use the xlsxwriter module as the Excel writer. + import xlsxwriter + + if mode == 'a': + raise ValueError('Append mode is not supported with xlsxwriter!') + + super(_XlsxWriter, self).__init__(path, engine=engine, + date_format=date_format, + datetime_format=datetime_format, + mode=mode, + **engine_kwargs) + + self.book = xlsxwriter.Workbook(path, **engine_kwargs) + + def save(self): + """ + Save workbook to disk. + """ + + return self.book.close() + + def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, + freeze_panes=None): + # Write the frame cells using xlsxwriter. + sheet_name = self._get_sheet_name(sheet_name) + + if sheet_name in self.sheets: + wks = self.sheets[sheet_name] + else: + wks = self.book.add_worksheet(sheet_name) + self.sheets[sheet_name] = wks + + style_dict = {'null': None} + + if _validate_freeze_panes(freeze_panes): + wks.freeze_panes(*(freeze_panes)) + + for cell in cells: + val, fmt = self._value_with_fmt(cell.val) + + stylekey = json.dumps(cell.style) + if fmt: + stylekey += fmt + + if stylekey in style_dict: + style = style_dict[stylekey] + else: + style = self.book.add_format( + _XlsxStyler.convert(cell.style, fmt)) + style_dict[stylekey] = style + + if cell.mergestart is not None and cell.mergeend is not None: + wks.merge_range(startrow + cell.row, + startcol + cell.col, + startrow + cell.mergestart, + startcol + cell.mergeend, + cell.val, style) + else: + wks.write(startrow + cell.row, + startcol + cell.col, + val, style) diff --git a/pandas/io/excel/_xlwt.py b/pandas/io/excel/_xlwt.py new file mode 100644 index 0000000000000..191fbe914b750 --- /dev/null +++ b/pandas/io/excel/_xlwt.py @@ -0,0 +1,132 @@ +import pandas._libs.json as json + +from pandas.io.excel._base import ExcelWriter +from pandas.io.excel._util import _validate_freeze_panes + + +class _XlwtWriter(ExcelWriter): + engine = 'xlwt' + supported_extensions = ('.xls',) + + def __init__(self, path, engine=None, encoding=None, mode='w', + **engine_kwargs): + # Use the xlwt module as the Excel writer. + import xlwt + engine_kwargs['engine'] = engine + + if mode == 'a': + raise ValueError('Append mode is not supported with xlwt!') + + super(_XlwtWriter, self).__init__(path, mode=mode, **engine_kwargs) + + if encoding is None: + encoding = 'ascii' + self.book = xlwt.Workbook(encoding=encoding) + self.fm_datetime = xlwt.easyxf(num_format_str=self.datetime_format) + self.fm_date = xlwt.easyxf(num_format_str=self.date_format) + + def save(self): + """ + Save workbook to disk. + """ + return self.book.save(self.path) + + def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, + freeze_panes=None): + # Write the frame cells using xlwt. + + sheet_name = self._get_sheet_name(sheet_name) + + if sheet_name in self.sheets: + wks = self.sheets[sheet_name] + else: + wks = self.book.add_sheet(sheet_name) + self.sheets[sheet_name] = wks + + if _validate_freeze_panes(freeze_panes): + wks.set_panes_frozen(True) + wks.set_horz_split_pos(freeze_panes[0]) + wks.set_vert_split_pos(freeze_panes[1]) + + style_dict = {} + + for cell in cells: + val, fmt = self._value_with_fmt(cell.val) + + stylekey = json.dumps(cell.style) + if fmt: + stylekey += fmt + + if stylekey in style_dict: + style = style_dict[stylekey] + else: + style = self._convert_to_style(cell.style, fmt) + style_dict[stylekey] = style + + if cell.mergestart is not None and cell.mergeend is not None: + wks.write_merge(startrow + cell.row, + startrow + cell.mergestart, + startcol + cell.col, + startcol + cell.mergeend, + val, style) + else: + wks.write(startrow + cell.row, + startcol + cell.col, + val, style) + + @classmethod + def _style_to_xlwt(cls, item, firstlevel=True, field_sep=',', + line_sep=';'): + """helper which recursively generate an xlwt easy style string + for example: + + hstyle = {"font": {"bold": True}, + "border": {"top": "thin", + "right": "thin", + "bottom": "thin", + "left": "thin"}, + "align": {"horiz": "center"}} + will be converted to + font: bold on; \ + border: top thin, right thin, bottom thin, left thin; \ + align: horiz center; + """ + if hasattr(item, 'items'): + if firstlevel: + it = ["{key}: {val}" + .format(key=key, val=cls._style_to_xlwt(value, False)) + for key, value in item.items()] + out = "{sep} ".format(sep=(line_sep).join(it)) + return out + else: + it = ["{key} {val}" + .format(key=key, val=cls._style_to_xlwt(value, False)) + for key, value in item.items()] + out = "{sep} ".format(sep=(field_sep).join(it)) + return out + else: + item = "{item}".format(item=item) + item = item.replace("True", "on") + item = item.replace("False", "off") + return item + + @classmethod + def _convert_to_style(cls, style_dict, num_format_str=None): + """ + converts a style_dict to an xlwt style object + Parameters + ---------- + style_dict : style dictionary to convert + num_format_str : optional number format string + """ + import xlwt + + if style_dict: + xlwt_stylestr = cls._style_to_xlwt(style_dict) + style = xlwt.easyxf(xlwt_stylestr, field_sep=',', line_sep=';') + else: + style = xlwt.XFStyle() + if num_format_str is not None: + style.num_format_str = num_format_str + + return style From 57fb83f56f5a78f422104d7eac0811836f855ddf Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Mon, 11 Feb 2019 06:32:53 -0800 Subject: [PATCH 100/215] API: Ensure DatetimeTZDtype standardizes pytz timezones (#25254) * API: Ensure DatetimeTZDtype standardizes pytz timezones * Add whatsnew --- doc/source/user_guide/timeseries.rst | 24 ++++++++++++++++++++++++ doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/dtypes/dtypes.py | 1 + pandas/tests/dtypes/test_dtypes.py | 10 ++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/doc/source/user_guide/timeseries.rst b/doc/source/user_guide/timeseries.rst index 5841125817d03..23f1aabd69ff3 100644 --- a/doc/source/user_guide/timeseries.rst +++ b/doc/source/user_guide/timeseries.rst @@ -321,6 +321,15 @@ which can be specified. These are computed from the starting point specified by pd.to_datetime([1349720105100, 1349720105200, 1349720105300, 1349720105400, 1349720105500], unit='ms') +Constructing a :class:`Timestamp` or :class:`DatetimeIndex` with an epoch timestamp +with the ``tz`` argument specified will localize the epoch timestamps to UTC +first then convert the result to the specified time zone. + +.. ipython:: python + + pd.Timestamp(1262347200000000000, tz='US/Pacific') + pd.DatetimeIndex([1262347200000000000], tz='US/Pacific') + .. note:: Epoch times will be rounded to the nearest nanosecond. @@ -2205,6 +2214,21 @@ you can use the ``tz_convert`` method. rng_pytz.tz_convert('US/Eastern') +.. note:: + + When using ``pytz`` time zones, :class:`DatetimeIndex` will construct a different + time zone object than a :class:`Timestamp` for the same time zone input. A :class:`DatetimeIndex` + can hold a collection of :class:`Timestamp` objects that may have different UTC offsets and cannot be + succinctly represented by one ``pytz`` time zone instance while one :class:`Timestamp` + represents one point in time with a specific UTC offset. + + .. ipython:: python + + dti = pd.date_range('2019-01-01', periods=3, freq='D', tz='US/Pacific') + dti.tz + ts = pd.Timestamp('2019-01-01', tz='US/Pacific') + ts.tz + .. warning:: Be wary of conversions between libraries. For some time zones, ``pytz`` and ``dateutil`` have different diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 95c8d6e2cf6a3..dcb0cbb064f21 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -33,7 +33,7 @@ Backwards incompatible API changes Other API Changes ^^^^^^^^^^^^^^^^^ -- +- :class:`DatetimeTZDtype` will now standardize pytz timezones to a common timezone instance (:issue:`24713`) - - diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index 8b9ac680493a1..f187d786d9f61 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -639,6 +639,7 @@ def __init__(self, unit="ns", tz=None): if tz: tz = timezones.maybe_get_tz(tz) + tz = timezones.tz_standardize(tz) elif tz is not None: raise pytz.UnknownTimeZoneError(tz) elif tz is None: diff --git a/pandas/tests/dtypes/test_dtypes.py b/pandas/tests/dtypes/test_dtypes.py index 0fe0a845f5129..710f215686eab 100644 --- a/pandas/tests/dtypes/test_dtypes.py +++ b/pandas/tests/dtypes/test_dtypes.py @@ -3,6 +3,7 @@ import numpy as np import pytest +import pytz from pandas.core.dtypes.common import ( is_bool_dtype, is_categorical, is_categorical_dtype, @@ -302,6 +303,15 @@ def test_empty(self): with pytest.raises(TypeError, match="A 'tz' is required."): DatetimeTZDtype() + def test_tz_standardize(self): + # GH 24713 + tz = pytz.timezone('US/Eastern') + dr = date_range('2013-01-01', periods=3, tz='US/Eastern') + dtype = DatetimeTZDtype('ns', dr.tz) + assert dtype.tz == tz + dtype = DatetimeTZDtype('ns', dr[0].tz) + assert dtype.tz == tz + class TestPeriodDtype(Base): From ea1d5f52a329ba24f1b4424f1e8f00cf9c8485c8 Mon Sep 17 00:00:00 2001 From: Nicholas Musolino Date: Mon, 11 Feb 2019 11:08:47 -0500 Subject: [PATCH 101/215] BUG: Fix exceptions when Series.interpolate's `order` parameter is missing or invalid (#25246) * BUG: raise accurate exception from Series.interpolate (#24014) * Actually validate `order` before use in spline * Remove unnecessary check and dead code * Clean up comparison/tests based on feedback * Include invalid order value in exception * Check for NaN order in spline validation * Add whatsnew entry for bug fix * CLN: Make unit tests assert one error at a time * CLN: break test into distinct test case * PEP8 fix in test module * CLN: Test fixture for interpolate methods --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/internals/blocks.py | 30 ++++----- pandas/core/missing.py | 7 ++- pandas/tests/series/test_missing.py | 97 ++++++++++++++++++----------- 4 files changed, 78 insertions(+), 58 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index dcb0cbb064f21..11e735028a7d5 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -141,7 +141,7 @@ Indexing Missing ^^^^^^^ -- +- Fixed misleading exception message in :meth:`Series.missing` if argument ``order`` is required, but omitted (:issue:`10633`, :issue:`24014`). - - diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index ac7d21de442db..4e2c04dba8b04 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -1115,24 +1115,18 @@ def check_int_bool(self, inplace): fill_value=fill_value, coerce=coerce, downcast=downcast) - # try an interp method - try: - m = missing.clean_interp_method(method, **kwargs) - except ValueError: - m = None - - if m is not None: - r = check_int_bool(self, inplace) - if r is not None: - return r - return self._interpolate(method=m, index=index, values=values, - axis=axis, limit=limit, - limit_direction=limit_direction, - limit_area=limit_area, - fill_value=fill_value, inplace=inplace, - downcast=downcast, **kwargs) - - raise ValueError("invalid method '{0}' to interpolate.".format(method)) + # validate the interp method + m = missing.clean_interp_method(method, **kwargs) + + r = check_int_bool(self, inplace) + if r is not None: + return r + return self._interpolate(method=m, index=index, values=values, + axis=axis, limit=limit, + limit_direction=limit_direction, + limit_area=limit_area, + fill_value=fill_value, inplace=inplace, + downcast=downcast, **kwargs) def _interpolate_with_fill(self, method='pad', axis=0, inplace=False, limit=None, fill_value=None, coerce=False, diff --git a/pandas/core/missing.py b/pandas/core/missing.py index cc7bdf95200d1..9acdb1a06b2d1 100644 --- a/pandas/core/missing.py +++ b/pandas/core/missing.py @@ -293,9 +293,10 @@ def _interpolate_scipy_wrapper(x, y, new_x, method, fill_value=None, bounds_error=bounds_error) new_y = terp(new_x) elif method == 'spline': - # GH #10633 - if not order: - raise ValueError("order needs to be specified and greater than 0") + # GH #10633, #24014 + if isna(order) or (order <= 0): + raise ValueError("order needs to be specified and greater than 0; " + "got order: {}".format(order)) terp = interpolate.UnivariateSpline(x, y, k=order, **kwargs) new_y = terp(new_x) else: diff --git a/pandas/tests/series/test_missing.py b/pandas/tests/series/test_missing.py index 985288c439917..f07dd1dfb5fda 100644 --- a/pandas/tests/series/test_missing.py +++ b/pandas/tests/series/test_missing.py @@ -854,8 +854,23 @@ def test_series_pad_backfill_limit(self): assert_series_equal(result, expected) -class TestSeriesInterpolateData(): +@pytest.fixture(params=['linear', 'index', 'values', 'nearest', 'slinear', + 'zero', 'quadratic', 'cubic', 'barycentric', 'krogh', + 'polynomial', 'spline', 'piecewise_polynomial', + 'from_derivatives', 'pchip', 'akima', ]) +def nontemporal_method(request): + """ Fixture that returns an (method name, required kwargs) pair. + + This fixture does not include method 'time' as a parameterization; that + method requires a Series with a DatetimeIndex, and is generally tested + separately from these non-temporal methods. + """ + method = request.param + kwargs = dict(order=1) if method in ('spline', 'polynomial') else dict() + return method, kwargs + +class TestSeriesInterpolateData(): def test_interpolate(self, datetime_series, string_series): ts = Series(np.arange(len(datetime_series), dtype=float), datetime_series.index) @@ -875,12 +890,12 @@ def test_interpolate(self, datetime_series, string_series): time_interp = ord_ts_copy.interpolate(method='time') tm.assert_series_equal(time_interp, ord_ts) - # try time interpolation on a non-TimeSeries - # Only raises ValueError if there are NaNs. - non_ts = string_series.copy() - non_ts[0] = np.NaN - msg = ("time-weighted interpolation only works on Series or DataFrames" - " with a DatetimeIndex") + def test_interpolate_time_raises_for_non_timeseries(self): + # When method='time' is used on a non-TimeSeries that contains a null + # value, a ValueError should be raised. + non_ts = Series([0, 1, 2, np.NaN]) + msg = ("time-weighted interpolation only works on Series.* " + "with a DatetimeIndex") with pytest.raises(ValueError, match=msg): non_ts.interpolate(method='time') @@ -1061,21 +1076,35 @@ def test_interp_limit(self): result = s.interpolate(method='linear', limit=2) assert_series_equal(result, expected) - # GH 9217, make sure limit is an int and greater than 0 - methods = ['linear', 'time', 'index', 'values', 'nearest', 'zero', - 'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh', - 'polynomial', 'spline', 'piecewise_polynomial', None, - 'from_derivatives', 'pchip', 'akima'] - s = pd.Series([1, 2, np.nan, np.nan, 5]) - msg = (r"Limit must be greater than 0|" - "time-weighted interpolation only works on Series or" - r" DataFrames with a DatetimeIndex|" - r"invalid method '(polynomial|spline|None)' to interpolate|" - "Limit must be an integer") - for limit in [-1, 0, 1., 2.]: - for method in methods: - with pytest.raises(ValueError, match=msg): - s.interpolate(limit=limit, method=method) + @pytest.mark.parametrize("limit", [-1, 0]) + def test_interpolate_invalid_nonpositive_limit(self, nontemporal_method, + limit): + # GH 9217: make sure limit is greater than zero. + s = pd.Series([1, 2, np.nan, 4]) + method, kwargs = nontemporal_method + with pytest.raises(ValueError, match="Limit must be greater than 0"): + s.interpolate(limit=limit, method=method, **kwargs) + + def test_interpolate_invalid_float_limit(self, nontemporal_method): + # GH 9217: make sure limit is an integer. + s = pd.Series([1, 2, np.nan, 4]) + method, kwargs = nontemporal_method + limit = 2.0 + with pytest.raises(ValueError, match="Limit must be an integer"): + s.interpolate(limit=limit, method=method, **kwargs) + + @pytest.mark.parametrize("invalid_method", [None, 'nonexistent_method']) + def test_interp_invalid_method(self, invalid_method): + s = Series([1, 3, np.nan, 12, np.nan, 25]) + + msg = "method must be one of.* Got '{}' instead".format(invalid_method) + with pytest.raises(ValueError, match=msg): + s.interpolate(method=invalid_method) + + # When an invalid method and invalid limit (such as -1) are + # provided, the error message reflects the invalid method. + with pytest.raises(ValueError, match=msg): + s.interpolate(method=invalid_method, limit=-1) def test_interp_limit_forward(self): s = Series([1, 3, np.nan, np.nan, np.nan, 11]) @@ -1276,11 +1305,20 @@ def test_interp_limit_no_nans(self): @td.skip_if_no_scipy @pytest.mark.parametrize("method", ['polynomial', 'spline']) def test_no_order(self, method): + # see GH-10633, GH-24014 s = Series([0, 1, np.nan, 3]) - msg = "invalid method '{}' to interpolate".format(method) + msg = "You must specify the order of the spline or polynomial" with pytest.raises(ValueError, match=msg): s.interpolate(method=method) + @td.skip_if_no_scipy + @pytest.mark.parametrize('order', [-1, -1.0, 0, 0.0, np.nan]) + def test_interpolate_spline_invalid_order(self, order): + s = Series([0, 1, np.nan, 3]) + msg = "order needs to be specified and greater than 0" + with pytest.raises(ValueError, match=msg): + s.interpolate(method='spline', order=order) + @td.skip_if_no_scipy def test_spline(self): s = Series([1, 2, np.nan, 4, 5, np.nan, 7]) @@ -1313,19 +1351,6 @@ def test_spline_interpolation(self): expected1 = s.interpolate(method='spline', order=1) assert_series_equal(result1, expected1) - @td.skip_if_no_scipy - def test_spline_error(self): - # see gh-10633 - s = pd.Series(np.arange(10) ** 2) - s[np.random.randint(0, 9, 3)] = np.nan - msg = "invalid method 'spline' to interpolate" - with pytest.raises(ValueError, match=msg): - s.interpolate(method='spline') - - msg = "order needs to be specified and greater than 0" - with pytest.raises(ValueError, match=msg): - s.interpolate(method='spline', order=0) - def test_interp_timedelta64(self): # GH 6424 df = Series([1, np.nan, 3], From a9a03a25d82ee285b6da48bfb20b9057d20bb9e7 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Mon, 11 Feb 2019 10:53:42 -0800 Subject: [PATCH 102/215] BUG: DataFrame.join on tz-aware DatetimeIndex (#25260) --- doc/source/whatsnew/v0.24.2.rst | 2 +- doc/source/whatsnew/v0.25.0.rst | 1 - pandas/core/reshape/merge.py | 2 +- pandas/tests/reshape/merge/test_join.py | 22 ++++++++++++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 5ae777ca68eba..f17c4974cd450 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -79,7 +79,7 @@ Bug Fixes **Reshaping** - -- +- Bug in :func:`DataFrame.join` when joining on a timezone aware :class:`DatetimeIndex` (:issue:`23931`) - **Visualization** diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 11e735028a7d5..95362521f3b9f 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -183,7 +183,6 @@ Reshaping - Bug in :func:`pandas.merge` adds a string of ``None`` if ``None`` is assigned in suffixes instead of remain the column name as-is (:issue:`24782`). - Bug in :func:`merge` when merging by index name would sometimes result in an incorrectly numbered index (:issue:`24212`) - :func:`to_records` now accepts dtypes to its `column_dtypes` parameter (:issue:`24895`) -- Sparse diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py index ad3327e694b67..fb50a3c60f705 100644 --- a/pandas/core/reshape/merge.py +++ b/pandas/core/reshape/merge.py @@ -909,7 +909,7 @@ def _get_merge_keys(self): in zip(self.right.index.levels, self.right.index.codes)] else: - right_keys = [self.right.index.values] + right_keys = [self.right.index._values] elif _any(self.right_on): for k in self.right_on: if is_rkey(k): diff --git a/pandas/tests/reshape/merge/test_join.py b/pandas/tests/reshape/merge/test_join.py index 5d7a9ab6f4cf0..62c9047b17f3d 100644 --- a/pandas/tests/reshape/merge/test_join.py +++ b/pandas/tests/reshape/merge/test_join.py @@ -682,6 +682,28 @@ def test_join_multi_to_multi(self, join_type): with pytest.raises(ValueError, match=msg): right.join(left, on=['abc', 'xy'], how=join_type) + def test_join_on_tz_aware_datetimeindex(self): + # GH 23931 + df1 = pd.DataFrame( + { + 'date': pd.date_range(start='2018-01-01', periods=5, + tz='America/Chicago'), + 'vals': list('abcde') + } + ) + + df2 = pd.DataFrame( + { + 'date': pd.date_range(start='2018-01-03', periods=5, + tz='America/Chicago'), + 'vals_2': list('tuvwx') + } + ) + result = df1.join(df2.set_index('date'), on='date') + expected = df1.copy() + expected['vals_2'] = pd.Series([np.nan] * len(expected), dtype=object) + assert_frame_equal(result, expected) + def _check_join(left, right, result, join_col, how='left', lsuffix='_x', rsuffix='_y'): From ec5f911e6042b2efce40df3cea0d23ba15981c6d Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 11 Feb 2019 11:37:53 -0800 Subject: [PATCH 103/215] REF: use _constructor and ABCFoo to avoid runtime imports (#25272) --- pandas/core/dtypes/dtypes.py | 10 ++++------ pandas/core/dtypes/missing.py | 8 ++++---- pandas/core/tools/datetimes.py | 8 +++----- pandas/core/tools/timedeltas.py | 7 +++---- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index f187d786d9f61..640d43f3b0e03 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -8,7 +8,8 @@ from pandas._libs.interval import Interval from pandas._libs.tslibs import NaT, Period, Timestamp, timezones -from pandas.core.dtypes.generic import ABCCategoricalIndex, ABCIndexClass +from pandas.core.dtypes.generic import ( + ABCCategoricalIndex, ABCDateOffset, ABCIndexClass) from pandas import compat @@ -758,8 +759,7 @@ def __new__(cls, freq=None): # empty constructor for pickle compat return object.__new__(cls) - from pandas.tseries.offsets import DateOffset - if not isinstance(freq, DateOffset): + if not isinstance(freq, ABCDateOffset): freq = cls._parse_dtype_strict(freq) try: @@ -790,12 +790,10 @@ def construct_from_string(cls, string): Strict construction from a string, raise a TypeError if not possible """ - from pandas.tseries.offsets import DateOffset - if (isinstance(string, compat.string_types) and (string.startswith('period[') or string.startswith('Period[')) or - isinstance(string, DateOffset)): + isinstance(string, ABCDateOffset)): # do not parse string like U as period[U] # avoid tuple to be regarded as freq try: diff --git a/pandas/core/dtypes/missing.py b/pandas/core/dtypes/missing.py index 3c6d3f212342b..697c58a365233 100644 --- a/pandas/core/dtypes/missing.py +++ b/pandas/core/dtypes/missing.py @@ -221,8 +221,8 @@ def _isna_ndarraylike(obj): # box if isinstance(obj, ABCSeries): - from pandas import Series - result = Series(result, index=obj.index, name=obj.name, copy=False) + result = obj._constructor( + result, index=obj.index, name=obj.name, copy=False) return result @@ -250,8 +250,8 @@ def _isna_ndarraylike_old(obj): # box if isinstance(obj, ABCSeries): - from pandas import Series - result = Series(result, index=obj.index, name=obj.name, copy=False) + result = obj._constructor( + result, index=obj.index, name=obj.name, copy=False) return result diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py index 3da349c570274..0c76ac6cd75ac 100644 --- a/pandas/core/tools/datetimes.py +++ b/pandas/core/tools/datetimes.py @@ -588,9 +588,8 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, if not cache_array.empty: result = arg.map(cache_array) else: - from pandas import Series values = convert_listlike(arg._values, True, format) - result = Series(values, index=arg.index, name=arg.name) + result = arg._constructor(values, index=arg.index, name=arg.name) elif isinstance(arg, (ABCDataFrame, compat.MutableMapping)): result = _assemble_from_unit_mappings(arg, errors, box, tz) elif isinstance(arg, ABCIndexClass): @@ -827,7 +826,6 @@ def to_time(arg, format=None, infer_time_format=False, errors='raise'): ------- datetime.time """ - from pandas.core.series import Series def _convert_listlike(arg, format): @@ -892,9 +890,9 @@ def _convert_listlike(arg, format): return arg elif isinstance(arg, time): return arg - elif isinstance(arg, Series): + elif isinstance(arg, ABCSeries): values = _convert_listlike(arg._values, format) - return Series(values, index=arg.index, name=arg.name) + return arg._constructor(values, index=arg.index, name=arg.name) elif isinstance(arg, ABCIndexClass): return _convert_listlike(arg, format) elif is_list_like(arg): diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index 30cb15f311b9f..7ebaf3056e79e 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -6,12 +6,12 @@ import numpy as np +from pandas._libs.tslibs import NaT from pandas._libs.tslibs.timedeltas import Timedelta, parse_timedelta_unit from pandas.core.dtypes.common import is_list_like from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries -import pandas as pd from pandas.core.arrays.timedeltas import sequence_to_td64ns @@ -100,10 +100,9 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'): if arg is None: return arg elif isinstance(arg, ABCSeries): - from pandas import Series values = _convert_listlike(arg._values, unit=unit, box=False, errors=errors) - return Series(values, index=arg.index, name=arg.name) + return arg._constructor(values, index=arg.index, name=arg.name) elif isinstance(arg, ABCIndexClass): return _convert_listlike(arg, unit=unit, box=box, errors=errors, name=arg.name) @@ -136,7 +135,7 @@ def _coerce_scalar_to_timedelta_type(r, unit='ns', box=True, errors='raise'): return r # coerce - result = pd.NaT + result = NaT return result From 64d8948a87d4d4330285692a86597437b29377a7 Mon Sep 17 00:00:00 2001 From: Noam Hershtig Date: Mon, 11 Feb 2019 21:39:27 +0200 Subject: [PATCH 104/215] Refactor groupby group_prod, group_var, group_mean, group_ohlc (#25249) --- pandas/_libs/groupby.pyx | 217 ++++++++++++++++++++++++++++- pandas/_libs/groupby_helper.pxi.in | 213 ---------------------------- pandas/tests/test_algos.py | 4 +- 3 files changed, 216 insertions(+), 218 deletions(-) diff --git a/pandas/_libs/groupby.pyx b/pandas/_libs/groupby.pyx index 950ba3f89ffb7..e6b6e2c8a0055 100644 --- a/pandas/_libs/groupby.pyx +++ b/pandas/_libs/groupby.pyx @@ -382,6 +382,10 @@ def group_any_all(uint8_t[:] out, if values[i] == flag_val: out[lab] = flag_val +# ---------------------------------------------------------------------- +# group_add, group_prod, group_var, group_mean, group_ohlc +# ---------------------------------------------------------------------- + @cython.wraparound(False) @cython.boundscheck(False) @@ -396,9 +400,9 @@ def _group_add(floating[:, :] out, cdef: Py_ssize_t i, j, N, K, lab, ncounts = len(counts) floating val, count - ndarray[floating, ndim=2] sumx, nobs + floating[:, :] sumx, nobs - if not len(values) == len(labels): + if len(values) != len(labels): raise AssertionError("len(index) != len(labels)") nobs = np.zeros_like(out) @@ -407,7 +411,6 @@ def _group_add(floating[:, :] out, N, K = (values).shape with nogil: - for i in range(N): lab = labels[i] if lab < 0: @@ -433,5 +436,213 @@ def _group_add(floating[:, :] out, group_add_float32 = _group_add['float'] group_add_float64 = _group_add['double'] + +@cython.wraparound(False) +@cython.boundscheck(False) +def _group_prod(floating[:, :] out, + int64_t[:] counts, + floating[:, :] values, + const int64_t[:] labels, + Py_ssize_t min_count=0): + """ + Only aggregates on axis=0 + """ + cdef: + Py_ssize_t i, j, N, K, lab, ncounts = len(counts) + floating val, count + floating[:, :] prodx, nobs + + if not len(values) == len(labels): + raise AssertionError("len(index) != len(labels)") + + nobs = np.zeros_like(out) + prodx = np.ones_like(out) + + N, K = (values).shape + + with nogil: + for i in range(N): + lab = labels[i] + if lab < 0: + continue + + counts[lab] += 1 + for j in range(K): + val = values[i, j] + + # not nan + if val == val: + nobs[lab, j] += 1 + prodx[lab, j] *= val + + for i in range(ncounts): + for j in range(K): + if nobs[i, j] < min_count: + out[i, j] = NAN + else: + out[i, j] = prodx[i, j] + + +group_prod_float32 = _group_prod['float'] +group_prod_float64 = _group_prod['double'] + + +@cython.wraparound(False) +@cython.boundscheck(False) +@cython.cdivision(True) +def _group_var(floating[:, :] out, + int64_t[:] counts, + floating[:, :] values, + const int64_t[:] labels, + Py_ssize_t min_count=-1): + cdef: + Py_ssize_t i, j, N, K, lab, ncounts = len(counts) + floating val, ct, oldmean + floating[:, :] nobs, mean + + assert min_count == -1, "'min_count' only used in add and prod" + + if not len(values) == len(labels): + raise AssertionError("len(index) != len(labels)") + + nobs = np.zeros_like(out) + mean = np.zeros_like(out) + + N, K = (values).shape + + out[:, :] = 0.0 + + with nogil: + for i in range(N): + lab = labels[i] + if lab < 0: + continue + + counts[lab] += 1 + + for j in range(K): + val = values[i, j] + + # not nan + if val == val: + nobs[lab, j] += 1 + oldmean = mean[lab, j] + mean[lab, j] += (val - oldmean) / nobs[lab, j] + out[lab, j] += (val - mean[lab, j]) * (val - oldmean) + + for i in range(ncounts): + for j in range(K): + ct = nobs[i, j] + if ct < 2: + out[i, j] = NAN + else: + out[i, j] /= (ct - 1) + + +group_var_float32 = _group_var['float'] +group_var_float64 = _group_var['double'] + + +@cython.wraparound(False) +@cython.boundscheck(False) +def _group_mean(floating[:, :] out, + int64_t[:] counts, + floating[:, :] values, + const int64_t[:] labels, + Py_ssize_t min_count=-1): + cdef: + Py_ssize_t i, j, N, K, lab, ncounts = len(counts) + floating val, count + floating[:, :] sumx, nobs + + assert min_count == -1, "'min_count' only used in add and prod" + + if not len(values) == len(labels): + raise AssertionError("len(index) != len(labels)") + + nobs = np.zeros_like(out) + sumx = np.zeros_like(out) + + N, K = (values).shape + + with nogil: + for i in range(N): + lab = labels[i] + if lab < 0: + continue + + counts[lab] += 1 + for j in range(K): + val = values[i, j] + # not nan + if val == val: + nobs[lab, j] += 1 + sumx[lab, j] += val + + for i in range(ncounts): + for j in range(K): + count = nobs[i, j] + if nobs[i, j] == 0: + out[i, j] = NAN + else: + out[i, j] = sumx[i, j] / count + + +group_mean_float32 = _group_mean['float'] +group_mean_float64 = _group_mean['double'] + + +@cython.wraparound(False) +@cython.boundscheck(False) +def _group_ohlc(floating[:, :] out, + int64_t[:] counts, + floating[:, :] values, + const int64_t[:] labels, + Py_ssize_t min_count=-1): + """ + Only aggregates on axis=0 + """ + cdef: + Py_ssize_t i, j, N, K, lab + floating val, count + Py_ssize_t ngroups = len(counts) + + assert min_count == -1, "'min_count' only used in add and prod" + + if len(labels) == 0: + return + + N, K = (values).shape + + if out.shape[1] != 4: + raise ValueError('Output array must have 4 columns') + + if K > 1: + raise NotImplementedError("Argument 'values' must have only " + "one dimension") + out[:] = np.nan + + with nogil: + for i in range(N): + lab = labels[i] + if lab == -1: + continue + + counts[lab] += 1 + val = values[i, 0] + if val != val: + continue + + if out[lab, 0] != out[lab, 0]: + out[lab, 0] = out[lab, 1] = out[lab, 2] = out[lab, 3] = val + else: + out[lab, 1] = max(out[lab, 1], val) + out[lab, 2] = min(out[lab, 2], val) + out[lab, 3] = val + + +group_ohlc_float32 = _group_ohlc['float'] +group_ohlc_float64 = _group_ohlc['double'] + # generated from template include "groupby_helper.pxi" diff --git a/pandas/_libs/groupby_helper.pxi.in b/pandas/_libs/groupby_helper.pxi.in index db7018e1a7254..63cd4d6ac6ff2 100644 --- a/pandas/_libs/groupby_helper.pxi.in +++ b/pandas/_libs/groupby_helper.pxi.in @@ -8,219 +8,6 @@ cdef extern from "numpy/npy_math.h": float64_t NAN "NPY_NAN" _int64_max = np.iinfo(np.int64).max -# ---------------------------------------------------------------------- -# group_prod, group_var, group_mean, group_ohlc -# ---------------------------------------------------------------------- - -{{py: - -# name, c_type -dtypes = [('float64', 'float64_t'), - ('float32', 'float32_t')] - -def get_dispatch(dtypes): - - for name, c_type in dtypes: - yield name, c_type -}} - -{{for name, c_type in get_dispatch(dtypes)}} - - -@cython.wraparound(False) -@cython.boundscheck(False) -def group_prod_{{name}}({{c_type}}[:, :] out, - int64_t[:] counts, - {{c_type}}[:, :] values, - const int64_t[:] labels, - Py_ssize_t min_count=0): - """ - Only aggregates on axis=0 - """ - cdef: - Py_ssize_t i, j, N, K, lab, ncounts = len(counts) - {{c_type}} val, count - ndarray[{{c_type}}, ndim=2] prodx, nobs - - if not len(values) == len(labels): - raise AssertionError("len(index) != len(labels)") - - nobs = np.zeros_like(out) - prodx = np.ones_like(out) - - N, K = (values).shape - - with nogil: - for i in range(N): - lab = labels[i] - if lab < 0: - continue - - counts[lab] += 1 - for j in range(K): - val = values[i, j] - - # not nan - if val == val: - nobs[lab, j] += 1 - prodx[lab, j] *= val - - for i in range(ncounts): - for j in range(K): - if nobs[i, j] < min_count: - out[i, j] = NAN - else: - out[i, j] = prodx[i, j] - - -@cython.wraparound(False) -@cython.boundscheck(False) -@cython.cdivision(True) -def group_var_{{name}}({{c_type}}[:, :] out, - int64_t[:] counts, - {{c_type}}[:, :] values, - const int64_t[:] labels, - Py_ssize_t min_count=-1): - cdef: - Py_ssize_t i, j, N, K, lab, ncounts = len(counts) - {{c_type}} val, ct, oldmean - ndarray[{{c_type}}, ndim=2] nobs, mean - - assert min_count == -1, "'min_count' only used in add and prod" - - if not len(values) == len(labels): - raise AssertionError("len(index) != len(labels)") - - nobs = np.zeros_like(out) - mean = np.zeros_like(out) - - N, K = (values).shape - - out[:, :] = 0.0 - - with nogil: - for i in range(N): - lab = labels[i] - if lab < 0: - continue - - counts[lab] += 1 - - for j in range(K): - val = values[i, j] - - # not nan - if val == val: - nobs[lab, j] += 1 - oldmean = mean[lab, j] - mean[lab, j] += (val - oldmean) / nobs[lab, j] - out[lab, j] += (val - mean[lab, j]) * (val - oldmean) - - for i in range(ncounts): - for j in range(K): - ct = nobs[i, j] - if ct < 2: - out[i, j] = NAN - else: - out[i, j] /= (ct - 1) -# add passing bin edges, instead of labels - - -@cython.wraparound(False) -@cython.boundscheck(False) -def group_mean_{{name}}({{c_type}}[:, :] out, - int64_t[:] counts, - {{c_type}}[:, :] values, - const int64_t[:] labels, - Py_ssize_t min_count=-1): - cdef: - Py_ssize_t i, j, N, K, lab, ncounts = len(counts) - {{c_type}} val, count - ndarray[{{c_type}}, ndim=2] sumx, nobs - - assert min_count == -1, "'min_count' only used in add and prod" - - if not len(values) == len(labels): - raise AssertionError("len(index) != len(labels)") - - nobs = np.zeros_like(out) - sumx = np.zeros_like(out) - - N, K = (values).shape - - with nogil: - for i in range(N): - lab = labels[i] - if lab < 0: - continue - - counts[lab] += 1 - for j in range(K): - val = values[i, j] - # not nan - if val == val: - nobs[lab, j] += 1 - sumx[lab, j] += val - - for i in range(ncounts): - for j in range(K): - count = nobs[i, j] - if nobs[i, j] == 0: - out[i, j] = NAN - else: - out[i, j] = sumx[i, j] / count - - -@cython.wraparound(False) -@cython.boundscheck(False) -def group_ohlc_{{name}}({{c_type}}[:, :] out, - int64_t[:] counts, - {{c_type}}[:, :] values, - const int64_t[:] labels, - Py_ssize_t min_count=-1): - """ - Only aggregates on axis=0 - """ - cdef: - Py_ssize_t i, j, N, K, lab - {{c_type}} val, count - Py_ssize_t ngroups = len(counts) - - assert min_count == -1, "'min_count' only used in add and prod" - - if len(labels) == 0: - return - - N, K = (values).shape - - if out.shape[1] != 4: - raise ValueError('Output array must have 4 columns') - - if K > 1: - raise NotImplementedError("Argument 'values' must have only " - "one dimension") - out[:] = np.nan - - with nogil: - for i in range(N): - lab = labels[i] - if lab == -1: - continue - - counts[lab] += 1 - val = values[i, 0] - if val != val: - continue - - if out[lab, 0] != out[lab, 0]: - out[lab, 0] = out[lab, 1] = out[lab, 2] = out[lab, 3] = val - else: - out[lab, 1] = max(out[lab, 1], val) - out[lab, 2] = min(out[lab, 2], val) - out[lab, 3] = val - -{{endfor}} - # ---------------------------------------------------------------------- # group_nth, group_last, group_rank # ---------------------------------------------------------------------- diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py index 3d28b17750540..888cf78a1c66a 100644 --- a/pandas/tests/test_algos.py +++ b/pandas/tests/test_algos.py @@ -1222,7 +1222,7 @@ def test_group_var_constant(self): class TestGroupVarFloat64(GroupVarTestMixin): __test__ = True - algo = libgroupby.group_var_float64 + algo = staticmethod(libgroupby.group_var_float64) dtype = np.float64 rtol = 1e-5 @@ -1245,7 +1245,7 @@ def test_group_var_large_inputs(self): class TestGroupVarFloat32(GroupVarTestMixin): __test__ = True - algo = libgroupby.group_var_float32 + algo = staticmethod(libgroupby.group_var_float32) dtype = np.float32 rtol = 1e-2 From 3ab9318f2c0806a7fd38d4b4dc47bf6c3803c1e7 Mon Sep 17 00:00:00 2001 From: Irv Lustig Date: Mon, 11 Feb 2019 14:40:16 -0500 Subject: [PATCH 105/215] Fix typo in Cheat sheet with regex (#25215) --- doc/cheatsheet/Pandas_Cheat_Sheet.pdf | Bin 345999 -> 345905 bytes doc/cheatsheet/Pandas_Cheat_Sheet.pptx | Bin 105004 -> 105278 bytes doc/cheatsheet/Pandas_Cheat_Sheet_JA.pdf | Bin 420841 -> 420632 bytes doc/cheatsheet/Pandas_Cheat_Sheet_JA.pptx | Bin 82359 -> 82563 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/cheatsheet/Pandas_Cheat_Sheet.pdf b/doc/cheatsheet/Pandas_Cheat_Sheet.pdf index d50896dc5ccc52afb31899a9ed704149d3e1688e..48da05d053b96a1235566aa45c14e8cbe6b2c08c 100644 GIT binary patch delta 60187 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

    pD!g0 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 O03i>jyu678;r{>>yATlo delta 60318 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 z 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 CKF06> diff --git a/doc/cheatsheet/Pandas_Cheat_Sheet.pptx b/doc/cheatsheet/Pandas_Cheat_Sheet.pptx index 95f2771017db5fb1da502c92e34ee0157ab1dad0..039b3898fa30106263196791433c6e4779a17095 100644 GIT binary patch delta 42540 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 lQ;)4RU6Mdt&^x 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

    {YK)#sB@-RE}*-H0tL%LwhSIXpy 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 PoCSn&Fj-jS)aCydcpm%o delta 132324 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

    Dn?`(sqS6 z+A&u;I4@f~DW3rTHJWGo;Zgxha+A17fo;u}MUc6s`6uGR+T)j4Q%}D8mI?T5bYE3A ztbnH|VJus+*L>2$pw|#^7v3#B%@zE(VJSUfge`cQaaz_jEdxxxuOqimBPsBHjT~0x->3D5!IFE6Z58BWH#4MK&ST zm%gX(z9~XER?koj^I-IYHy{j-#NPS7hj&29fl zGf8P6;i>K2YcO5GRAvu1=Oq5!rkzLA840fl zPLXHD{^14$IuRu&(k@uvu@&p z9Q;trnOkVk8*J4t0KU7o!x{z-{Li}kOYp6Up^mA>9Qe*um(AvualFC`Jh-vwEPjtFH#t4FM*1(Fwbrs1i0uMfgKE zp1sDpruqddGfgC2zQ@;w*3WVKT6ASr@|m@iQFR_NJrV391AhAgZON6|0hJz>eu<{;~~J=rW7`Hx_16RAJ~Osv>e0GQB2H3WY9Atly$g9cYwk zaQxjku`3JaQ-+>muOBXV7C7eVw`tSoDN0IQXOPoBgR@<4uHimWgfWr5#)KwNMKks( zz(m^qu0-s?1#!Hpl9ig$w7~!-jKcS#3)0v^e1ORn8ksL;mvioomdk=i4_D|FsY=v( z;wj@Dm5h!E6qo-EU`rULT{jlqBT$UOJ~(CsjgXxO_&t4LAZfj-Adv zK}q9qh4-hYYNv9iwx>=#O+PX`AiiThS6}FD{_BDqAqg_^>`NTN&F|15e!TomSOLNe zwuL5quvG%=Y>XZ^a^?XbwsEqb9=;g~`*}(ZpO=CId@lxCPK97C7lVqWl_F58DHSr6 z;n~1LfD4kd%O<9j{Ghx%krdgG=5X>+R8p%kwFF&yW?wO2L^-gJ`WU{Q8;8!P zm-vEHh?P@KWU7Q8NN`L$xHuRqrbe zgHeu!hOL-}#Uud$sTFO_7#w?*F>C`(RjhkT28RR8cf9hrW;`&#+GM?i*bY}q9?!p^F(E8A$yQ#R$~53~>x?ypIXSa~Y~tvla}1Zd?a#eH6U+OEUU{BlGWOsa zpy^KzNghN;tu(?crVSia44-67Qb)l$65Oub-GFPpaOA~N7&_mde68|CM4EBo8yhO4 zDZL652JsuLaQCvob*FA@^+))aD&AU{m;X(`vDpl0`dwGZ{=NVB*w{2lCppJrrEgqT zC`U2*M-AL)=C$z%OIgT)tfC2|ak#&hUxe?7#7fQmCH7`Xhfj!^Yns)-DgGXK`}RP6 zteiHF8AKS&k4Y4-P3ZFBd#5R*p9i+sW`}njpc8U&FIZZ|>ZW6J81b0t% z#grlylz5A`SP;6x_2mpsO!7)7Q{i|WBG7%Qa86>RN|~~yoI^} zLp|ea*<#+JaIMSPtsu^B+#mI++5$Qc&&~m8zR9^!S~T_0

    W+%{(>lM4S@xgBwbZ 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�_mM{w8zA+^T$F%F5kJHi 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 diff --git a/doc/cheatsheet/Pandas_Cheat_Sheet_JA.pptx b/doc/cheatsheet/Pandas_Cheat_Sheet_JA.pptx index cb0f058db544809b9de0bcef31776f0f66883053..564d92ddbb56a75fe211d9b8512c14e217116e62 100644 GIT binary patch delta 24103 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

    8b?0gni#1WJ4Q!Y=It(LkDCP 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 D^_jDc delta 23758 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 Date: Tue, 12 Feb 2019 05:51:16 +0000 Subject: [PATCH 106/215] Edit parameter type in pandas.core.frame.py DataFrame.count (#25198) --- pandas/core/frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 5c28259e0cb63..85aa13526e77c 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -7236,7 +7236,7 @@ def count(self, axis=0, level=None, numeric_only=False): If the axis is a `MultiIndex` (hierarchical), count along a particular `level`, collapsing into a `DataFrame`. A `str` specifies the level name. - numeric_only : boolean, default False + numeric_only : bool, default False Include only `float`, `int` or `boolean` data. Returns From a89e19d59e0bff2d02e4647af1904e2c9701dd5f Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 13 Feb 2019 13:04:31 +0000 Subject: [PATCH 107/215] TST/CLN: remove test_slice_ints_with_floats_raises (#25277) --- pandas/tests/indexes/test_base.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index c99007cef90d4..8415bab802239 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -1541,8 +1541,9 @@ def test_slice_locs(self, dtype): assert index2.slice_locs(8, 2) == (2, 6) assert index2.slice_locs(7, 3) == (2, 5) - def test_slice_float_locs(self): - index = Index(np.array([0, 1, 2, 5, 6, 7, 9, 10], dtype=float)) + @pytest.mark.parametrize("dtype", [int, float]) + def test_slice_float_locs(self, dtype): + index = Index(np.array([0, 1, 2, 5, 6, 7, 9, 10], dtype=dtype)) n = len(index) assert index.slice_locs(5.0, 10.0) == (3, n) assert index.slice_locs(4.5, 10.5) == (3, 8) @@ -1551,24 +1552,6 @@ def test_slice_float_locs(self): assert index2.slice_locs(8.5, 1.5) == (2, 6) assert index2.slice_locs(10.5, -1) == (0, n) - @pytest.mark.xfail(reason="Assertions were not correct - see GH#20915") - def test_slice_ints_with_floats_raises(self): - # int slicing with floats - # GH 4892, these are all TypeErrors - index = Index(np.array([0, 1, 2, 5, 6, 7, 9, 10], dtype=int)) - n = len(index) - - pytest.raises(TypeError, - lambda: index.slice_locs(5.0, 10.0)) - pytest.raises(TypeError, - lambda: index.slice_locs(4.5, 10.5)) - - index2 = index[::-1] - pytest.raises(TypeError, - lambda: index2.slice_locs(8.5, 1.5), (2, 6)) - pytest.raises(TypeError, - lambda: index2.slice_locs(10.5, -1), (0, n)) - def test_slice_locs_dup(self): index = Index(['a', 'a', 'b', 'c', 'd', 'd']) assert index.slice_locs('a', 'd') == (0, 6) From b8306f19da098a826f621022ddce279119de2f95 Mon Sep 17 00:00:00 2001 From: William Ayd Date: Wed, 13 Feb 2019 05:48:27 -0800 Subject: [PATCH 108/215] Removed Panel class from HDF ASVs (#25281) --- asv_bench/benchmarks/io/hdf.py | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/asv_bench/benchmarks/io/hdf.py b/asv_bench/benchmarks/io/hdf.py index f08904ba70a5f..a5dc28eb9508c 100644 --- a/asv_bench/benchmarks/io/hdf.py +++ b/asv_bench/benchmarks/io/hdf.py @@ -1,7 +1,5 @@ -import warnings - import numpy as np -from pandas import DataFrame, Panel, date_range, HDFStore, read_hdf +from pandas import DataFrame, date_range, HDFStore, read_hdf import pandas.util.testing as tm from ..pandas_vb_common import BaseIO @@ -99,31 +97,6 @@ def time_store_info(self): self.store.info() -class HDFStorePanel(BaseIO): - - def setup(self): - self.fname = '__test__.h5' - with warnings.catch_warnings(record=True): - self.p = Panel(np.random.randn(20, 1000, 25), - items=['Item%03d' % i for i in range(20)], - major_axis=date_range('1/1/2000', periods=1000), - minor_axis=['E%03d' % i for i in range(25)]) - self.store = HDFStore(self.fname) - self.store.append('p1', self.p) - - def teardown(self): - self.store.close() - self.remove(self.fname) - - def time_read_store_table_panel(self): - with warnings.catch_warnings(record=True): - self.store.select('p1') - - def time_write_store_table_panel(self): - with warnings.catch_warnings(record=True): - self.store.append('p2', self.p) - - class HDF(BaseIO): params = ['table', 'fixed'] From 6a8e7087831193afbc3e1799460614506743077b Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 14 Feb 2019 02:49:50 +0100 Subject: [PATCH 109/215] DOC: Fix minor typo in docstring (#25285) --- pandas/core/frame.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 85aa13526e77c..f8b48e6610ce5 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -5761,9 +5761,9 @@ def stack(self, level=-1, dropna=True): Notes ----- The function is named by analogy with a collection of books - being re-organised from being side by side on a horizontal + being reorganized from being side by side on a horizontal position (the columns of the dataframe) to being stacked - vertically on top of of each other (in the index of the + vertically on top of each other (in the index of the dataframe). Examples From b144f6671e65d1bc6fe9309f113e420432053ae3 Mon Sep 17 00:00:00 2001 From: Daniel Saxton Date: Thu, 14 Feb 2019 01:05:40 -0600 Subject: [PATCH 110/215] DOC/CLN: Fix errors in DataFrame docstrings (#24952) --- pandas/core/frame.py | 109 ++++++++++++++++++++++------------------- pandas/core/generic.py | 26 +++++----- 2 files changed, 71 insertions(+), 64 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index f8b48e6610ce5..7ac18b79daba1 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -1065,7 +1065,7 @@ def from_dict(cls, data, orient='columns', dtype=None, columns=None): Returns ------- - pandas.DataFrame + DataFrame See Also -------- @@ -1145,7 +1145,7 @@ def to_numpy(self, dtype=None, copy=False): Returns ------- - array : numpy.ndarray + numpy.ndarray See Also -------- @@ -1439,7 +1439,7 @@ def from_records(cls, data, index=None, exclude=None, columns=None, Returns ------- - df : DataFrame + DataFrame """ # Make a copy of the input columns so we can modify it @@ -1755,7 +1755,7 @@ def from_items(cls, items, columns=None, orient='columns'): Returns ------- - frame : DataFrame + DataFrame """ warnings.warn("from_items is deprecated. Please use " @@ -1866,7 +1866,7 @@ def from_csv(cls, path, header=0, sep=',', index_col=0, parse_dates=True, Returns ------- - y : DataFrame + DataFrame See Also -------- @@ -1956,7 +1956,7 @@ def to_panel(self): Returns ------- - panel : Panel + Panel """ raise NotImplementedError("Panel is being removed in pandas 0.25.0.") @@ -2478,7 +2478,7 @@ def memory_usage(self, index=True, deep=False): Returns ------- - sizes : Series + Series A Series whose index is the original column names and whose values is the memory usage of each column in bytes. @@ -2696,7 +2696,7 @@ def get_value(self, index, col, takeable=False): Returns ------- - value : scalar value + scalar value """ warnings.warn("get_value is deprecated and will be removed " @@ -2741,7 +2741,7 @@ def set_value(self, index, col, value, takeable=False): Returns ------- - frame : DataFrame + DataFrame If label pair is contained, will be reference to calling DataFrame, otherwise a new object """ @@ -3177,7 +3177,7 @@ def select_dtypes(self, include=None, exclude=None): Returns ------- - subset : DataFrame + DataFrame The subset of the frame including the dtypes in ``include`` and excluding the dtypes in ``exclude``. @@ -3542,7 +3542,7 @@ def _sanitize_column(self, key, value, broadcast=True): Returns ------- - sanitized_column : numpy-array + numpy.ndarray """ def reindexer(value): @@ -3811,7 +3811,7 @@ def drop(self, labels=None, axis=0, index=None, columns=None, Returns ------- - dropped : pandas.DataFrame + DataFrame Raises ------ @@ -3936,7 +3936,7 @@ def rename(self, *args, **kwargs): Returns ------- - renamed : DataFrame + DataFrame See Also -------- @@ -4579,7 +4579,7 @@ def drop_duplicates(self, subset=None, keep='first', inplace=False): Returns ------- - deduplicated : DataFrame + DataFrame """ if self.empty: return self.copy() @@ -4613,7 +4613,7 @@ def duplicated(self, subset=None, keep='first'): Returns ------- - duplicated : Series + Series """ from pandas.core.sorting import get_group_index from pandas._libs.hashtable import duplicated_int64, _SIZE_HINT_LIMIT @@ -4981,7 +4981,7 @@ def swaplevel(self, i=-2, j=-1, axis=0): Returns ------- - swapped : same type as caller (new object) + DataFrame .. versionchanged:: 0.18.1 @@ -5260,7 +5260,7 @@ def combine_first(self, other): Returns ------- - combined : DataFrame + DataFrame See Also -------- @@ -5621,7 +5621,7 @@ def pivot(self, index=None, columns=None, values=None): Returns ------- - table : DataFrame + DataFrame See Also -------- @@ -5907,7 +5907,7 @@ def unstack(self, level=-1, fill_value=None): Returns ------- - unstacked : DataFrame or Series + Series or DataFrame See Also -------- @@ -6073,7 +6073,7 @@ def diff(self, periods=1, axis=0): Returns ------- - diffed : DataFrame + DataFrame See Also -------- @@ -6345,7 +6345,7 @@ def apply(self, func, axis=0, broadcast=None, raw=False, reduce=None, Returns ------- - applied : Series or DataFrame + Series or DataFrame See Also -------- @@ -6538,7 +6538,7 @@ def append(self, other, ignore_index=False, Returns ------- - appended : DataFrame + DataFrame See Also -------- @@ -6956,12 +6956,13 @@ def corr(self, method='pearson', min_periods=1): min_periods : int, optional Minimum number of observations required per pair of columns - to have a valid result. Currently only available for pearson - and spearman correlation + to have a valid result. Currently only available for Pearson + and Spearman correlation. Returns ------- - y : DataFrame + DataFrame + Correlation matrix. See Also -------- @@ -6970,14 +6971,15 @@ def corr(self, method='pearson', min_periods=1): Examples -------- - >>> histogram_intersection = lambda a, b: np.minimum(a, b - ... ).sum().round(decimals=1) + >>> def histogram_intersection(a, b): + ... v = np.minimum(a, b).sum().round(decimals=1) + ... return v >>> df = pd.DataFrame([(.2, .3), (.0, .6), (.6, .0), (.2, .1)], ... columns=['dogs', 'cats']) >>> df.corr(method=histogram_intersection) - dogs cats - dogs 1.0 0.3 - cats 0.3 1.0 + dogs cats + dogs 1.0 0.3 + cats 0.3 1.0 """ numeric_df = self._get_numeric_data() cols = numeric_df.columns @@ -7140,10 +7142,11 @@ def corrwith(self, other, axis=0, drop=False, method='pearson'): Parameters ---------- other : DataFrame, Series + Object with which to compute correlations. axis : {0 or 'index', 1 or 'columns'}, default 0 - 0 or 'index' to compute column-wise, 1 or 'columns' for row-wise - drop : boolean, default False - Drop missing indices from result + 0 or 'index' to compute column-wise, 1 or 'columns' for row-wise. + drop : bool, default False + Drop missing indices from result. method : {'pearson', 'kendall', 'spearman'} or callable * pearson : standard correlation coefficient * kendall : Kendall Tau correlation coefficient @@ -7155,7 +7158,8 @@ def corrwith(self, other, axis=0, drop=False, method='pearson'): Returns ------- - correls : Series + Series + Pairwise correlations. See Also ------- @@ -7485,7 +7489,7 @@ def nunique(self, axis=0, dropna=True): Returns ------- - nunique : Series + Series See Also -------- @@ -7523,7 +7527,8 @@ def idxmin(self, axis=0, skipna=True): Returns ------- - idxmin : Series + Series + Indexes of minima along the specified axis. Raises ------ @@ -7559,7 +7564,8 @@ def idxmax(self, axis=0, skipna=True): Returns ------- - idxmax : Series + Series + Indexes of maxima along the specified axis. Raises ------ @@ -7706,7 +7712,7 @@ def quantile(self, q=0.5, axis=0, numeric_only=True, Returns ------- - quantiles : Series or DataFrame + Series or DataFrame If ``q`` is an array, a DataFrame will be returned where the index is ``q``, the columns are the columns of self, and the @@ -7776,19 +7782,19 @@ def to_timestamp(self, freq=None, how='start', axis=0, copy=True): Parameters ---------- - freq : string, default frequency of PeriodIndex - Desired frequency + freq : str, default frequency of PeriodIndex + Desired frequency. how : {'s', 'e', 'start', 'end'} Convention for converting period to timestamp; start of period - vs. end + vs. end. axis : {0 or 'index', 1 or 'columns'}, default 0 - The axis to convert (the index by default) - copy : boolean, default True - If false then underlying input data is not copied + The axis to convert (the index by default). + copy : bool, default True + If False then underlying input data is not copied. Returns ------- - df : DataFrame with DatetimeIndex + DataFrame with DatetimeIndex """ new_data = self._data if copy: @@ -7812,15 +7818,16 @@ def to_period(self, freq=None, axis=0, copy=True): Parameters ---------- - freq : string, default + freq : str, default + Frequency of the PeriodIndex. axis : {0 or 'index', 1 or 'columns'}, default 0 - The axis to convert (the index by default) - copy : boolean, default True - If False then underlying input data is not copied + The axis to convert (the index by default). + copy : bool, default True + If False then underlying input data is not copied. Returns ------- - ts : TimeSeries with PeriodIndex + TimeSeries with PeriodIndex """ new_data = self._data if copy: @@ -7893,7 +7900,7 @@ def isin(self, values): match. Note that 'falcon' does not match based on the number of legs in df2. - >>> other = pd.DataFrame({'num_legs': [8, 2],'num_wings': [0, 2]}, + >>> other = pd.DataFrame({'num_legs': [8, 2], 'num_wings': [0, 2]}, ... index=['spider', 'falcon']) >>> df.isin(other) num_legs num_wings diff --git a/pandas/core/generic.py b/pandas/core/generic.py index c886493f90eaf..1a404630b660e 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -774,18 +774,18 @@ def pop(self, item): Parameters ---------- item : str - Column label to be popped + Label of column to be popped. Returns ------- - popped : Series + Series Examples -------- - >>> df = pd.DataFrame([('falcon', 'bird', 389.0), - ... ('parrot', 'bird', 24.0), - ... ('lion', 'mammal', 80.5), - ... ('monkey', 'mammal', np.nan)], + >>> df = pd.DataFrame([('falcon', 'bird', 389.0), + ... ('parrot', 'bird', 24.0), + ... ('lion', 'mammal', 80.5), + ... ('monkey','mammal', np.nan)], ... columns=('name', 'class', 'max_speed')) >>> df name class max_speed @@ -937,7 +937,7 @@ def swaplevel(self, i=-2, j=-1, axis=0): Parameters ---------- - i, j : int, string (can be mixed) + i, j : int, str (can be mixed) Level of index to be swapped. Can pass level name as string. Returns @@ -973,9 +973,9 @@ def rename(self, *args, **kwargs): and raise on DataFrame or Panel. dict-like or functions are transformations to apply to that axis' values - copy : boolean, default True - Also copy underlying data - inplace : boolean, default False + copy : bool, default True + Also copy underlying data. + inplace : bool, default False Whether to return a new %(klass)s. If True then value of copy is ignored. level : int or level name, default None @@ -2947,7 +2947,7 @@ def to_csv(self, path_or_buf=None, sep=",", na_rep='', float_format=None, will treat them as non-numeric. quotechar : str, default '\"' String of length 1. Character used to quote fields. - line_terminator : string, optional + line_terminator : str, optional The newline character or character sequence to use in the output file. Defaults to `os.linesep`, which depends on the OS in which this method is called ('\n' for linux, '\r\n' for Windows, i.e.). @@ -10282,7 +10282,7 @@ def _doc_parms(cls): Parameters ---------- axis : %(axis_descr)s -skipna : boolean, default True +skipna : bool, default True Exclude NA/null values. If an entire row/column is NA, the result will be NA level : int or level name, default None @@ -10291,7 +10291,7 @@ def _doc_parms(cls): ddof : int, default 1 Delta Degrees of Freedom. The divisor used in calculations is N - ddof, where N represents the number of elements. -numeric_only : boolean, default None +numeric_only : bool, default None Include only float, int, boolean columns. If None, will attempt to use everything, then use only numeric data. Not implemented for Series. From 790e575f3605d003e0239484ec6598de56d519c6 Mon Sep 17 00:00:00 2001 From: William Ayd Date: Thu, 14 Feb 2019 09:27:27 -0800 Subject: [PATCH 111/215] Skipped broken Py2 / Windows test (#25323) --- pandas/tests/test_downstream.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/tests/test_downstream.py b/pandas/tests/test_downstream.py index e22b9a0ef25e3..92b4e5a99041a 100644 --- a/pandas/tests/test_downstream.py +++ b/pandas/tests/test_downstream.py @@ -9,7 +9,7 @@ import numpy as np # noqa import pytest -from pandas.compat import PY36 +from pandas.compat import PY2, PY36, is_platform_windows from pandas import DataFrame from pandas.util import testing as tm @@ -58,6 +58,8 @@ def test_xarray(df): assert df.to_xarray() is not None +@pytest.mark.skipif(is_platform_windows() and PY2, + reason="Broken on Windows / Py2") def test_oo_optimizable(): # GH 21071 subprocess.check_call([sys.executable, "-OO", "-c", "import pandas"]) From 4be995c13452766da48a82f3ecd0b5a24e48f526 Mon Sep 17 00:00:00 2001 From: Zach Angell <42625717+zangell44@users.noreply.github.com> Date: Thu, 14 Feb 2019 14:18:05 -0500 Subject: [PATCH 112/215] Rt05 documentation error fix issue 25108 (#25309) --- ci/code_checks.sh | 4 ++-- pandas/core/algorithms.py | 2 +- pandas/core/arrays/categorical.py | 2 +- pandas/core/arrays/datetimelike.py | 2 +- pandas/core/frame.py | 2 +- pandas/core/generic.py | 18 +++++++++--------- pandas/core/indexes/base.py | 10 +++++----- pandas/core/indexes/multi.py | 6 +++--- pandas/core/panel.py | 6 +++--- pandas/core/reshape/melt.py | 2 +- pandas/core/series.py | 24 ++++++++++++++---------- pandas/core/tools/numeric.py | 2 +- pandas/core/window.py | 2 +- pandas/io/formats/style.py | 2 +- pandas/plotting/_core.py | 21 +++++++++++---------- pandas/plotting/_misc.py | 2 +- pandas/tseries/frequencies.py | 2 +- 17 files changed, 57 insertions(+), 52 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index eacab199cc0be..ac6aade106ce6 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -241,8 +241,8 @@ fi ### DOCSTRINGS ### if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then - MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, PR10, EX04, RT04, SS05, SA05)' ; echo $MSG - $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR04,PR05,EX04,RT04,SS05,SA05 + MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, PR10, EX04, RT04, RT05, SS05, SA05)' ; echo $MSG + $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR04,PR05,EX04,RT04,RT05,SS05,SA05 RET=$(($RET + $?)) ; echo $MSG "DONE" fi diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index 77681f6ac3f93..c5c8f47ad6dba 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -291,7 +291,7 @@ def unique(values): unique values. If the input is an Index, the return is an Index If the input is a Categorical dtype, the return is a Categorical - If the input is a Series/ndarray, the return will be an ndarray + If the input is a Series/ndarray, the return will be an ndarray. See Also -------- diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index ab58f86e0a6bc..d7d0882bbcc94 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1292,7 +1292,7 @@ def __array__(self, dtype=None): values : numpy array A numpy array of either the specified dtype or, if dtype==None (default), the same dtype as - categorical.categories.dtype + categorical.categories.dtype. """ ret = take_1d(self.categories.values, self._codes) if dtype and not is_dtype_equal(dtype, self.categories.dtype): diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 73e799f9e0a36..84536ac72a455 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -154,7 +154,7 @@ def strftime(self, date_format): Returns ------- Index - Index of formatted strings + Index of formatted strings. See Also -------- diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 7ac18b79daba1..cf97c94f6d129 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2743,7 +2743,7 @@ def set_value(self, index, col, value, takeable=False): ------- DataFrame If label pair is contained, will be reference to calling DataFrame, - otherwise a new object + otherwise a new object. """ warnings.warn("set_value is deprecated and will be removed " "in a future release. Please use " diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 1a404630b660e..e2308836d982a 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -4953,7 +4953,7 @@ def pipe(self, func, *args, **kwargs): If DataFrame.agg is called with a single function, returns a Series If DataFrame.agg is called with several functions, returns a DataFrame If Series.agg is called with single function, returns a scalar - If Series.agg is called with several functions, returns a Series + If Series.agg is called with several functions, returns a Series. %(see_also)s @@ -5349,7 +5349,7 @@ def get_values(self): Returns ------- numpy.ndarray - Numpy representation of DataFrame + Numpy representation of DataFrame. See Also -------- @@ -5428,7 +5428,7 @@ def get_ftype_counts(self): ------- dtype : Series Series with the count of columns with each type and - sparsity (dense/sparse) + sparsity (dense/sparse). See Also -------- @@ -6657,7 +6657,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, ------- Series or DataFrame Returns the same object type as the caller, interpolated at - some or all ``NaN`` values + some or all ``NaN`` values. See Also -------- @@ -6877,11 +6877,11 @@ def asof(self, where, subset=None): ------- scalar, Series, or DataFrame - Scalar : when `self` is a Series and `where` is a scalar + Scalar : when `self` is a Series and `where` is a scalar. Series: when `self` is a Series and `where` is an array-like, - or when `self` is a DataFrame and `where` is a scalar + or when `self` is a DataFrame and `where` is a scalar. DataFrame : when `self` is a DataFrame and `where` is an - array-like + array-like. See Also -------- @@ -7235,7 +7235,7 @@ def clip(self, lower=None, upper=None, axis=None, inplace=False, ------- Series or DataFrame Same type as calling object with the values outside the - clip boundaries replaced + clip boundaries replaced. Examples -------- @@ -8386,7 +8386,7 @@ def ranker(data): Returns ------- (left, right) : (%(klass)s, type of other) - Aligned objects + Aligned objects. """) @Appender(_shared_docs['align'] % _shared_doc_kwargs) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index cf813f4c3030b..f2c8ac6e9b413 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1828,7 +1828,7 @@ def isna(self): Returns ------- numpy.ndarray - A boolean array of whether my values are NA + A boolean array of whether my values are NA. See Also -------- @@ -3098,9 +3098,9 @@ def reindex(self, target, method=None, level=None, limit=None, Returns ------- new_index : pd.Index - Resulting index + Resulting index. indexer : np.ndarray or None - Indices of output values in original index + Indices of output values in original index. """ # GH6552: preserve names when reindexing to non-named target @@ -4259,7 +4259,7 @@ def shift(self, periods=1, freq=None): Returns ------- pandas.Index - Shifted index + Shifted index. See Also -------- @@ -4422,7 +4422,7 @@ def set_value(self, arr, key, value): in the target are marked by -1. missing : ndarray of int An indexer into the target of the values not found. - These correspond to the -1 in the indexer array + These correspond to the -1 in the indexer array. """ @Appender(_index_shared_docs['get_indexer_non_unique'] % _index_doc_kwargs) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index e2237afbcac0f..efb77b5d155a1 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1956,7 +1956,7 @@ def swaplevel(self, i=-2, j=-1): Returns ------- MultiIndex - A new MultiIndex + A new MultiIndex. .. versionchanged:: 0.18.1 @@ -2053,9 +2053,9 @@ def sortlevel(self, level=0, ascending=True, sort_remaining=True): Returns ------- sorted_index : pd.MultiIndex - Resulting index + Resulting index. indexer : np.ndarray - Indices of output values in original index + Indices of output values in original index. """ from pandas.core.sorting import indexer_from_factorized diff --git a/pandas/core/panel.py b/pandas/core/panel.py index dda5533f1ea7b..1555542079d80 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -540,7 +540,7 @@ def set_value(self, *args, **kwargs): ------- panel : Panel If label combo is contained, will be reference to calling Panel, - otherwise a new object + otherwise a new object. """ warnings.warn("set_value is deprecated and will be removed " "in a future release. Please use " @@ -803,7 +803,7 @@ def major_xs(self, key): Returns ------- y : DataFrame - Index -> minor axis, columns -> items + Index -> minor axis, columns -> items. Notes ----- @@ -827,7 +827,7 @@ def minor_xs(self, key): Returns ------- y : DataFrame - Index -> major axis, columns -> items + Index -> major axis, columns -> items. Notes ----- diff --git a/pandas/core/reshape/melt.py b/pandas/core/reshape/melt.py index 312a108ad3380..0fa80de812c5f 100644 --- a/pandas/core/reshape/melt.py +++ b/pandas/core/reshape/melt.py @@ -230,7 +230,7 @@ def wide_to_long(df, stubnames, i, j, sep="", suffix=r'\d+'): ------- DataFrame A DataFrame that contains each stub name as a variable, with new index - (i, j) + (i, j). Notes ----- diff --git a/pandas/core/series.py b/pandas/core/series.py index b2011fdcdee98..31c6247436418 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1215,7 +1215,7 @@ def set_value(self, label, value, takeable=False): ------- Series If label is contained, will be reference to calling Series, - otherwise a new object + otherwise a new object. """ warnings.warn("set_value is deprecated and will be removed " "in a future release. Please use " @@ -1648,10 +1648,19 @@ def unique(self): Returns ------- ndarray or ExtensionArray - The unique values returned as a NumPy array. In case of an - extension-array backed Series, a new - :class:`~api.extensions.ExtensionArray` of that type with just - the unique values is returned. This includes + The unique values returned as a NumPy array. See Notes. + + See Also + -------- + unique : Top-level unique method for any 1-d array-like object. + Index.unique : Return Index with unique values from an Index object. + + Notes + ----- + Returns the unique values as a NumPy array. In case of an + extension-array backed Series, a new + :class:`~api.extensions.ExtensionArray` of that type with just + the unique values is returned. This includes * Categorical * Period @@ -1660,11 +1669,6 @@ def unique(self): * Sparse * IntegerNA - See Also - -------- - unique : Top-level unique method for any 1-d array-like object. - Index.unique : Return Index with unique values from an Index object. - Examples -------- >>> pd.Series([2, 1, 3, 3], name='A').unique() diff --git a/pandas/core/tools/numeric.py b/pandas/core/tools/numeric.py index b8a7eb5b0c570..08ce649d8602c 100644 --- a/pandas/core/tools/numeric.py +++ b/pandas/core/tools/numeric.py @@ -59,7 +59,7 @@ def to_numeric(arg, errors='raise', downcast=None): Returns ------- ret : numeric if parsing succeeded. - Return type depends on input. Series if Series, otherwise ndarray + Return type depends on input. Series if Series, otherwise ndarray. See Also -------- diff --git a/pandas/core/window.py b/pandas/core/window.py index fb37d790f950c..9e29fdb94c1e0 100644 --- a/pandas/core/window.py +++ b/pandas/core/window.py @@ -1271,7 +1271,7 @@ def skew(self, **kwargs): ------- Series or DataFrame Returned object type is determined by the caller of the %(name)s - calculation + calculation. See Also -------- diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index cd6e3505d71db..c8b5dc6b9b7c0 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -435,7 +435,7 @@ def render(self, **kwargs): Returns ------- rendered : str - The rendered HTML + The rendered HTML. Notes ----- diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index a525b9cff1182..2c672f235f1e1 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -2050,9 +2050,17 @@ def plot_series(data, kind='line', ax=None, # Series unique Returns ------- - result : + result + See Notes. - The return type depends on the `return_type` parameter: + See Also + -------- + Series.plot.hist: Make a histogram. + matplotlib.pyplot.boxplot : Matplotlib equivalent plot. + + Notes + ----- + The return type depends on the `return_type` parameter: * 'axes' : object of class matplotlib.axes.Axes * 'dict' : dict of matplotlib.lines.Line2D objects @@ -2063,13 +2071,6 @@ def plot_series(data, kind='line', ax=None, # Series unique * :class:`~pandas.Series` * :class:`~numpy.array` (for ``return_type = None``) - See Also - -------- - Series.plot.hist: Make a histogram. - matplotlib.pyplot.boxplot : Matplotlib equivalent plot. - - Notes - ----- Use ``return_type='dict'`` when you want to tweak the appearance of the lines after plotting. In this case a dict containing the Lines making up the boxes, caps, fliers, medians, and whiskers is returned. @@ -3332,7 +3333,7 @@ def area(self, x=None, y=None, **kwds): Returns ------- matplotlib.axes.Axes or numpy.ndarray - Area plot, or array of area plots if subplots is True + Area plot, or array of area plots if subplots is True. See Also -------- diff --git a/pandas/plotting/_misc.py b/pandas/plotting/_misc.py index 62a33245f99ef..21592a5b4a0a1 100644 --- a/pandas/plotting/_misc.py +++ b/pandas/plotting/_misc.py @@ -390,7 +390,7 @@ def bootstrap_plot(series, fig=None, size=50, samples=500, **kwds): Returns ------- fig : matplotlib.figure.Figure - Matplotlib figure + Matplotlib figure. See Also -------- diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index f591b24f5b648..4802447cbc99d 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -68,7 +68,7 @@ def to_offset(freq): Returns ------- delta : DateOffset - None if freq is None + None if freq is None. Raises ------ From 53281a5962e75655cb3e10cbf290234655a359e1 Mon Sep 17 00:00:00 2001 From: Takuya N Date: Fri, 15 Feb 2019 13:07:54 +0900 Subject: [PATCH 113/215] Fix typos in docs (#25305) --- doc/source/user_guide/groupby.rst | 2 +- doc/source/whatsnew/v0.10.0.rst | 2 +- doc/source/whatsnew/v0.16.1.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/user_guide/groupby.rst b/doc/source/user_guide/groupby.rst index 2c2e5c5425216..e4dd82afcdf65 100644 --- a/doc/source/user_guide/groupby.rst +++ b/doc/source/user_guide/groupby.rst @@ -1317,7 +1317,7 @@ arbitrary function, for example: df.groupby(['Store', 'Product']).pipe(mean) where ``mean`` takes a GroupBy object and finds the mean of the Revenue and Quantity -columns repectively for each Store-Product combination. The ``mean`` function can +columns respectively for each Store-Product combination. The ``mean`` function can be any function that takes in a GroupBy object; the ``.pipe`` will pass the GroupBy object as a parameter into the function you specify. diff --git a/doc/source/whatsnew/v0.10.0.rst b/doc/source/whatsnew/v0.10.0.rst index bc2a4918bc27b..2d6550bb6888d 100644 --- a/doc/source/whatsnew/v0.10.0.rst +++ b/doc/source/whatsnew/v0.10.0.rst @@ -370,7 +370,7 @@ Updated PyTables Support df1.get_dtype_counts() - performance improvements on table writing -- support for arbitrarly indexed dimensions +- support for arbitrarily indexed dimensions - ``SparseSeries`` now has a ``density`` property (:issue:`2384`) - enable ``Series.str.strip/lstrip/rstrip`` methods to take an input argument to strip arbitrary characters (:issue:`2411`) diff --git a/doc/source/whatsnew/v0.16.1.rst b/doc/source/whatsnew/v0.16.1.rst index 7621cb9c1e27c..cbcb23e356577 100644 --- a/doc/source/whatsnew/v0.16.1.rst +++ b/doc/source/whatsnew/v0.16.1.rst @@ -136,7 +136,7 @@ groupby operations on the index will preserve the index nature as well reindexing operations, will return a resulting index based on the type of the passed indexer, meaning that passing a list will return a plain-old-``Index``; indexing with a ``Categorical`` will return a ``CategoricalIndex``, indexed according to the categories -of the PASSED ``Categorical`` dtype. This allows one to arbitrarly index these even with +of the PASSED ``Categorical`` dtype. This allows one to arbitrarily index these even with values NOT in the categories, similarly to how you can reindex ANY pandas index. .. code-block:: ipython From 83fe6ca422ecc35f311897abedadcc492b2a0777 Mon Sep 17 00:00:00 2001 From: Jonathon Vandezande Date: Fri, 15 Feb 2019 13:42:45 -0500 Subject: [PATCH 114/215] Doc: corrects spelling in generic.py (#25333) --- pandas/core/generic.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index e2308836d982a..b1fcbba7bd7ec 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -7604,16 +7604,16 @@ def groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, using the `level` parameter: >>> arrays = [['Falcon', 'Falcon', 'Parrot', 'Parrot'], - ... ['Capitve', 'Wild', 'Capitve', 'Wild']] + ... ['Captive', 'Wild', 'Captive', 'Wild']] >>> index = pd.MultiIndex.from_arrays(arrays, names=('Animal', 'Type')) >>> df = pd.DataFrame({'Max Speed' : [390., 350., 30., 20.]}, ... index=index) >>> df Max Speed Animal Type - Falcon Capitve 390.0 + Falcon Captive 390.0 Wild 350.0 - Parrot Capitve 30.0 + Parrot Captive 30.0 Wild 20.0 >>> df.groupby(level=0).mean() Max Speed @@ -7623,7 +7623,7 @@ def groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, >>> df.groupby(level=1).mean() Max Speed Type - Capitve 210.0 + Captive 210.0 Wild 185.0 """ from pandas.core.groupby.groupby import groupby From 9de4cc1bc6b719eea183419fd6343fa691139139 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Sat, 16 Feb 2019 08:32:03 -0800 Subject: [PATCH 115/215] BUG: groupby.transform retains timezone information (#25264) --- doc/source/whatsnew/v0.24.2.rst | 2 +- pandas/core/groupby/generic.py | 2 +- pandas/tests/groupby/test_transform.py | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index f17c4974cd450..0c78cf01ad300 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -78,7 +78,7 @@ Bug Fixes **Reshaping** -- +- Bug in :meth:`pandas.core.groupby.GroupBy.transform` where applying a function to a timezone aware column would return a timezone naive result (:issue:`24198`) - Bug in :func:`DataFrame.join` when joining on a timezone aware :class:`DatetimeIndex` (:issue:`23931`) - diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index 27e13e86a6e9e..52056a6842ed9 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -964,7 +964,7 @@ def _transform_fast(self, func, func_nm): ids, _, ngroup = self.grouper.group_info cast = self._transform_should_cast(func_nm) - out = algorithms.take_1d(func().values, ids) + out = algorithms.take_1d(func()._values, ids) if cast: out = self._try_cast(out, self.obj) return Series(out, index=self.obj.index, name=self.obj.name) diff --git a/pandas/tests/groupby/test_transform.py b/pandas/tests/groupby/test_transform.py index f120402e6e8ca..b645073fcf72a 100644 --- a/pandas/tests/groupby/test_transform.py +++ b/pandas/tests/groupby/test_transform.py @@ -834,3 +834,14 @@ def demean_rename(x): tm.assert_frame_equal(result, expected) result_single = df.groupby('group').value.transform(demean_rename) tm.assert_series_equal(result_single, expected['value']) + + +@pytest.mark.parametrize('func', [min, max, np.min, np.max, 'first', 'last']) +def test_groupby_transform_timezone_column(func): + # GH 24198 + ts = pd.to_datetime('now', utc=True).tz_convert('Asia/Singapore') + result = pd.DataFrame({'end_time': [ts], 'id': [1]}) + result['max_end_time'] = result.groupby('id').end_time.transform(func) + expected = pd.DataFrame([[ts, 1, ts]], columns=['end_time', 'id', + 'max_end_time']) + tm.assert_frame_equal(result, expected) From 33b11b9adc557d5d083f073dac3bdeacffea154f Mon Sep 17 00:00:00 2001 From: EternalLearner42 <46832510+EternalLearner42@users.noreply.github.com> Date: Sat, 16 Feb 2019 16:34:50 +0000 Subject: [PATCH 116/215] Fixes Formatting Exception (#25088) --- doc/source/whatsnew/v0.24.2.rst | 1 + pandas/io/formats/terminal.py | 20 ++++++++++++++------ pandas/tests/io/formats/test_console.py | 19 +++++++++++++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 0c78cf01ad300..0c66df3129b2d 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -53,6 +53,7 @@ Bug Fixes **I/O** +- Better handling of terminal printing when the terminal dimensions are not known (:issue:`25080`); - Bug in reading a HDF5 table-format ``DataFrame`` created in Python 2, in Python 3 (:issue:`24925`) - Bug in reading a JSON with ``orient='table'`` generated by :meth:`DataFrame.to_json` with ``index=False`` (:issue:`25170`) - Bug where float indexes could have misaligned values when printing (:issue:`25061`) diff --git a/pandas/io/formats/terminal.py b/pandas/io/formats/terminal.py index bb34259d710c7..cf2383955d593 100644 --- a/pandas/io/formats/terminal.py +++ b/pandas/io/formats/terminal.py @@ -15,6 +15,7 @@ import os import shutil +import subprocess from pandas.compat import PY3 @@ -94,22 +95,29 @@ def _get_terminal_size_tput(): # get terminal width # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width # -height-of-a-terminal-window + try: - import subprocess proc = subprocess.Popen(["tput", "cols"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) - output = proc.communicate(input=None) - cols = int(output[0]) + output_cols = proc.communicate(input=None) proc = subprocess.Popen(["tput", "lines"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) - output = proc.communicate(input=None) - rows = int(output[0]) - return (cols, rows) + output_rows = proc.communicate(input=None) except OSError: return None + try: + # Some terminals (e.g. spyder) may report a terminal size of '', + # making the `int` fail. + + cols = int(output_cols[0]) + rows = int(output_rows[0]) + return cols, rows + except (ValueError, IndexError): + return None + def _get_terminal_size_linux(): def ioctl_GWINSZ(fd): diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 055763bf62d6e..45c5e982c1c48 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -1,6 +1,9 @@ +import subprocess # noqa: F401 + import pytest from pandas.io.formats.console import detect_console_encoding +from pandas.io.formats.terminal import _get_terminal_size_tput class MockEncoding(object): # TODO(py27): replace with mock @@ -72,3 +75,19 @@ def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): context.setattr('sys.stdout', MockEncoding(std)) context.setattr('sys.getdefaultencoding', lambda: 'sysDefaultEncoding') assert detect_console_encoding() == 'sysDefaultEncoding' + + +@pytest.mark.parametrize("size", ['', ['']]) +def test_terminal_unknown_dimensions(monkeypatch, size): + mock = pytest.importorskip("unittest.mock") + + def communicate(*args, **kwargs): + return size + + monkeypatch.setattr('subprocess.Popen', mock.Mock()) + monkeypatch.setattr('subprocess.Popen.return_value.returncode', None) + monkeypatch.setattr( + 'subprocess.Popen.return_value.communicate', communicate) + result = _get_terminal_size_tput() + + assert result is None From ea9848ce782a5ebccb6de5aedd7ac3aef7b3a39d Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Sat, 16 Feb 2019 09:22:40 -0800 Subject: [PATCH 117/215] Bug: OverflowError in resample.agg with tz data (#25297) --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/indexes/datetimes.py | 2 +- pandas/tests/resample/test_resample_api.py | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 95362521f3b9f..286d267f024a1 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -172,7 +172,7 @@ Plotting Groupby/Resample/Rolling ^^^^^^^^^^^^^^^^^^^^^^^^ -- +- Bug in :meth:`pandas.core.resample.Resampler.agg` with a timezone aware index where ``OverflowError`` would raise when passing a list of functions (:issue:`22660`) - - diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index df91c71cfe238..0e3dae61561c1 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1010,7 +1010,7 @@ def get_loc(self, key, method=None, tolerance=None): except (KeyError, ValueError, TypeError): try: return self._get_string_slice(key) - except (TypeError, KeyError, ValueError): + except (TypeError, KeyError, ValueError, OverflowError): pass try: diff --git a/pandas/tests/resample/test_resample_api.py b/pandas/tests/resample/test_resample_api.py index 69acf4ba6bde8..97f1e07380ef9 100644 --- a/pandas/tests/resample/test_resample_api.py +++ b/pandas/tests/resample/test_resample_api.py @@ -549,3 +549,25 @@ def test_selection_api_validation(): exp.index.name = 'd' assert_frame_equal(exp, df.resample('2D', level='d').sum()) + + +@pytest.mark.parametrize('col_name', ['t2', 't2x', 't2q', 'T_2M', + 't2p', 't2m', 't2m1', 'T2M']) +def test_agg_with_datetime_index_list_agg_func(col_name): + # GH 22660 + # The parametrized column names would get converted to dates by our + # date parser. Some would result in OutOfBoundsError (ValueError) while + # others would result in OverflowError when passed into Timestamp. + # We catch these errors and move on to the correct branch. + df = pd.DataFrame(list(range(200)), + index=pd.date_range(start='2017-01-01', freq='15min', + periods=200, tz='Europe/Berlin'), + columns=[col_name]) + result = df.resample('1d').aggregate(['mean']) + expected = pd.DataFrame([47.5, 143.5, 195.5], + index=pd.date_range(start='2017-01-01', freq='D', + periods=3, tz='Europe/Berlin'), + columns=pd.MultiIndex(levels=[[col_name], + ['mean']], + codes=[[0], [0]])) + assert_frame_equal(result, expected) From eace576d68c64960a83caedbf4019870837572ae Mon Sep 17 00:00:00 2001 From: Daniel Saxton Date: Sat, 16 Feb 2019 11:27:49 -0600 Subject: [PATCH 118/215] DOC/CLN: Fix various docstring errors (#25295) --- pandas/core/arrays/categorical.py | 72 ++++++++++---------- pandas/core/arrays/datetimes.py | 64 +++++++++--------- pandas/core/base.py | 22 ++++-- pandas/core/frame.py | 4 +- pandas/core/generic.py | 63 ++++++++--------- pandas/core/reshape/concat.py | 38 +++++------ pandas/core/reshape/pivot.py | 56 ++++++++-------- pandas/core/reshape/reshape.py | 14 ++-- pandas/core/reshape/tile.py | 2 +- pandas/core/strings.py | 108 ++++++++++++++++-------------- 10 files changed, 232 insertions(+), 211 deletions(-) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index d7d0882bbcc94..c2b024c9ae12e 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -214,7 +214,7 @@ def contains(cat, key, container): class Categorical(ExtensionArray, PandasObject): """ - Represent a categorical variable in classic R / S-plus fashion + Represent a categorical variable in classic R / S-plus fashion. `Categoricals` can only take on only a limited, and usually fixed, number of possible values (`categories`). In contrast to statistical categorical @@ -235,7 +235,7 @@ class Categorical(ExtensionArray, PandasObject): The unique categories for this categorical. If not given, the categories are assumed to be the unique values of `values` (sorted, if possible, otherwise in the order in which they appear). - ordered : boolean, (default False) + ordered : bool, default False Whether or not this categorical is treated as a ordered categorical. If True, the resulting categorical will be ordered. An ordered categorical respects, when sorted, the order of its @@ -253,7 +253,7 @@ class Categorical(ExtensionArray, PandasObject): codes : ndarray The codes (integer positions, which point to the categories) of this categorical, read only. - ordered : boolean + ordered : bool Whether or not this Categorical is ordered. dtype : CategoricalDtype The instance of ``CategoricalDtype`` storing the ``categories`` @@ -297,7 +297,7 @@ class Categorical(ExtensionArray, PandasObject): Ordered `Categoricals` can be sorted according to the custom order of the categories and can have a min and max value. - >>> c = pd.Categorical(['a','b','c','a','b','c'], ordered=True, + >>> c = pd.Categorical(['a', 'b', 'c', 'a', 'b', 'c'], ordered=True, ... categories=['c', 'b', 'a']) >>> c [a, b, c, a, b, c] @@ -618,7 +618,7 @@ def from_codes(cls, codes, categories=None, ordered=None, dtype=None): ---------- codes : array-like, integers An integer array, where each integer points to a category in - categories or dtype.categories, or else is -1 for NaN + categories or dtype.categories, or else is -1 for NaN. categories : index-like, optional The categories for the categorical. Items need to be unique. If the categories are not given here, then they must be provided @@ -700,7 +700,7 @@ def _set_categories(self, categories, fastpath=False): Parameters ---------- - fastpath : boolean (default: False) + fastpath : bool, default False Don't perform validation of the categories for uniqueness or nulls Examples @@ -747,15 +747,15 @@ def _set_dtype(self, dtype): def set_ordered(self, value, inplace=False): """ - Set the ordered attribute to the boolean value + Set the ordered attribute to the boolean value. Parameters ---------- - value : boolean to set whether this categorical is ordered (True) or - not (False) - inplace : boolean (default: False) - Whether or not to set the ordered attribute inplace or return a copy - of this categorical with ordered set to the value + value : bool + Set whether this categorical is ordered (True) or not (False). + inplace : bool, default False + Whether or not to set the ordered attribute in-place or return + a copy of this categorical with ordered set to the value. """ inplace = validate_bool_kwarg(inplace, 'inplace') new_dtype = CategoricalDtype(self.categories, ordered=value) @@ -770,9 +770,9 @@ def as_ordered(self, inplace=False): Parameters ---------- - inplace : boolean (default: False) - Whether or not to set the ordered attribute inplace or return a copy - of this categorical with ordered set to True + inplace : bool, default False + Whether or not to set the ordered attribute in-place or return + a copy of this categorical with ordered set to True. """ inplace = validate_bool_kwarg(inplace, 'inplace') return self.set_ordered(True, inplace=inplace) @@ -783,9 +783,9 @@ def as_unordered(self, inplace=False): Parameters ---------- - inplace : boolean (default: False) - Whether or not to set the ordered attribute inplace or return a copy - of this categorical with ordered set to False + inplace : bool, default False + Whether or not to set the ordered attribute in-place or return + a copy of this categorical with ordered set to False. """ inplace = validate_bool_kwarg(inplace, 'inplace') return self.set_ordered(False, inplace=inplace) @@ -815,19 +815,19 @@ def set_categories(self, new_categories, ordered=None, rename=False, ---------- new_categories : Index-like The categories in new order. - ordered : boolean, (default: False) + ordered : bool, default False Whether or not the categorical is treated as a ordered categorical. If not given, do not change the ordered information. - rename : boolean (default: False) + rename : bool, default False Whether or not the new_categories should be considered as a rename of the old categories or as reordered categories. - inplace : boolean (default: False) - Whether or not to reorder the categories inplace or return a copy of - this categorical with reordered categories. + inplace : bool, default False + Whether or not to reorder the categories in-place or return a copy + of this categorical with reordered categories. Returns ------- - cat : Categorical with reordered categories or None if inplace. + Categorical with reordered categories or None if inplace. Raises ------ @@ -890,7 +890,7 @@ def rename_categories(self, new_categories, inplace=False): Currently, Series are considered list like. In a future version of pandas they'll be considered dict-like. - inplace : boolean (default: False) + inplace : bool, default False Whether or not to rename the categories inplace or return a copy of this categorical with renamed categories. @@ -967,10 +967,10 @@ def reorder_categories(self, new_categories, ordered=None, inplace=False): ---------- new_categories : Index-like The categories in new order. - ordered : boolean, optional + ordered : bool, optional Whether or not the categorical is treated as a ordered categorical. If not given, do not change the ordered information. - inplace : boolean (default: False) + inplace : bool, default False Whether or not to reorder the categories inplace or return a copy of this categorical with reordered categories. @@ -1010,7 +1010,7 @@ def add_categories(self, new_categories, inplace=False): ---------- new_categories : category or list-like of category The new categories to be included. - inplace : boolean (default: False) + inplace : bool, default False Whether or not to add the categories inplace or return a copy of this categorical with added categories. @@ -1060,7 +1060,7 @@ def remove_categories(self, removals, inplace=False): ---------- removals : category or list of categories The categories which should be removed. - inplace : boolean (default: False) + inplace : bool, default False Whether or not to remove the categories inplace or return a copy of this categorical with removed categories. @@ -1108,7 +1108,7 @@ def remove_unused_categories(self, inplace=False): Parameters ---------- - inplace : boolean (default: False) + inplace : bool, default False Whether or not to drop unused categories inplace or return a copy of this categorical with unused categories dropped. @@ -1460,7 +1460,7 @@ def value_counts(self, dropna=True): Parameters ---------- - dropna : boolean, default True + dropna : bool, default True Don't include counts of NaN. Returns @@ -1581,9 +1581,9 @@ def sort_values(self, inplace=False, ascending=True, na_position='last'): Parameters ---------- - inplace : boolean, default False + inplace : bool, default False Do operation in place. - ascending : boolean, default True + ascending : bool, default True Order ascending. Passing False orders descending. The ordering parameter provides the method by which the category values are organized. @@ -2239,7 +2239,7 @@ def mode(self, dropna=True): Parameters ---------- - dropna : boolean, default True + dropna : bool, default True Don't consider counts of NaN/NaT. .. versionadded:: 0.24.0 @@ -2332,7 +2332,7 @@ def equals(self, other): Returns ------- - are_equal : boolean + bool """ if self.is_dtype_equal(other): if self.categories.equals(other.categories): @@ -2356,7 +2356,7 @@ def is_dtype_equal(self, other): Returns ------- - are_equal : boolean + bool """ try: diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 1b2a4da389dc4..cd8e8ed520ddc 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -799,14 +799,14 @@ def tz_convert(self, tz): Parameters ---------- - tz : string, pytz.timezone, dateutil.tz.tzfile or None + tz : str, pytz.timezone, dateutil.tz.tzfile or None Time zone for time. Corresponding timestamps would be converted to this time zone of the Datetime Array/Index. A `tz` of None will convert to UTC and remove the timezone information. Returns ------- - normalized : same type as self + Array or Index Raises ------ @@ -842,7 +842,7 @@ def tz_convert(self, tz): With the ``tz=None``, we can remove the timezone (after converting to UTC if necessary): - >>> dti = pd.date_range(start='2014-08-01 09:00',freq='H', + >>> dti = pd.date_range(start='2014-08-01 09:00', freq='H', ... periods=3, tz='Europe/Berlin') >>> dti @@ -882,7 +882,7 @@ def tz_localize(self, tz, ambiguous='raise', nonexistent='raise', Parameters ---------- - tz : string, pytz.timezone, dateutil.tz.tzfile or None + tz : str, pytz.timezone, dateutil.tz.tzfile or None Time zone to convert timestamps to. Passing ``None`` will remove the time zone information preserving local time. ambiguous : 'infer', 'NaT', bool array, default 'raise' @@ -930,7 +930,7 @@ def tz_localize(self, tz, ambiguous='raise', nonexistent='raise', Returns ------- - result : same type as self + Same type as self Array/Index converted to the specified time zone. Raises @@ -970,43 +970,39 @@ def tz_localize(self, tz, ambiguous='raise', nonexistent='raise', Be careful with DST changes. When there is sequential data, pandas can infer the DST time: - >>> s = pd.to_datetime(pd.Series([ - ... '2018-10-28 01:30:00', - ... '2018-10-28 02:00:00', - ... '2018-10-28 02:30:00', - ... '2018-10-28 02:00:00', - ... '2018-10-28 02:30:00', - ... '2018-10-28 03:00:00', - ... '2018-10-28 03:30:00'])) + >>> s = pd.to_datetime(pd.Series(['2018-10-28 01:30:00', + ... '2018-10-28 02:00:00', + ... '2018-10-28 02:30:00', + ... '2018-10-28 02:00:00', + ... '2018-10-28 02:30:00', + ... '2018-10-28 03:00:00', + ... '2018-10-28 03:30:00'])) >>> s.dt.tz_localize('CET', ambiguous='infer') - 2018-10-28 01:30:00+02:00 0 - 2018-10-28 02:00:00+02:00 1 - 2018-10-28 02:30:00+02:00 2 - 2018-10-28 02:00:00+01:00 3 - 2018-10-28 02:30:00+01:00 4 - 2018-10-28 03:00:00+01:00 5 - 2018-10-28 03:30:00+01:00 6 - dtype: int64 + 0 2018-10-28 01:30:00+02:00 + 1 2018-10-28 02:00:00+02:00 + 2 2018-10-28 02:30:00+02:00 + 3 2018-10-28 02:00:00+01:00 + 4 2018-10-28 02:30:00+01:00 + 5 2018-10-28 03:00:00+01:00 + 6 2018-10-28 03:30:00+01:00 + dtype: datetime64[ns, CET] In some cases, inferring the DST is impossible. In such cases, you can pass an ndarray to the ambiguous parameter to set the DST explicitly - >>> s = pd.to_datetime(pd.Series([ - ... '2018-10-28 01:20:00', - ... '2018-10-28 02:36:00', - ... '2018-10-28 03:46:00'])) + >>> s = pd.to_datetime(pd.Series(['2018-10-28 01:20:00', + ... '2018-10-28 02:36:00', + ... '2018-10-28 03:46:00'])) >>> s.dt.tz_localize('CET', ambiguous=np.array([True, True, False])) - 0 2018-10-28 01:20:00+02:00 - 1 2018-10-28 02:36:00+02:00 - 2 2018-10-28 03:46:00+01:00 - dtype: datetime64[ns, CET] + 0 2015-03-29 03:00:00+02:00 + 1 2015-03-29 03:30:00+02:00 + dtype: datetime64[ns, Europe/Warsaw] If the DST transition causes nonexistent times, you can shift these dates forward or backwards with a timedelta object or `'shift_forward'` or `'shift_backwards'`. - >>> s = pd.to_datetime(pd.Series([ - ... '2015-03-29 02:30:00', - ... '2015-03-29 03:30:00'])) + >>> s = pd.to_datetime(pd.Series(['2015-03-29 02:30:00', + ... '2015-03-29 03:30:00'])) >>> s.dt.tz_localize('Europe/Warsaw', nonexistent='shift_forward') 0 2015-03-29 03:00:00+02:00 1 2015-03-29 03:30:00+02:00 @@ -1129,7 +1125,7 @@ def to_period(self, freq=None): Parameters ---------- - freq : string or Offset, optional + freq : str or Offset, optional One of pandas' :ref:`offset strings ` or an Offset object. Will be inferred by default. @@ -1150,7 +1146,7 @@ def to_period(self, freq=None): Examples -------- - >>> df = pd.DataFrame({"y": [1,2,3]}, + >>> df = pd.DataFrame({"y": [1, 2, 3]}, ... index=pd.to_datetime(["2000-03-31 00:00:00", ... "2000-05-31 00:00:00", ... "2000-08-31 00:00:00"])) diff --git a/pandas/core/base.py b/pandas/core/base.py index 5a98e83c65884..7fdc64a8d9f85 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -794,7 +794,7 @@ def array(self): Returns ------- - array : ExtensionArray + ExtensionArray An ExtensionArray of the values stored within. For extension types, this is the actual array. For NumPy native types, this is a thin (no copy) wrapper around :class:`numpy.ndarray`. @@ -1022,7 +1022,7 @@ def max(self, axis=None, skipna=True): def argmax(self, axis=None, skipna=True): """ - Return a ndarray of the maximum argument indexer. + Return an ndarray of the maximum argument indexer. Parameters ---------- @@ -1087,6 +1087,10 @@ def argmin(self, axis=None, skipna=True): Dummy argument for consistency with Series skipna : bool, default True + Returns + ------- + numpy.ndarray + See Also -------- numpy.ndarray.argmin @@ -1102,6 +1106,10 @@ def tolist(self): (for str, int, float) or a pandas scalar (for Timestamp/Timedelta/Interval/Period) + Returns + ------- + list + See Also -------- numpy.ndarray.tolist @@ -1162,7 +1170,7 @@ def _map_values(self, mapper, na_action=None): Returns ------- - applied : Union[Index, MultiIndex], inferred + Union[Index, MultiIndex], inferred The output of the mapping function applied to the index. If the function returns a tuple with more than one element a MultiIndex will be returned. @@ -1246,7 +1254,7 @@ def value_counts(self, normalize=False, sort=True, ascending=False, Returns ------- - counts : Series + Series See Also -------- @@ -1363,7 +1371,7 @@ def is_unique(self): Returns ------- - is_unique : boolean + bool """ return self.nunique(dropna=False) == len(self) @@ -1377,7 +1385,7 @@ def is_monotonic(self): Returns ------- - is_monotonic : boolean + bool """ from pandas import Index return Index(self).is_monotonic @@ -1394,7 +1402,7 @@ def is_monotonic_decreasing(self): Returns ------- - is_monotonic_decreasing : boolean + bool """ from pandas import Index return Index(self).is_monotonic_decreasing diff --git a/pandas/core/frame.py b/pandas/core/frame.py index cf97c94f6d129..a239ff4b4d5db 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -6346,6 +6346,8 @@ def apply(self, func, axis=0, broadcast=None, raw=False, reduce=None, Returns ------- Series or DataFrame + Result of applying ``func`` along the given axis of the + DataFrame. See Also -------- @@ -6364,7 +6366,7 @@ def apply(self, func, axis=0, broadcast=None, raw=False, reduce=None, Examples -------- - >>> df = pd.DataFrame([[4, 9],] * 3, columns=['A', 'B']) + >>> df = pd.DataFrame([[4, 9]] * 3, columns=['A', 'B']) >>> df A B 0 4 9 diff --git a/pandas/core/generic.py b/pandas/core/generic.py index b1fcbba7bd7ec..3a73861086bed 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -5959,17 +5959,18 @@ def fillna(self, value=None, method=None, axis=None, inplace=False, value : scalar, dict, Series, or DataFrame Value to use to fill holes (e.g. 0), alternately a dict/Series/DataFrame of values specifying which value to use for - each index (for a Series) or column (for a DataFrame). (values not - in the dict/Series/DataFrame will not be filled). This value cannot + each index (for a Series) or column (for a DataFrame). Values not + in the dict/Series/DataFrame will not be filled. This value cannot be a list. method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None Method to use for filling holes in reindexed Series pad / ffill: propagate last valid observation forward to next valid - backfill / bfill: use NEXT valid observation to fill gap + backfill / bfill: use next valid observation to fill gap. axis : %(axes_single_arg)s - inplace : boolean, default False - If True, fill in place. Note: this will modify any - other views on this object, (e.g. a no-copy slice for a column in a + Axis along which to fill missing values. + inplace : bool, default False + If True, fill in-place. Note: this will modify any + other views on this object (e.g., a no-copy slice for a column in a DataFrame). limit : int, default None If method is specified, this is the maximum number of consecutive @@ -5979,18 +5980,20 @@ def fillna(self, value=None, method=None, axis=None, inplace=False, maximum number of entries along the entire axis where NaNs will be filled. Must be greater than 0 if not None. downcast : dict, default is None - a dict of item->dtype of what to downcast if possible, + A dict of item->dtype of what to downcast if possible, or the string 'infer' which will try to downcast to an appropriate - equal type (e.g. float64 to int64 if possible) + equal type (e.g. float64 to int64 if possible). Returns ------- - filled : %(klass)s + %(klass)s + Object with missing values filled. See Also -------- interpolate : Fill NaN values using interpolation. - reindex, asfreq + reindex : Conform object to new index. + asfreq : Convert TimeSeries to specified frequency. Examples -------- @@ -5998,7 +6001,7 @@ def fillna(self, value=None, method=None, axis=None, inplace=False, ... [3, 4, np.nan, 1], ... [np.nan, np.nan, np.nan, 5], ... [np.nan, 3, np.nan, 4]], - ... columns=list('ABCD')) + ... columns=list('ABCD')) >>> df A B C D 0 NaN 2.0 NaN 0 @@ -6752,7 +6755,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, Note how the first entry in column 'b' remains ``NaN``, because there is no entry befofe it to use for interpolation. - >>> df = pd.DataFrame([(0.0, np.nan, -1.0, 1.0), + >>> df = pd.DataFrame([(0.0, np.nan, -1.0, 1.0), ... (np.nan, 2.0, np.nan, np.nan), ... (2.0, 3.0, np.nan, 9.0), ... (np.nan, 4.0, -4.0, 16.0)], @@ -7221,9 +7224,9 @@ def clip(self, lower=None, upper=None, axis=None, inplace=False, upper : float or array_like, default None Maximum threshold value. All values above this threshold will be set to it. - axis : int or string axis name, optional + axis : int or str axis name, optional Align object with lower and upper along the given axis. - inplace : boolean, default False + inplace : bool, default False Whether to perform the operation in place on the data. .. versionadded:: 0.21.0 @@ -7345,7 +7348,7 @@ def clip_upper(self, threshold, axis=None, inplace=False): axis : {0 or 'index', 1 or 'columns'}, default 0 Align object with `threshold` along the given axis. - inplace : boolean, default False + inplace : bool, default False Whether to perform the operation in place on the data. .. versionadded:: 0.21.0 @@ -7426,7 +7429,7 @@ def clip_lower(self, threshold, axis=None, inplace=False): axis : {0 or 'index', 1 or 'columns'}, default 0 Align `self` with `threshold` along the given axis. - inplace : boolean, default False + inplace : bool, default False Whether to perform the operation in place on the data. .. versionadded:: 0.21.0 @@ -7583,9 +7586,9 @@ def groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, Examples -------- - >>> df = pd.DataFrame({'Animal' : ['Falcon', 'Falcon', - ... 'Parrot', 'Parrot'], - ... 'Max Speed' : [380., 370., 24., 26.]}) + >>> df = pd.DataFrame({'Animal': ['Falcon', 'Falcon', + ... 'Parrot', 'Parrot'], + ... 'Max Speed': [380., 370., 24., 26.]}) >>> df Animal Max Speed 0 Falcon 380.0 @@ -7606,8 +7609,8 @@ def groupby(self, by=None, axis=0, level=None, as_index=True, sort=True, >>> arrays = [['Falcon', 'Falcon', 'Parrot', 'Parrot'], ... ['Captive', 'Wild', 'Captive', 'Wild']] >>> index = pd.MultiIndex.from_arrays(arrays, names=('Animal', 'Type')) - >>> df = pd.DataFrame({'Max Speed' : [390., 350., 30., 20.]}, - ... index=index) + >>> df = pd.DataFrame({'Max Speed': [390., 350., 30., 20.]}, + ... index=index) >>> df Max Speed Animal Type @@ -7740,14 +7743,14 @@ def at_time(self, time, asof=False, axis=None): Parameters ---------- - time : datetime.time or string + time : datetime.time or str axis : {0 or 'index', 1 or 'columns'}, default 0 .. versionadded:: 0.24.0 Returns ------- - values_at_time : same type as caller + Series or DataFrame Raises ------ @@ -7765,7 +7768,7 @@ def at_time(self, time, asof=False, axis=None): Examples -------- >>> i = pd.date_range('2018-04-09', periods=4, freq='12H') - >>> ts = pd.DataFrame({'A': [1,2,3,4]}, index=i) + >>> ts = pd.DataFrame({'A': [1, 2, 3, 4]}, index=i) >>> ts A 2018-04-09 00:00:00 1 @@ -7800,17 +7803,17 @@ def between_time(self, start_time, end_time, include_start=True, Parameters ---------- - start_time : datetime.time or string - end_time : datetime.time or string - include_start : boolean, default True - include_end : boolean, default True + start_time : datetime.time or str + end_time : datetime.time or str + include_start : bool, default True + include_end : bool, default True axis : {0 or 'index', 1 or 'columns'}, default 0 .. versionadded:: 0.24.0 Returns ------- - values_between_time : same type as caller + Series or DataFrame Raises ------ @@ -7828,7 +7831,7 @@ def between_time(self, start_time, end_time, include_start=True, Examples -------- >>> i = pd.date_range('2018-04-09', periods=4, freq='1D20min') - >>> ts = pd.DataFrame({'A': [1,2,3,4]}, index=i) + >>> ts = pd.DataFrame({'A': [1, 2, 3, 4]}, index=i) >>> ts A 2018-04-09 00:00:00 1 diff --git a/pandas/core/reshape/concat.py b/pandas/core/reshape/concat.py index 53671e00e88b4..a6c945ac2e464 100644 --- a/pandas/core/reshape/concat.py +++ b/pandas/core/reshape/concat.py @@ -38,15 +38,15 @@ def concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, If a dict is passed, the sorted keys will be used as the `keys` argument, unless it is passed, in which case the values will be selected (see below). Any None objects will be dropped silently unless - they are all None in which case a ValueError will be raised + they are all None in which case a ValueError will be raised. axis : {0/'index', 1/'columns'}, default 0 - The axis to concatenate along + The axis to concatenate along. join : {'inner', 'outer'}, default 'outer' - How to handle indexes on other axis(es) + How to handle indexes on other axis (or axes). join_axes : list of Index objects Specific indexes to use for the other n - 1 axes instead of performing - inner/outer set logic - ignore_index : boolean, default False + inner/outer set logic. + ignore_index : bool, default False If True, do not use the index values along the concatenation axis. The resulting axis will be labeled 0, ..., n - 1. This is useful if you are concatenating objects where the concatenation axis does not have @@ -54,16 +54,16 @@ def concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, axes are still respected in the join. keys : sequence, default None If multiple levels passed, should contain tuples. Construct - hierarchical index using the passed keys as the outermost level + hierarchical index using the passed keys as the outermost level. levels : list of sequences, default None Specific levels (unique values) to use for constructing a - MultiIndex. Otherwise they will be inferred from the keys + MultiIndex. Otherwise they will be inferred from the keys. names : list, default None - Names for the levels in the resulting hierarchical index - verify_integrity : boolean, default False + Names for the levels in the resulting hierarchical index. + verify_integrity : bool, default False Check whether the new concatenated axis contains duplicates. This can - be very expensive relative to the actual data concatenation - sort : boolean, default None + be very expensive relative to the actual data concatenation. + sort : bool, default None Sort non-concatenation axis if it is not already aligned when `join` is 'outer'. The current default of sorting is deprecated and will change to not-sorting in a future version of pandas. @@ -76,12 +76,12 @@ def concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, .. versionadded:: 0.23.0 - copy : boolean, default True - If False, do not copy data unnecessarily + copy : bool, default True + If False, do not copy data unnecessarily. Returns ------- - concatenated : object, type of objs + object, type of objs When concatenating all ``Series`` along the index (axis=0), a ``Series`` is returned. When ``objs`` contains at least one ``DataFrame``, a ``DataFrame`` is returned. When concatenating along @@ -89,10 +89,10 @@ def concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, See Also -------- - Series.append - DataFrame.append - DataFrame.join - DataFrame.merge + Series.append : Concatenate Series. + DataFrame.append : Concatenate DataFrames. + DataFrame.join : Join DataFrames using indexes. + DataFrame.merge : Merge DataFrames by indexes or columns. Notes ----- @@ -128,7 +128,7 @@ def concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, Add a hierarchical index at the outermost level of the data with the ``keys`` option. - >>> pd.concat([s1, s2], keys=['s1', 's2',]) + >>> pd.concat([s1, s2], keys=['s1', 's2']) s1 0 a 1 b s2 0 c diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index 54f11646fc753..8d7616c4b6b61 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -392,36 +392,36 @@ def crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, margins_name='All', dropna=True, normalize=False): """ - Compute a simple cross-tabulation of two (or more) factors. By default + Compute a simple cross tabulation of two (or more) factors. By default computes a frequency table of the factors unless an array of values and an - aggregation function are passed + aggregation function are passed. Parameters ---------- index : array-like, Series, or list of arrays/Series - Values to group by in the rows + Values to group by in the rows. columns : array-like, Series, or list of arrays/Series - Values to group by in the columns + Values to group by in the columns. values : array-like, optional Array of values to aggregate according to the factors. Requires `aggfunc` be specified. rownames : sequence, default None - If passed, must match number of row arrays passed + If passed, must match number of row arrays passed. colnames : sequence, default None - If passed, must match number of column arrays passed + If passed, must match number of column arrays passed. aggfunc : function, optional - If specified, requires `values` be specified as well - margins : boolean, default False - Add row/column margins (subtotals) - margins_name : string, default 'All' - Name of the row / column that will contain the totals + If specified, requires `values` be specified as well. + margins : bool, default False + Add row/column margins (subtotals). + margins_name : str, default 'All' + Name of the row/column that will contain the totals when margins is True. .. versionadded:: 0.21.0 - dropna : boolean, default True - Do not include columns whose entries are all NaN - normalize : boolean, {'all', 'index', 'columns'}, or {0,1}, default False + dropna : bool, default True + Do not include columns whose entries are all NaN. + normalize : bool, {'all', 'index', 'columns'}, or {0,1}, default False Normalize by dividing all values by the sum of values. - If passed 'all' or `True`, will normalize over all values. @@ -433,7 +433,13 @@ def crosstab(index, columns, values=None, rownames=None, colnames=None, Returns ------- - crosstab : DataFrame + DataFrame + Cross tabulation of the data. + + See Also + -------- + DataFrame.pivot : Reshape data based on column values. + pivot_table : Create a pivot table as a DataFrame. Notes ----- @@ -455,32 +461,26 @@ def crosstab(index, columns, values=None, rownames=None, colnames=None, ... "one", "two", "two", "two", "one"], dtype=object) >>> c = np.array(["dull", "dull", "shiny", "dull", "dull", "shiny", ... "shiny", "dull", "shiny", "shiny", "shiny"], - ... dtype=object) - + ... dtype=object) >>> pd.crosstab(a, [b, c], rownames=['a'], colnames=['b', 'c']) - ... # doctest: +NORMALIZE_WHITESPACE b one two c dull shiny dull shiny a bar 1 2 1 0 foo 2 2 1 2 + Here 'c' and 'f' are not represented in the data and will not be + shown in the output because dropna is True by default. Set + dropna=False to preserve categories with no data. + >>> foo = pd.Categorical(['a', 'b'], categories=['a', 'b', 'c']) >>> bar = pd.Categorical(['d', 'e'], categories=['d', 'e', 'f']) - >>> crosstab(foo, bar) # 'c' and 'f' are not represented in the data, - # and will not be shown in the output because - # dropna is True by default. Set 'dropna=False' - # to preserve categories with no data - ... # doctest: +SKIP + >>> pd.crosstab(foo, bar) col_0 d e row_0 a 1 0 b 0 1 - - >>> crosstab(foo, bar, dropna=False) # 'c' and 'f' are not represented - # in the data, but they still will be counted - # and shown in the output - ... # doctest: +SKIP + >>> pd.crosstab(foo, bar, dropna=False) col_0 d e f row_0 a 1 0 0 diff --git a/pandas/core/reshape/reshape.py b/pandas/core/reshape/reshape.py index f436b3b92a359..6ba33301753d6 100644 --- a/pandas/core/reshape/reshape.py +++ b/pandas/core/reshape/reshape.py @@ -701,19 +701,20 @@ def _convert_level_number(level_num, columns): def get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, columns=None, sparse=False, drop_first=False, dtype=None): """ - Convert categorical variable into dummy/indicator variables + Convert categorical variable into dummy/indicator variables. Parameters ---------- data : array-like, Series, or DataFrame - prefix : string, list of strings, or dict of strings, default None + Data of which to get dummy indicators. + prefix : str, list of str, or dict of str, default None String to append DataFrame column names. Pass a list with length equal to the number of columns when calling get_dummies on a DataFrame. Alternatively, `prefix` can be a dictionary mapping column names to prefixes. - prefix_sep : string, default '_' + prefix_sep : str, default '_' If appending prefix, separator/delimiter to use. Or pass a - list or dictionary as with `prefix.` + list or dictionary as with `prefix`. dummy_na : bool, default False Add a column to indicate NaNs, if False NaNs are ignored. columns : list-like, default None @@ -736,11 +737,12 @@ def get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, Returns ------- - dummies : DataFrame + DataFrame + Dummy-coded data. See Also -------- - Series.str.get_dummies + Series.str.get_dummies : Convert Series to dummy codes. Examples -------- diff --git a/pandas/core/reshape/tile.py b/pandas/core/reshape/tile.py index 2a654fec36a9f..f99fd9004bb31 100644 --- a/pandas/core/reshape/tile.py +++ b/pandas/core/reshape/tile.py @@ -163,7 +163,7 @@ def cut(x, bins, right=True, labels=None, retbins=False, precision=3, Use `drop` optional when bins is not unique >>> pd.cut(s, [0, 2, 4, 6, 10, 10], labels=False, retbins=True, - ... right=False, duplicates='drop') + ... right=False, duplicates='drop') ... # doctest: +ELLIPSIS (a 0.0 b 1.0 diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 183a91c952140..cc7a4db515c42 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -120,7 +120,7 @@ def str_count(arr, pat, flags=0): Returns ------- - counts : Series or Index + Series or Index Same type as the calling object containing the integer counts. See Also @@ -283,7 +283,7 @@ def str_contains(arr, pat, case=True, flags=0, na=np.nan, regex=True): return `True`. However, '.0' as a regex matches any character followed by a 0. - >>> s2 = pd.Series(['40','40.0','41','41.0','35']) + >>> s2 = pd.Series(['40', '40.0', '41', '41.0', '35']) >>> s2.str.contains('.0', regex=True) 0 True 1 True @@ -433,13 +433,13 @@ def str_replace(arr, pat, repl, n=-1, case=None, flags=0, regex=True): Parameters ---------- - pat : string or compiled regex + pat : str or compiled regex String can be a character sequence or regular expression. .. versionadded:: 0.20.0 `pat` also accepts a compiled regex. - repl : string or callable + repl : str or callable Replacement string or a callable. The callable is passed the regex match object and must return a replacement string to be used. See :func:`re.sub`. @@ -448,15 +448,15 @@ def str_replace(arr, pat, repl, n=-1, case=None, flags=0, regex=True): `repl` also accepts a callable. n : int, default -1 (all) - Number of replacements to make from start - case : boolean, default None + Number of replacements to make from start. + case : bool, default None - If True, case sensitive (the default if `pat` is a string) - Set to False for case insensitive - Cannot be set if `pat` is a compiled regex flags : int, default 0 (no flags) - re module flags, e.g. re.IGNORECASE - Cannot be set if `pat` is a compiled regex - regex : boolean, default True + regex : bool, default True - If True, assumes the passed-in pattern is a regular expression. - If False, treats the pattern as a literal string - Cannot be set to False if `pat` is a compiled regex or `repl` is @@ -537,6 +537,7 @@ def str_replace(arr, pat, repl, n=-1, case=None, flags=0, regex=True): Using a compiled regex with flags + >>> import re >>> regex_pat = re.compile(r'FUZ', flags=re.IGNORECASE) >>> pd.Series(['foo', 'fuz', np.nan]).str.replace(regex_pat, 'bar') 0 foo @@ -604,6 +605,7 @@ def str_repeat(arr, repeats): 0 a 1 b 2 c + dtype: object Single int repeats string in Series @@ -611,6 +613,7 @@ def str_repeat(arr, repeats): 0 aa 1 bb 2 cc + dtype: object Sequence of int repeats corresponding string in Series @@ -618,6 +621,7 @@ def str_repeat(arr, repeats): 0 a 1 bb 2 ccc + dtype: object """ if is_scalar(repeats): def rep(x): @@ -646,13 +650,14 @@ def str_match(arr, pat, case=True, flags=0, na=np.nan): Parameters ---------- - pat : string - Character sequence or regular expression - case : boolean, default True - If True, case sensitive + pat : str + Character sequence or regular expression. + case : bool, default True + If True, case sensitive. flags : int, default 0 (no flags) - re module flags, e.g. re.IGNORECASE - na : default NaN, fill value for missing values + re module flags, e.g. re.IGNORECASE. + na : default NaN + Fill value for missing values. Returns ------- @@ -768,7 +773,7 @@ def str_extract(arr, pat, flags=0, expand=True): Parameters ---------- - pat : string + pat : str Regular expression pattern with capturing groups. flags : int, default 0 (no flags) Flags from the ``re`` module, e.g. ``re.IGNORECASE``, that @@ -966,21 +971,23 @@ def str_extractall(arr, pat, flags=0): def str_get_dummies(arr, sep='|'): """ - Split each string in the Series by sep and return a frame of - dummy/indicator variables. + Split each string in the Series by sep and return a DataFrame + of dummy/indicator variables. Parameters ---------- - sep : string, default "|" + sep : str, default "|" String to split on. Returns ------- - dummies : DataFrame + DataFrame + Dummy variables corresponding to values of the Series. See Also -------- - get_dummies + get_dummies : Convert categorical variable into dummy/indicator + variables. Examples -------- @@ -1089,11 +1096,11 @@ def str_findall(arr, pat, flags=0): Parameters ---------- - pat : string + pat : str Pattern or regular expression. flags : int, default 0 - ``re`` module flags, e.g. `re.IGNORECASE` (default is 0, which means - no flags). + Flags from ``re`` module, e.g. `re.IGNORECASE` (default is 0, which + means no flags). Returns ------- @@ -1182,17 +1189,18 @@ def str_find(arr, sub, start=0, end=None, side='left'): Parameters ---------- sub : str - Substring being searched + Substring being searched. start : int - Left edge index + Left edge index. end : int - Right edge index + Right edge index. side : {'left', 'right'}, default 'left' - Specifies a starting side, equivalent to ``find`` or ``rfind`` + Specifies a starting side, equivalent to ``find`` or ``rfind``. Returns ------- - found : Series/Index of integer values + Series or Index + Indexes where substring is found. """ if not isinstance(sub, compat.string_types): @@ -1430,7 +1438,7 @@ def str_slice_replace(arr, start=None, stop=None, repl=None): Returns ------- - replaced : Series or Index + Series or Index Same type as the original object. See Also @@ -1513,7 +1521,7 @@ def str_strip(arr, to_strip=None, side='both'): Returns ------- - stripped : Series/Index of objects + Series or Index """ if side == 'both': f = lambda x: x.strip(to_strip) @@ -1537,30 +1545,30 @@ def str_wrap(arr, width, **kwargs): Parameters ---------- width : int - Maximum line-width + Maximum line width. expand_tabs : bool, optional - If true, tab characters will be expanded to spaces (default: True) + If True, tab characters will be expanded to spaces (default: True). replace_whitespace : bool, optional - If true, each whitespace character (as defined by string.whitespace) + If True, each whitespace character (as defined by string.whitespace) remaining after tab expansion will be replaced by a single space - (default: True) + (default: True). drop_whitespace : bool, optional - If true, whitespace that, after wrapping, happens to end up at the - beginning or end of a line is dropped (default: True) + If True, whitespace that, after wrapping, happens to end up at the + beginning or end of a line is dropped (default: True). break_long_words : bool, optional - If true, then words longer than width will be broken in order to ensure + If True, then words longer than width will be broken in order to ensure that no lines are longer than width. If it is false, long words will - not be broken, and some lines may be longer than width. (default: True) + not be broken, and some lines may be longer than width (default: True). break_on_hyphens : bool, optional - If true, wrapping will occur preferably on whitespace and right after + If True, wrapping will occur preferably on whitespace and right after hyphens in compound words, as it is customary in English. If false, only whitespaces will be considered as potentially good places for line breaks, but you need to set break_long_words to false if you want truly - insecable words. (default: True) + insecable words (default: True). Returns ------- - wrapped : Series/Index of objects + Series or Index Notes ----- @@ -1581,6 +1589,7 @@ def str_wrap(arr, width, **kwargs): >>> s.str.wrap(12) 0 line to be\nwrapped 1 another line\nto be\nwrapped + dtype: object """ kwargs['width'] = width @@ -1613,7 +1622,7 @@ def str_translate(arr, table, deletechars=None): Returns ------- - translated : Series/Index of objects + Series or Index """ if deletechars is None: f = lambda x: x.translate(table) @@ -1641,15 +1650,16 @@ def str_get(arr, i): Returns ------- - items : Series/Index of objects + Series or Index Examples -------- >>> s = pd.Series(["String", - (1, 2, 3), - ["a", "b", "c"], - 123, -456, - {1:"Hello", "2":"World"}]) + ... (1, 2, 3), + ... ["a", "b", "c"], + ... 123, + ... -456, + ... {1: "Hello", "2": "World"}]) >>> s 0 String 1 (1, 2, 3) @@ -1674,7 +1684,7 @@ def str_get(arr, i): 2 c 3 NaN 4 NaN - 5 NaN + 5 None dtype: object """ def f(x): @@ -1699,7 +1709,7 @@ def str_decode(arr, encoding, errors="strict"): Returns ------- - decoded : Series/Index of objects + Series or Index """ if encoding in _cpython_optimized_decoders: # CPython optimized implementation @@ -2091,7 +2101,7 @@ def cat(self, others=None, sep=None, na_rep=None, join=None): Returns ------- - concat : str or Series/Index of objects + str, Series or Index If `others` is None, `str` is returned, otherwise a `Series/Index` (same type as caller) of objects is returned. From 2ce21967dda3f8daa7c244d6d2184c7b8d3e9d6c Mon Sep 17 00:00:00 2001 From: Chris Bertinato Date: Sat, 16 Feb 2019 12:38:20 -0500 Subject: [PATCH 119/215] COMPAT: alias .to_numpy() for timestamp and timedelta scalars (#25142) --- doc/source/reference/arrays.rst | 2 ++ doc/source/whatsnew/v0.25.0.rst | 1 + pandas/_libs/tslibs/nattype.pyx | 20 +++++++++++++++++++ pandas/_libs/tslibs/timedeltas.pyx | 20 +++++++++++++++++++ pandas/_libs/tslibs/timestamps.pyx | 20 +++++++++++++++++++ pandas/tests/scalar/test_nat.py | 17 ++++++++++++---- .../tests/scalar/timedelta/test_timedelta.py | 5 +++++ .../tests/scalar/timestamp/test_timestamp.py | 5 +++++ 8 files changed, 86 insertions(+), 4 deletions(-) diff --git a/doc/source/reference/arrays.rst b/doc/source/reference/arrays.rst index 1dc74ad83b7e6..a129b75636536 100644 --- a/doc/source/reference/arrays.rst +++ b/doc/source/reference/arrays.rst @@ -120,6 +120,7 @@ Methods Timestamp.timetuple Timestamp.timetz Timestamp.to_datetime64 + Timestamp.to_numpy Timestamp.to_julian_date Timestamp.to_period Timestamp.to_pydatetime @@ -191,6 +192,7 @@ Methods Timedelta.round Timedelta.to_pytimedelta Timedelta.to_timedelta64 + Timedelta.to_numpy Timedelta.total_seconds A collection of timedeltas may be stored in a :class:`TimedeltaArray`. diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 286d267f024a1..47204ec0a06f2 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -34,6 +34,7 @@ Other API Changes ^^^^^^^^^^^^^^^^^ - :class:`DatetimeTZDtype` will now standardize pytz timezones to a common timezone instance (:issue:`24713`) +- ``Timestamp`` and ``Timedelta`` scalars now implement the :meth:`to_numpy` method as aliases to :meth:`Timestamp.to_datetime64` and :meth:`Timedelta.to_timedelta64`, respectively. (:issue:`24653`) - - diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index b64c3479f23fe..a13fcfdc855d5 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -188,6 +188,26 @@ cdef class _NaT(datetime): """ return np.datetime64('NaT', 'ns') + def to_numpy(self, dtype=None, copy=False): + """ + Convert the Timestamp to a NumPy datetime64. + + .. versionadded:: 0.25.0 + + This is an alias method for `Timestamp.to_datetime64()`. The dtype and + copy parameters are available here only for compatibility. Their values + will not affect the return value. + + Returns + ------- + numpy.datetime64 + + See Also + -------- + DatetimeIndex.to_numpy : Similar method for DatetimeIndex. + """ + return self.to_datetime64() + def __repr__(self): return 'NaT' diff --git a/pandas/_libs/tslibs/timedeltas.pyx b/pandas/_libs/tslibs/timedeltas.pyx index 58b2faac8b06b..6e40063fb925a 100644 --- a/pandas/_libs/tslibs/timedeltas.pyx +++ b/pandas/_libs/tslibs/timedeltas.pyx @@ -824,6 +824,26 @@ cdef class _Timedelta(timedelta): """ Returns a numpy.timedelta64 object with 'ns' precision """ return np.timedelta64(self.value, 'ns') + def to_numpy(self, dtype=None, copy=False): + """ + Convert the Timestamp to a NumPy timedelta64. + + .. versionadded:: 0.25.0 + + This is an alias method for `Timedelta.to_timedelta64()`. The dtype and + copy parameters are available here only for compatibility. Their values + will not affect the return value. + + Returns + ------- + numpy.timedelta64 + + See Also + -------- + Series.to_numpy : Similar method for Series. + """ + return self.to_timedelta64() + def total_seconds(self): """ Total duration of timedelta in seconds (to ns precision) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 8a95d2494dfa4..a2929dbeb471f 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -345,6 +345,26 @@ cdef class _Timestamp(datetime): """ return np.datetime64(self.value, 'ns') + def to_numpy(self, dtype=None, copy=False): + """ + Convert the Timestamp to a NumPy datetime64. + + .. versionadded:: 0.25.0 + + This is an alias method for `Timestamp.to_datetime64()`. The dtype and + copy parameters are available here only for compatibility. Their values + will not affect the return value. + + Returns + ------- + numpy.datetime64 + + See Also + -------- + DatetimeIndex.to_numpy : Similar method for DatetimeIndex. + """ + return self.to_datetime64() + def __add__(self, other): cdef: int64_t other_int, nanos diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index abf95b276cda1..43747ea8621d9 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -9,7 +9,7 @@ from pandas import ( DatetimeIndex, Index, NaT, Period, Series, Timedelta, TimedeltaIndex, - Timestamp) + Timestamp, isna) from pandas.core.arrays import PeriodArray from pandas.util import testing as tm @@ -201,9 +201,10 @@ def _get_overlap_public_nat_methods(klass, as_tuple=False): "fromtimestamp", "isocalendar", "isoformat", "isoweekday", "month_name", "now", "replace", "round", "strftime", "strptime", "time", "timestamp", "timetuple", "timetz", - "to_datetime64", "to_pydatetime", "today", "toordinal", - "tz_convert", "tz_localize", "tzname", "utcfromtimestamp", - "utcnow", "utcoffset", "utctimetuple", "weekday"]), + "to_datetime64", "to_numpy", "to_pydatetime", "today", + "toordinal", "tz_convert", "tz_localize", "tzname", + "utcfromtimestamp", "utcnow", "utcoffset", "utctimetuple", + "weekday"]), (Timedelta, ["total_seconds"]) ]) def test_overlap_public_nat_methods(klass, expected): @@ -339,3 +340,11 @@ def test_nat_arithmetic_td64_vector(op_name, box): def test_nat_pinned_docstrings(): # see gh-17327 assert NaT.ctime.__doc__ == datetime.ctime.__doc__ + + +def test_to_numpy_alias(): + # GH 24653: alias .to_numpy() for scalars + expected = NaT.to_datetime64() + result = NaT.to_numpy() + + assert isna(expected) and isna(result) diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index 7d5b479810205..bf71c37aa9c3d 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -414,6 +414,11 @@ def test_timedelta_conversions(self): assert (Timedelta(timedelta(days=1)) == np.timedelta64(1, 'D').astype('m8[ns]')) + def test_to_numpy_alias(self): + # GH 24653: alias .to_numpy() for scalars + td = Timedelta('10m7s') + assert td.to_timedelta64() == td.to_numpy() + def test_round(self): t1 = Timedelta('1 days 02:34:56.789123456') diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index c27ef3d0662c8..f42fad4c925f0 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -969,3 +969,8 @@ def test_to_period_tz_warning(self): with tm.assert_produces_warning(UserWarning): # warning that timezone info will be lost ts.to_period('D') + + def test_to_numpy_alias(self): + # GH 24653: alias .to_numpy() for scalars + ts = Timestamp(datetime.now()) + assert ts.to_datetime64() == ts.to_numpy() From b0dac6c18ea32e0fcffdf435f754bafad5d4b68b Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Sat, 16 Feb 2019 09:56:48 -0800 Subject: [PATCH 120/215] ENH: Support times with timezones in at_time (#25280) --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/indexes/datetimes.py | 13 ++++++------- pandas/tests/frame/test_timeseries.py | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 47204ec0a06f2..686c5ad0165e7 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -20,7 +20,7 @@ Other Enhancements ^^^^^^^^^^^^^^^^^^ - :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`) -- +- :meth:`DataFrame.at_time` and :meth:`Series.at_time` now support :meth:`datetime.time` objects with timezones (:issue:`24043`) - .. _whatsnew_0250.api_breaking: diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 0e3dae61561c1..1037e2d9a3bd6 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1302,20 +1302,19 @@ def indexer_at_time(self, time, asof=False): -------- indexer_between_time, DataFrame.at_time """ - from dateutil.parser import parse - if asof: raise NotImplementedError("'asof' argument is not supported") if isinstance(time, compat.string_types): + from dateutil.parser import parse time = parse(time).time() if time.tzinfo: - # TODO - raise NotImplementedError("argument 'time' with timezone info is " - "not supported") - - time_micros = self._get_time_micros() + if self.tz is None: + raise ValueError("Index must be timezone aware.") + time_micros = self.tz_convert(time.tzinfo)._get_time_micros() + else: + time_micros = self._get_time_micros() micros = _time_to_micros(time) return (micros == time_micros).nonzero()[0] diff --git a/pandas/tests/frame/test_timeseries.py b/pandas/tests/frame/test_timeseries.py index bc37317f72802..31e81a9ca77c2 100644 --- a/pandas/tests/frame/test_timeseries.py +++ b/pandas/tests/frame/test_timeseries.py @@ -6,6 +6,7 @@ import numpy as np import pytest +import pytz from pandas.compat import product @@ -647,6 +648,28 @@ def test_at_time(self): rs = ts.at_time('16:00') assert len(rs) == 0 + @pytest.mark.parametrize('hour', ['1:00', '1:00AM', time(1), + time(1, tzinfo=pytz.UTC)]) + def test_at_time_errors(self, hour): + # GH 24043 + dti = pd.date_range('2018', periods=3, freq='H') + df = pd.DataFrame(list(range(len(dti))), index=dti) + if getattr(hour, 'tzinfo', None) is None: + result = df.at_time(hour) + expected = df.iloc[1:2] + tm.assert_frame_equal(result, expected) + else: + with pytest.raises(ValueError, match="Index must be timezone"): + df.at_time(hour) + + def test_at_time_tz(self): + # GH 24043 + dti = pd.date_range('2018', periods=3, freq='H', tz='US/Pacific') + df = pd.DataFrame(list(range(len(dti))), index=dti) + result = df.at_time(time(4, tzinfo=pytz.timezone('US/Eastern'))) + expected = df.iloc[1:2] + tm.assert_frame_equal(result, expected) + def test_at_time_raises(self): # GH20725 df = pd.DataFrame([[1, 2, 3], [4, 5, 6]]) From 29008f5664c07e74c2c83cffb75fc004341bb27c Mon Sep 17 00:00:00 2001 From: Arno Veenstra Date: Sat, 16 Feb 2019 19:49:50 +0100 Subject: [PATCH 121/215] BUG: Fix passing of numeric_only argument for categorical reduce (#25304) --- doc/source/whatsnew/v0.24.2.rst | 1 + pandas/core/arrays/categorical.py | 2 +- pandas/core/series.py | 8 ++++++-- pandas/tests/reductions/test_reductions.py | 21 +++++++++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 0c66df3129b2d..8e59c2300e7ca 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -25,6 +25,7 @@ Fixed Regressions - Fixed regression in :meth:`DataFrame.apply` causing ``RecursionError`` when ``dict``-like classes were passed as argument. (:issue:`25196`) - Fixed regression in :meth:`DataFrame.duplicated()`, where empty dataframe was not returning a boolean dtyped Series. (:issue:`25184`) +- Fixed regression in :meth:`Series.min` and :meth:`Series.max` where ``numeric_only=True`` was ignored when the ``Series`` contained ```Categorical`` data (:issue:`25299`) .. _whatsnew_0242.enhancements: diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index c2b024c9ae12e..79e565df94eae 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -2172,7 +2172,7 @@ def _reverse_indexer(self): return result # reduction ops # - def _reduce(self, name, axis=0, skipna=True, **kwargs): + def _reduce(self, name, axis=0, **kwargs): func = getattr(self, name, None) if func is None: msg = 'Categorical cannot perform the operation {op}' diff --git a/pandas/core/series.py b/pandas/core/series.py index 31c6247436418..a5dfe8d43c336 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3678,8 +3678,12 @@ def _reduce(self, op, name, axis=0, skipna=True, numeric_only=None, if axis is not None: self._get_axis_number(axis) - # dispatch to ExtensionArray interface - if isinstance(delegate, ExtensionArray): + if isinstance(delegate, Categorical): + # TODO deprecate numeric_only argument for Categorical and use + # skipna as well, see GH25303 + return delegate._reduce(name, numeric_only=numeric_only, **kwds) + elif isinstance(delegate, ExtensionArray): + # dispatch to ExtensionArray interface return delegate._reduce(name, skipna=skipna, **kwds) elif is_datetime64_dtype(delegate): # use DatetimeIndex implementation to handle skipna correctly diff --git a/pandas/tests/reductions/test_reductions.py b/pandas/tests/reductions/test_reductions.py index 173f719edd465..8520855d14918 100644 --- a/pandas/tests/reductions/test_reductions.py +++ b/pandas/tests/reductions/test_reductions.py @@ -960,6 +960,27 @@ def test_min_max(self): assert np.isnan(_min) assert _max == 1 + def test_min_max_numeric_only(self): + # TODO deprecate numeric_only argument for Categorical and use + # skipna as well, see GH25303 + cat = Series(Categorical( + ["a", "b", np.nan, "a"], categories=['b', 'a'], ordered=True)) + + _min = cat.min() + _max = cat.max() + assert np.isnan(_min) + assert _max == "a" + + _min = cat.min(numeric_only=True) + _max = cat.max(numeric_only=True) + assert _min == "b" + assert _max == "a" + + _min = cat.min(numeric_only=False) + _max = cat.max(numeric_only=False) + assert np.isnan(_min) + assert _max == "a" + class TestSeriesMode(object): # Note: the name TestSeriesMode indicates these tests From 4a20d5b9e526435a53b7cb1dc0a819299d31f040 Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Sat, 16 Feb 2019 15:41:04 -0500 Subject: [PATCH 122/215] TST: use a fixed seed to have the same uniques across python versions (#25346) TST: add pytest-mock to handle mocker fixture --- ci/deps/azure-27-compat.yaml | 1 + ci/deps/azure-27-locale.yaml | 1 + ci/deps/azure-36-locale_slow.yaml | 1 + ci/deps/azure-37-locale.yaml | 1 + ci/deps/azure-37-numpydev.yaml | 1 + ci/deps/azure-macos-35.yaml | 1 + ci/deps/azure-windows-27.yaml | 1 + ci/deps/azure-windows-36.yaml | 1 + ci/deps/travis-27.yaml | 1 + ci/deps/travis-36-locale.yaml | 1 + ci/deps/travis-36-slow.yaml | 1 + ci/deps/travis-36.yaml | 1 + ci/deps/travis-37.yaml | 1 + pandas/tests/io/formats/test_console.py | 5 ++--- pandas/tests/resample/test_datetime_index.py | 8 ++++++-- 15 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ci/deps/azure-27-compat.yaml b/ci/deps/azure-27-compat.yaml index 8899e22bdf6cf..986855c464852 100644 --- a/ci/deps/azure-27-compat.yaml +++ b/ci/deps/azure-27-compat.yaml @@ -20,6 +20,7 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - pip: - html5lib==1.0b2 - beautifulsoup4==4.2.1 diff --git a/ci/deps/azure-27-locale.yaml b/ci/deps/azure-27-locale.yaml index 0846ef5e8264e..f73079ecbe3d2 100644 --- a/ci/deps/azure-27-locale.yaml +++ b/ci/deps/azure-27-locale.yaml @@ -22,6 +22,7 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - hypothesis>=3.58.0 - pip: - html5lib==1.0b2 diff --git a/ci/deps/azure-36-locale_slow.yaml b/ci/deps/azure-36-locale_slow.yaml index c7d2334623501..6b8d38fd25082 100644 --- a/ci/deps/azure-36-locale_slow.yaml +++ b/ci/deps/azure-36-locale_slow.yaml @@ -28,6 +28,7 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - moto - pip: - hypothesis>=3.58.0 diff --git a/ci/deps/azure-37-locale.yaml b/ci/deps/azure-37-locale.yaml index b5a05c49b8083..569b71dae003b 100644 --- a/ci/deps/azure-37-locale.yaml +++ b/ci/deps/azure-37-locale.yaml @@ -27,6 +27,7 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - pip: - hypothesis>=3.58.0 - moto # latest moto in conda-forge fails with 3.7, move to conda dependencies when this is fixed diff --git a/ci/deps/azure-37-numpydev.yaml b/ci/deps/azure-37-numpydev.yaml index 99ae228f25de3..a37be124cc546 100644 --- a/ci/deps/azure-37-numpydev.yaml +++ b/ci/deps/azure-37-numpydev.yaml @@ -8,6 +8,7 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - hypothesis>=3.58.0 - pip: - "git+git://github.com/dateutil/dateutil.git" diff --git a/ci/deps/azure-macos-35.yaml b/ci/deps/azure-macos-35.yaml index 58abbabce3d86..d1fe926744ecd 100644 --- a/ci/deps/azure-macos-35.yaml +++ b/ci/deps/azure-macos-35.yaml @@ -24,6 +24,7 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - pip: - python-dateutil==2.5.3 - hypothesis>=3.58.0 diff --git a/ci/deps/azure-windows-27.yaml b/ci/deps/azure-windows-27.yaml index b1533b071fa74..74faeed83c387 100644 --- a/ci/deps/azure-windows-27.yaml +++ b/ci/deps/azure-windows-27.yaml @@ -27,5 +27,6 @@ dependencies: - cython>=0.28.2 - pytest - pytest-xdist + - pytest-mock - moto - hypothesis>=3.58.0 diff --git a/ci/deps/azure-windows-36.yaml b/ci/deps/azure-windows-36.yaml index 7b132a134c44e..94d67b3d37788 100644 --- a/ci/deps/azure-windows-36.yaml +++ b/ci/deps/azure-windows-36.yaml @@ -25,4 +25,5 @@ dependencies: - cython>=0.28.2 - pytest - pytest-xdist + - pytest-mock - hypothesis>=3.58.0 diff --git a/ci/deps/travis-27.yaml b/ci/deps/travis-27.yaml index 2624797b24fa1..4915c003bce4e 100644 --- a/ci/deps/travis-27.yaml +++ b/ci/deps/travis-27.yaml @@ -41,6 +41,7 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - moto==1.3.4 - hypothesis>=3.58.0 - pip: diff --git a/ci/deps/travis-36-locale.yaml b/ci/deps/travis-36-locale.yaml index 2b38465c04512..2a7692f10752c 100644 --- a/ci/deps/travis-36-locale.yaml +++ b/ci/deps/travis-36-locale.yaml @@ -30,6 +30,7 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - moto - pip: - hypothesis>=3.58.0 diff --git a/ci/deps/travis-36-slow.yaml b/ci/deps/travis-36-slow.yaml index a6ffdb95e5e7c..7934d179c8618 100644 --- a/ci/deps/travis-36-slow.yaml +++ b/ci/deps/travis-36-slow.yaml @@ -27,5 +27,6 @@ dependencies: # universal - pytest - pytest-xdist + - pytest-mock - moto - hypothesis>=3.58.0 diff --git a/ci/deps/travis-36.yaml b/ci/deps/travis-36.yaml index 74db888d588f4..857c3fadfdaeb 100644 --- a/ci/deps/travis-36.yaml +++ b/ci/deps/travis-36.yaml @@ -36,6 +36,7 @@ dependencies: - pytest - pytest-xdist - pytest-cov + - pytest-mock - hypothesis>=3.58.0 - pip: - brotlipy diff --git a/ci/deps/travis-37.yaml b/ci/deps/travis-37.yaml index c503124d8cd26..125750191de7d 100644 --- a/ci/deps/travis-37.yaml +++ b/ci/deps/travis-37.yaml @@ -14,6 +14,7 @@ dependencies: - pytz - pytest - pytest-xdist + - pytest-mock - hypothesis>=3.58.0 - s3fs - pip: diff --git a/pandas/tests/io/formats/test_console.py b/pandas/tests/io/formats/test_console.py index 45c5e982c1c48..a3e0e195f4864 100644 --- a/pandas/tests/io/formats/test_console.py +++ b/pandas/tests/io/formats/test_console.py @@ -78,13 +78,12 @@ def test_detect_console_encoding_fallback_to_default(monkeypatch, std, locale): @pytest.mark.parametrize("size", ['', ['']]) -def test_terminal_unknown_dimensions(monkeypatch, size): - mock = pytest.importorskip("unittest.mock") +def test_terminal_unknown_dimensions(monkeypatch, size, mocker): def communicate(*args, **kwargs): return size - monkeypatch.setattr('subprocess.Popen', mock.Mock()) + monkeypatch.setattr('subprocess.Popen', mocker.Mock()) monkeypatch.setattr('subprocess.Popen.return_value.returncode', None) monkeypatch.setattr( 'subprocess.Popen.return_value.communicate', communicate) diff --git a/pandas/tests/resample/test_datetime_index.py b/pandas/tests/resample/test_datetime_index.py index ceccb48194f85..71b100401ec21 100644 --- a/pandas/tests/resample/test_datetime_index.py +++ b/pandas/tests/resample/test_datetime_index.py @@ -1160,9 +1160,13 @@ def test_resample_nunique_with_date_gap(): @pytest.mark.parametrize('k', [10, 100, 1000]) def test_resample_group_info(n, k): # GH10914 + + # use a fixed seed to always have the same uniques + prng = np.random.RandomState(1234) + dr = date_range(start='2015-08-27', periods=n // 10, freq='T') - ts = Series(np.random.randint(0, n // k, n).astype('int64'), - index=np.random.choice(dr, n)) + ts = Series(prng.randint(0, n // k, n).astype('int64'), + index=prng.choice(dr, n)) left = ts.resample('30T').nunique() ix = date_range(start=ts.index.min(), end=ts.index.max(), From 659e0cae6be2d7ab3370cc7d8ab936bc3ee1b159 Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Sun, 17 Feb 2019 12:27:06 -0500 Subject: [PATCH 123/215] TST: xfail excel styler tests, xref GH25351 (#25352) * TST: xfail excel styler tests, xref GH25351 * CI: cleanup .c files for cpplint>1.4 --- pandas/_libs/src/compat_helper.h | 6 +++--- pandas/_libs/src/inline_helper.h | 4 ++-- pandas/_libs/src/parse_helper.h | 2 +- pandas/_libs/src/parser/io.c | 4 ++-- pandas/_libs/src/parser/io.h | 2 +- pandas/_libs/src/parser/tokenizer.c | 2 +- pandas/_libs/src/parser/tokenizer.h | 4 ++-- pandas/_libs/tslibs/src/datetime/np_datetime.c | 2 +- .../tslibs/src/datetime/np_datetime_strings.c | 2 +- pandas/tests/io/test_excel.py | 11 +++++++---- pandas/util/move.c | 18 +++++++++--------- 11 files changed, 30 insertions(+), 27 deletions(-) diff --git a/pandas/_libs/src/compat_helper.h b/pandas/_libs/src/compat_helper.h index 462f53392adee..078069fb48af2 100644 --- a/pandas/_libs/src/compat_helper.h +++ b/pandas/_libs/src/compat_helper.h @@ -29,8 +29,8 @@ the macro, which restores compat. #ifndef PYPY_VERSION # if PY_VERSION_HEX < 0x03070000 && defined(PySlice_GetIndicesEx) # undef PySlice_GetIndicesEx -# endif -#endif +# endif // PY_VERSION_HEX +#endif // PYPY_VERSION PANDAS_INLINE int slice_get_indices(PyObject *s, Py_ssize_t length, @@ -44,7 +44,7 @@ PANDAS_INLINE int slice_get_indices(PyObject *s, #else return PySlice_GetIndicesEx((PySliceObject *)s, length, start, stop, step, slicelength); -#endif +#endif // PY_VERSION_HEX } #endif // PANDAS__LIBS_SRC_COMPAT_HELPER_H_ diff --git a/pandas/_libs/src/inline_helper.h b/pandas/_libs/src/inline_helper.h index 397ec8e7b2cb8..e203a05d2eb56 100644 --- a/pandas/_libs/src/inline_helper.h +++ b/pandas/_libs/src/inline_helper.h @@ -19,7 +19,7 @@ The full license is in the LICENSE file, distributed with this software. #define PANDAS_INLINE static inline #else #define PANDAS_INLINE - #endif -#endif + #endif // __GNUC__ +#endif // PANDAS_INLINE #endif // PANDAS__LIBS_SRC_INLINE_HELPER_H_ diff --git a/pandas/_libs/src/parse_helper.h b/pandas/_libs/src/parse_helper.h index b71131bee7008..6fcd2ed0a9ea0 100644 --- a/pandas/_libs/src/parse_helper.h +++ b/pandas/_libs/src/parse_helper.h @@ -30,7 +30,7 @@ int to_double(char *item, double *p_value, char sci, char decimal, #if PY_VERSION_HEX < 0x02060000 #define PyBytes_Check PyString_Check #define PyBytes_AS_STRING PyString_AS_STRING -#endif +#endif // PY_VERSION_HEX int floatify(PyObject *str, double *result, int *maybe_int) { int status; diff --git a/pandas/_libs/src/parser/io.c b/pandas/_libs/src/parser/io.c index 19271c78501ba..f578ce138e274 100644 --- a/pandas/_libs/src/parser/io.c +++ b/pandas/_libs/src/parser/io.c @@ -15,7 +15,7 @@ The full license is in the LICENSE file, distributed with this software. #ifndef O_BINARY #define O_BINARY 0 -#endif /* O_BINARY */ +#endif // O_BINARY /* On-disk FILE, uncompressed @@ -277,4 +277,4 @@ void *buffer_mmap_bytes(void *source, size_t nbytes, size_t *bytes_read, return NULL; } -#endif +#endif // HAVE_MMAP diff --git a/pandas/_libs/src/parser/io.h b/pandas/_libs/src/parser/io.h index d22e8ddaea88d..074322c7bdf78 100644 --- a/pandas/_libs/src/parser/io.h +++ b/pandas/_libs/src/parser/io.h @@ -25,7 +25,7 @@ typedef struct _file_source { #if !defined(_WIN32) && !defined(HAVE_MMAP) #define HAVE_MMAP -#endif +#endif // HAVE_MMAP typedef struct _memory_map { int fd; diff --git a/pandas/_libs/src/parser/tokenizer.c b/pandas/_libs/src/parser/tokenizer.c index a86af7c5416de..6acf3c3de0c91 100644 --- a/pandas/_libs/src/parser/tokenizer.c +++ b/pandas/_libs/src/parser/tokenizer.c @@ -1480,7 +1480,7 @@ int main(int argc, char *argv[]) { return 0; } -#endif +#endif // TEST // --------------------------------------------------------------------------- // Implementation of xstrtod diff --git a/pandas/_libs/src/parser/tokenizer.h b/pandas/_libs/src/parser/tokenizer.h index c32c061c7fa89..ce9dd39b16222 100644 --- a/pandas/_libs/src/parser/tokenizer.h +++ b/pandas/_libs/src/parser/tokenizer.h @@ -42,7 +42,7 @@ See LICENSE for the license #if defined(_MSC_VER) #define strtoll _strtoi64 -#endif +#endif // _MSC_VER /* @@ -75,7 +75,7 @@ See LICENSE for the license #define TRACE(X) printf X; #else #define TRACE(X) -#endif +#endif // VERBOSE #define PARSER_OUT_OF_MEMORY -1 diff --git a/pandas/_libs/tslibs/src/datetime/np_datetime.c b/pandas/_libs/tslibs/src/datetime/np_datetime.c index 866c9ca9d3ac7..87866d804503e 100644 --- a/pandas/_libs/tslibs/src/datetime/np_datetime.c +++ b/pandas/_libs/tslibs/src/datetime/np_datetime.c @@ -30,7 +30,7 @@ This file is derived from NumPy 1.7. See NUMPY_LICENSE.txt #if PY_MAJOR_VERSION >= 3 #define PyInt_AsLong PyLong_AsLong -#endif +#endif // PyInt_AsLong const npy_datetimestruct _NS_MIN_DTS = { 1677, 9, 21, 0, 12, 43, 145225, 0, 0}; diff --git a/pandas/_libs/tslibs/src/datetime/np_datetime_strings.c b/pandas/_libs/tslibs/src/datetime/np_datetime_strings.c index 05ccdd13598fb..207da4b8f8340 100644 --- a/pandas/_libs/tslibs/src/datetime/np_datetime_strings.c +++ b/pandas/_libs/tslibs/src/datetime/np_datetime_strings.c @@ -609,7 +609,7 @@ int make_iso_8601_datetime(npy_datetimestruct *dts, char *outstr, int outlen, tmplen = _snprintf(substr, sublen, "%04" NPY_INT64_FMT, dts->year); #else tmplen = snprintf(substr, sublen, "%04" NPY_INT64_FMT, dts->year); -#endif +#endif // _WIN32 /* If it ran out of space or there isn't space for the NULL terminator */ if (tmplen < 0 || tmplen > sublen) { goto string_too_short; diff --git a/pandas/tests/io/test_excel.py b/pandas/tests/io/test_excel.py index 8c92db734168b..09b2d86bde3d3 100644 --- a/pandas/tests/io/test_excel.py +++ b/pandas/tests/io/test_excel.py @@ -2413,7 +2413,10 @@ def style(df): ['', '', '']], index=df.index, columns=df.columns) - def assert_equal_style(cell1, cell2): + def assert_equal_style(cell1, cell2, engine): + if engine in ['xlsxwriter', 'openpyxl']: + pytest.xfail(reason=("GH25351: failing on some attribute " + "comparisons in {}".format(engine))) # XXX: should find a better way to check equality assert cell1.alignment.__dict__ == cell2.alignment.__dict__ assert cell1.border.__dict__ == cell2.border.__dict__ @@ -2457,7 +2460,7 @@ def custom_converter(css): assert len(col1) == len(col2) for cell1, cell2 in zip(col1, col2): assert cell1.value == cell2.value - assert_equal_style(cell1, cell2) + assert_equal_style(cell1, cell2, engine) n_cells += 1 # ensure iteration actually happened: @@ -2515,7 +2518,7 @@ def custom_converter(css): assert cell1.number_format == 'General' assert cell2.number_format == '0%' else: - assert_equal_style(cell1, cell2) + assert_equal_style(cell1, cell2, engine) assert cell1.value == cell2.value n_cells += 1 @@ -2533,7 +2536,7 @@ def custom_converter(css): assert not cell1.font.bold assert cell2.font.bold else: - assert_equal_style(cell1, cell2) + assert_equal_style(cell1, cell2, engine) assert cell1.value == cell2.value n_cells += 1 diff --git a/pandas/util/move.c b/pandas/util/move.c index 9bb662d50cb3f..188d7b79b35d2 100644 --- a/pandas/util/move.c +++ b/pandas/util/move.c @@ -19,15 +19,15 @@ The full license is in the LICENSE file, distributed with this software. /* in python 3, we cannot intern bytes objects so this is always false */ #define PyString_CHECK_INTERNED(cs) 0 -#endif /* !COMPILING_IN_PY2 */ +#endif // !COMPILING_IN_PY2 #ifndef Py_TPFLAGS_HAVE_GETCHARBUFFER #define Py_TPFLAGS_HAVE_GETCHARBUFFER 0 -#endif +#endif // Py_TPFLAGS_HAVE_GETCHARBUFFER #ifndef Py_TPFLAGS_HAVE_NEWBUFFER #define Py_TPFLAGS_HAVE_NEWBUFFER 0 -#endif +#endif // Py_TPFLAGS_HAVE_NEWBUFFER static PyObject *badmove; /* bad move exception class */ @@ -85,14 +85,14 @@ static PyBufferProcs stolenbuf_as_buffer = { (getbufferproc) stolenbuf_getbuffer, }; -#else /* Python 3 */ +#else // Python 3 static PyBufferProcs stolenbuf_as_buffer = { (getbufferproc) stolenbuf_getbuffer, NULL, }; -#endif /* COMPILING_IN_PY2 */ +#endif // COMPILING_IN_PY2 PyDoc_STRVAR(stolenbuf_doc, "A buffer that is wrapping a stolen bytes object's buffer."); @@ -208,7 +208,7 @@ static PyModuleDef move_module = { -1, methods, }; -#endif /* !COMPILING_IN_PY2 */ +#endif // !COMPILING_IN_PY2 PyDoc_STRVAR( badmove_doc, @@ -231,7 +231,7 @@ PyInit__move(void) #else #define ERROR_RETURN init_move(void) -#endif /* !COMPILING_IN_PY2 */ +#endif // !COMPILING_IN_PY2 { PyObject *m; @@ -250,7 +250,7 @@ init_move(void) if (!(m = PyModule_Create(&move_module))) #else if (!(m = Py_InitModule(MODULE_NAME, methods))) -#endif /* !COMPILING_IN_PY2 */ +#endif // !COMPILING_IN_PY2 { return ERROR_RETURN; } @@ -269,5 +269,5 @@ init_move(void) #if !COMPILING_IN_PY2 return m; -#endif /* !COMPILING_IN_PY2 */ +#endif // !COMPILING_IN_PY2 } From b90bcb59ec154a8ddb310759e47bd1c0b7a80657 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Mon, 18 Feb 2019 05:29:55 -0800 Subject: [PATCH 124/215] DOC: Correct doc mistake in combiner func (#25360) Closes gh-25359. --- doc/source/getting_started/basics.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/getting_started/basics.rst b/doc/source/getting_started/basics.rst index 02cbc7e2c3b6d..bbec7b5de1d2e 100644 --- a/doc/source/getting_started/basics.rst +++ b/doc/source/getting_started/basics.rst @@ -505,7 +505,7 @@ So, for instance, to reproduce :meth:`~DataFrame.combine_first` as above: .. ipython:: python def combiner(x, y): - np.where(pd.isna(x), y, x) + return np.where(pd.isna(x), y, x) df1.combine(df2, combiner) .. _basics.stats: From f74aba614554d1de4d7986fef6296a4eda951ac9 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Mon, 18 Feb 2019 15:37:54 +0100 Subject: [PATCH 125/215] DOC/BLD: fix --no-api option (#25209) --- doc/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 776b1bfa7bdd7..c59d28a6dc3ea 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -98,9 +98,9 @@ if (fname == 'index.rst' and os.path.abspath(dirname) == source_path): continue - elif pattern == '-api' and dirname == 'api': + elif pattern == '-api' and dirname == 'reference': exclude_patterns.append(fname) - elif fname != pattern: + elif pattern != '-api' and fname != pattern: exclude_patterns.append(fname) with open(os.path.join(source_path, 'index.rst.template')) as f: From 3d3093a1d923b02da79854918a6d43f92186e72b Mon Sep 17 00:00:00 2001 From: Alyssa Fu Ward Date: Tue, 19 Feb 2019 00:45:51 -0800 Subject: [PATCH 126/215] DOC: modify typos in Contributing section (#25365) --- doc/source/development/contributing.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/development/contributing.rst b/doc/source/development/contributing.rst index c9d6845107dfc..511936467641e 100644 --- a/doc/source/development/contributing.rst +++ b/doc/source/development/contributing.rst @@ -54,7 +54,7 @@ Bug reports must: ... ``` -#. Include the full version string of *pandas* and its dependencies. You can use the built in function:: +#. Include the full version string of *pandas* and its dependencies. You can use the built-in function:: >>> import pandas as pd >>> pd.show_versions() @@ -211,7 +211,7 @@ See the full conda docs `here `__. Creating a Python Environment (pip) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you aren't using conda for you development environment, follow these instructions. +If you aren't using conda for your development environment, follow these instructions. You'll need to have at least python3.5 installed on your system. .. code-block:: none @@ -484,7 +484,7 @@ contributing them to the project:: ./ci/code_checks.sh -The script verify the linting of code files, it looks for common mistake patterns +The script verifies the linting of code files, it looks for common mistake patterns (like missing spaces around sphinx directives that make the documentation not being rendered properly) and it also validates the doctests. It is possible to run the checks independently by using the parameters ``lint``, ``patterns`` and @@ -675,7 +675,7 @@ Otherwise, you need to do it manually: You'll also need to -1. write a new test that asserts a warning is issued when calling with the deprecated argument +1. Write a new test that asserts a warning is issued when calling with the deprecated argument 2. Update all of pandas existing tests and code to use the new argument See :ref:`contributing.warnings` for more. From 590cb547d5ca19b7fbbfb26d6bfd44b00061fab3 Mon Sep 17 00:00:00 2001 From: Devin Petersohn Date: Tue, 19 Feb 2019 05:12:34 -0800 Subject: [PATCH 127/215] Remove spurious MultiIndex creation in `_set_axis_name` (#25371) * Resovles #25370 * Introduced by #22969 --- pandas/core/generic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 3a73861086bed..6e79c02d7dbdd 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1333,7 +1333,6 @@ def _set_axis_name(self, name, axis=0, inplace=False): cat 4 monkey 2 """ - pd.MultiIndex.from_product([["mammal"], ['dog', 'cat', 'monkey']]) axis = self._get_axis_number(axis) idx = self._get_axis(axis).set_names(name) From 9561b9621fd41735b59c48a61a860b4ec9a3a1a3 Mon Sep 17 00:00:00 2001 From: Shivam Rana Date: Tue, 19 Feb 2019 18:44:24 +0530 Subject: [PATCH 128/215] #23049: test for Fatal Stack Overflow stemming From Misuse of astype('category') (#25366) --- pandas/tests/frame/test_combine_concat.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pandas/tests/frame/test_combine_concat.py b/pandas/tests/frame/test_combine_concat.py index 59497153c8524..c2364dc135a9a 100644 --- a/pandas/tests/frame/test_combine_concat.py +++ b/pandas/tests/frame/test_combine_concat.py @@ -504,6 +504,16 @@ def test_concat_numerical_names(self): names=[1, 2])) tm.assert_frame_equal(result, expected) + def test_concat_astype_dup_col(self): + # gh 23049 + df = pd.DataFrame([{'a': 'b'}]) + df = pd.concat([df, df], axis=1) + + result = df.astype('category') + expected = pd.DataFrame(np.array(["b", "b"]).reshape(1, 2), + columns=["a", "a"]).astype("category") + tm.assert_frame_equal(result, expected) + class TestDataFrameCombineFirst(TestData): From dbe357933fc7536ddf4fe8f62492a4d6a38bd0db Mon Sep 17 00:00:00 2001 From: Shivam Rana Date: Tue, 19 Feb 2019 19:02:41 +0530 Subject: [PATCH 129/215] 9236: test for the DataFrame.groupby with MultiIndex having pd.NaT (#25310) --- pandas/tests/groupby/test_groupby.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py index 1ae8efd2f6867..12a5d494648fc 100644 --- a/pandas/tests/groupby/test_groupby.py +++ b/pandas/tests/groupby/test_groupby.py @@ -1698,3 +1698,19 @@ def test_groupby_agg_ohlc_non_first(): result = df.groupby(pd.Grouper(freq='D')).agg(['sum', 'ohlc']) tm.assert_frame_equal(result, expected) + + +def test_groupby_multiindex_nat(): + # GH 9236 + values = [ + (pd.NaT, 'a'), + (datetime(2012, 1, 2), 'a'), + (datetime(2012, 1, 2), 'b'), + (datetime(2012, 1, 3), 'a') + ] + mi = pd.MultiIndex.from_tuples(values, names=['date', None]) + ser = pd.Series([3, 2, 2.5, 4], index=mi) + + result = ser.groupby(level=1).mean() + expected = pd.Series([3., 2.5], index=["a", "b"]) + assert_series_equal(result, expected) From c400bd3158bb125dbfdb19f35a5cbf9da80acbde Mon Sep 17 00:00:00 2001 From: Mak Sze Chun Date: Tue, 19 Feb 2019 21:46:03 +0800 Subject: [PATCH 130/215] [BUG] exception handling of MultiIndex.__contains__ too narrow (#25268) --- doc/source/whatsnew/v0.25.0.rst | 3 +-- pandas/core/indexes/multi.py | 2 +- pandas/tests/indexing/multiindex/test_multiindex.py | 8 ++++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 686c5ad0165e7..9d33c651ef283 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -149,10 +149,9 @@ Missing MultiIndex ^^^^^^^^^^ +- Bug in which incorrect exception raised by :meth:`pd.Timedelta` when testing the membership of :class:`MultiIndex` (:issue:`24570`) - - -- - I/O ^^^ diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index efb77b5d155a1..c19b6f61f2caa 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -840,7 +840,7 @@ def __contains__(self, key): try: self.get_loc(key) return True - except (LookupError, TypeError): + except (LookupError, TypeError, ValueError): return False contains = __contains__ diff --git a/pandas/tests/indexing/multiindex/test_multiindex.py b/pandas/tests/indexing/multiindex/test_multiindex.py index 4f5517f89e852..ccf017489e046 100644 --- a/pandas/tests/indexing/multiindex/test_multiindex.py +++ b/pandas/tests/indexing/multiindex/test_multiindex.py @@ -84,3 +84,11 @@ def test_multi_nan_indexing(self): name='a'), Index(['C1', 'C2', 'C3', 'C4'], name='b')]) tm.assert_frame_equal(result, expected) + + def test_contains(self): + # GH 24570 + tx = pd.timedelta_range('09:30:00', '16:00:00', freq='30 min') + idx = MultiIndex.from_arrays([tx, np.arange(len(tx))]) + assert tx[0] in idx + assert 'element_not_exit' not in idx + assert '0 day 09:30:00' in idx From f9cb58148c85b99b3829a496c3b8f6fc25c99a95 Mon Sep 17 00:00:00 2001 From: Shivam Rana Date: Tue, 19 Feb 2019 19:41:41 +0530 Subject: [PATCH 131/215] 14873: test for groupby.agg coercing booleans (#25327) --- .../tests/groupby/aggregate/test_aggregate.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pandas/tests/groupby/aggregate/test_aggregate.py b/pandas/tests/groupby/aggregate/test_aggregate.py index 9de8a08809009..0c2e74c0b735f 100644 --- a/pandas/tests/groupby/aggregate/test_aggregate.py +++ b/pandas/tests/groupby/aggregate/test_aggregate.py @@ -286,3 +286,20 @@ def test_multi_function_flexible_mix(df): with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): result = grouped.aggregate(d) tm.assert_frame_equal(result, expected) + + +def test_groupby_agg_coercing_bools(): + # issue 14873 + dat = pd.DataFrame( + {'a': [1, 1, 2, 2], 'b': [0, 1, 2, 3], 'c': [None, None, 1, 1]}) + gp = dat.groupby('a') + + index = Index([1, 2], name='a') + + result = gp['b'].aggregate(lambda x: (x != 0).all()) + expected = Series([False, True], index=index, name='b') + tm.assert_series_equal(result, expected) + + result = gp['c'].aggregate(lambda x: x.isnull().all()) + expected = Series([True, False], index=index, name='c') + tm.assert_series_equal(result, expected) From b2c751985ebd09b72d917d08dc06193dc0922018 Mon Sep 17 00:00:00 2001 From: Saurav Chakravorty Date: Tue, 19 Feb 2019 21:44:22 +0530 Subject: [PATCH 132/215] BUG/ENH: Timestamp.strptime (#25124) * BUG: constructor Timestamp.strptime() does not support %z. * Add doc string to NaT and Timestamp * updated the error message * Updated whatsnew entry. --- doc/source/whatsnew/v0.25.0.rst | 2 ++ pandas/_libs/tslibs/nattype.pyx | 9 ++++++++- pandas/_libs/tslibs/timestamps.pyx | 11 +++++++++++ pandas/tests/scalar/timestamp/test_timestamp.py | 8 ++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 9d33c651ef283..afde665407d18 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -28,6 +28,8 @@ Other Enhancements Backwards incompatible API changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- :meth:`Timestamp.strptime` will now rise a NotImplementedError (:issue:`21257`) + .. _whatsnew_0250.api.other: Other API Changes diff --git a/pandas/_libs/tslibs/nattype.pyx b/pandas/_libs/tslibs/nattype.pyx index a13fcfdc855d5..79e2e256c501d 100644 --- a/pandas/_libs/tslibs/nattype.pyx +++ b/pandas/_libs/tslibs/nattype.pyx @@ -374,7 +374,6 @@ class NaTType(_NaT): utctimetuple = _make_error_func('utctimetuple', datetime) timetz = _make_error_func('timetz', datetime) timetuple = _make_error_func('timetuple', datetime) - strptime = _make_error_func('strptime', datetime) strftime = _make_error_func('strftime', datetime) isocalendar = _make_error_func('isocalendar', datetime) dst = _make_error_func('dst', datetime) @@ -388,6 +387,14 @@ class NaTType(_NaT): # The remaining methods have docstrings copy/pasted from the analogous # Timestamp methods. + strptime = _make_error_func('strptime', # noqa:E128 + """ + Timestamp.strptime(string, format) + + Function is not implemented. Use pd.to_datetime(). + """ + ) + utcfromtimestamp = _make_error_func('utcfromtimestamp', # noqa:E128 """ Timestamp.utcfromtimestamp(ts) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index a2929dbeb471f..8d825e0a6179e 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -697,6 +697,17 @@ class Timestamp(_Timestamp): """ return cls(datetime.fromtimestamp(ts)) + # Issue 25016. + @classmethod + def strptime(cls, date_string, format): + """ + Timestamp.strptime(string, format) + + Function is not implemented. Use pd.to_datetime(). + """ + raise NotImplementedError("Timestamp.strptime() is not implmented." + "Use to_datetime() to parse date strings.") + @classmethod def combine(cls, date, time): """ diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index f42fad4c925f0..7d81d905eac4f 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -355,6 +355,14 @@ def test_constructor_invalid_tz(self): # interpreted as a `freq` Timestamp('2012-01-01', 'US/Pacific') + def test_constructor_strptime(self): + # GH25016 + # Test support for Timestamp.strptime + fmt = '%Y%m%d-%H%M%S-%f%z' + ts = '20190129-235348-000001+0000' + with pytest.raises(NotImplementedError): + Timestamp.strptime(ts, fmt) + def test_constructor_tz_or_tzinfo(self): # GH#17943, GH#17690, GH#5168 stamps = [Timestamp(year=2017, month=10, day=22, tz='UTC'), From 2909b830fa21c6bc2e9797aae25b13f9a060653a Mon Sep 17 00:00:00 2001 From: Zach Angell <42625717+zangell44@users.noreply.github.com> Date: Tue, 19 Feb 2019 22:51:04 -0500 Subject: [PATCH 133/215] Interval dtype fix (#25338) --- doc/source/whatsnew/v0.24.2.rst | 1 + pandas/core/dtypes/dtypes.py | 19 ++++++++++++------- pandas/tests/dtypes/test_dtypes.py | 14 +++++++++----- pandas/tests/series/test_operators.py | 7 +++++++ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 8e59c2300e7ca..f528c058d2868 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -26,6 +26,7 @@ Fixed Regressions - Fixed regression in :meth:`DataFrame.duplicated()`, where empty dataframe was not returning a boolean dtyped Series. (:issue:`25184`) - Fixed regression in :meth:`Series.min` and :meth:`Series.max` where ``numeric_only=True`` was ignored when the ``Series`` contained ```Categorical`` data (:issue:`25299`) +- Fixed regression in ``IntervalDtype`` construction where passing an incorrect string with 'Interval' as a prefix could result in a ``RecursionError``. (:issue:`25338`) .. _whatsnew_0242.enhancements: diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index 640d43f3b0e03..11a132c4d14ee 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -931,13 +931,18 @@ def construct_from_string(cls, string): attempt to construct this type from a string, raise a TypeError if its not possible """ - if (isinstance(string, compat.string_types) and - (string.startswith('interval') or - string.startswith('Interval'))): - return cls(string) + if not isinstance(string, compat.string_types): + msg = "a string needs to be passed, got type {typ}" + raise TypeError(msg.format(typ=type(string))) + + if (string.lower() == 'interval' or + cls._match.search(string) is not None): + return cls(string) - msg = "a string needs to be passed, got type {typ}" - raise TypeError(msg.format(typ=type(string))) + msg = ('Incorrectly formatted string passed to constructor. ' + 'Valid formats include Interval or Interval[dtype] ' + 'where dtype is numeric, datetime, or timedelta') + raise TypeError(msg) @property def type(self): @@ -978,7 +983,7 @@ def is_dtype(cls, dtype): return True else: return False - except ValueError: + except (ValueError, TypeError): return False else: return False diff --git a/pandas/tests/dtypes/test_dtypes.py b/pandas/tests/dtypes/test_dtypes.py index 710f215686eab..1c1442d6f2f23 100644 --- a/pandas/tests/dtypes/test_dtypes.py +++ b/pandas/tests/dtypes/test_dtypes.py @@ -511,10 +511,11 @@ def test_construction_not_supported(self, subtype): with pytest.raises(TypeError, match=msg): IntervalDtype(subtype) - def test_construction_errors(self): + @pytest.mark.parametrize('subtype', ['xx', 'IntervalA', 'Interval[foo]']) + def test_construction_errors(self, subtype): msg = 'could not construct IntervalDtype' with pytest.raises(TypeError, match=msg): - IntervalDtype('xx') + IntervalDtype(subtype) def test_construction_from_string(self): result = IntervalDtype('interval[int64]') @@ -523,7 +524,7 @@ def test_construction_from_string(self): assert is_dtype_equal(self.dtype, result) @pytest.mark.parametrize('string', [ - 'foo', 'foo[int64]', 0, 3.14, ('a', 'b'), None]) + 0, 3.14, ('a', 'b'), None]) def test_construction_from_string_errors(self, string): # these are invalid entirely msg = 'a string needs to be passed, got type' @@ -532,10 +533,12 @@ def test_construction_from_string_errors(self, string): IntervalDtype.construct_from_string(string) @pytest.mark.parametrize('string', [ - 'interval[foo]']) + 'foo', 'foo[int64]', 'IntervalA']) def test_construction_from_string_error_subtype(self, string): # this is an invalid subtype - msg = 'could not construct IntervalDtype' + msg = ("Incorrectly formatted string passed to constructor. " + r"Valid formats include Interval or Interval\[dtype\] " + "where dtype is numeric, datetime, or timedelta") with pytest.raises(TypeError, match=msg): IntervalDtype.construct_from_string(string) @@ -559,6 +562,7 @@ def test_is_dtype(self): assert not IntervalDtype.is_dtype('U') assert not IntervalDtype.is_dtype('S') assert not IntervalDtype.is_dtype('foo') + assert not IntervalDtype.is_dtype('IntervalA') assert not IntervalDtype.is_dtype(np.object_) assert not IntervalDtype.is_dtype(np.int64) assert not IntervalDtype.is_dtype(np.float64) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 4d3c9926fc5ae..b2aac441db195 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -563,6 +563,13 @@ def test_comp_ops_df_compat(self): with pytest.raises(ValueError, match=msg): left.to_frame() < right.to_frame() + def test_compare_series_interval_keyword(self): + # GH 25338 + s = Series(['IntervalA', 'IntervalB', 'IntervalC']) + result = s == 'IntervalA' + expected = Series([True, False, False]) + assert_series_equal(result, expected) + class TestSeriesFlexComparisonOps(object): From f4568fd76e864d8aee3d23f5a81302262d6e0dcb Mon Sep 17 00:00:00 2001 From: Thijs Damsma Date: Wed, 20 Feb 2019 09:04:31 +0100 Subject: [PATCH 134/215] [CLN] Excel Module Cleanups (#25275) Closes gh-25153 Authored-By: tdamsma --- pandas/io/excel/_base.py | 5 ++--- pandas/io/excel/_util.py | 39 ++++++++++++++++++++--------------- pandas/tests/io/test_excel.py | 7 ++----- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/pandas/io/excel/_base.py b/pandas/io/excel/_base.py index ed5943e9a1698..8f7bf8e0466f9 100644 --- a/pandas/io/excel/_base.py +++ b/pandas/io/excel/_base.py @@ -590,9 +590,8 @@ def __new__(cls, path, engine=None, **kwargs): if engine == 'auto': engine = _get_default_writer(ext) except KeyError: - error = ValueError("No engine for filetype: '{ext}'" - .format(ext=ext)) - raise error + raise ValueError("No engine for filetype: '{ext}'" + .format(ext=ext)) cls = get_writer(engine) return object.__new__(cls) diff --git a/pandas/io/excel/_util.py b/pandas/io/excel/_util.py index 1aeaf70f0832e..49255d83d1cd3 100644 --- a/pandas/io/excel/_util.py +++ b/pandas/io/excel/_util.py @@ -5,32 +5,39 @@ from pandas.core.dtypes.common import is_integer, is_list_like -from pandas.core import config - -_writer_extensions = ["xlsx", "xls", "xlsm"] - - _writers = {} def register_writer(klass): - """Adds engine to the excel writer registry. You must use this method to - integrate with ``to_excel``. Also adds config options for any new - ``supported_extensions`` defined on the writer.""" + """ + Add engine to the excel writer registry.io.excel. + + You must use this method to integrate with ``to_excel``. + + Parameters + ---------- + klass : ExcelWriter + """ if not callable(klass): raise ValueError("Can only register callables as engines") engine_name = klass.engine _writers[engine_name] = klass - for ext in klass.supported_extensions: - if ext.startswith('.'): - ext = ext[1:] - if ext not in _writer_extensions: - config.register_option("io.excel.{ext}.writer".format(ext=ext), - engine_name, validator=str) - _writer_extensions.append(ext) def _get_default_writer(ext): + """ + Return the default writer for the given extension. + + Parameters + ---------- + ext : str + The excel file extension for which to get the default engine. + + Returns + ------- + str + The default engine for the extension. + """ _default_writers = {'xlsx': 'openpyxl', 'xlsm': 'openpyxl', 'xls': 'xlwt'} try: import xlsxwriter # noqa @@ -230,8 +237,6 @@ def _fill_mi_header(row, control_row): return _maybe_convert_to_string(row), control_row -# fill blank if index_col not None - def _pop_header_name(row, index_col): """ diff --git a/pandas/tests/io/test_excel.py b/pandas/tests/io/test_excel.py index 09b2d86bde3d3..04c9c58a326a4 100644 --- a/pandas/tests/io/test_excel.py +++ b/pandas/tests/io/test_excel.py @@ -2359,7 +2359,7 @@ def test_register_writer(self): class DummyClass(ExcelWriter): called_save = False called_write_cells = False - supported_extensions = ['test', 'xlsx', 'xls'] + supported_extensions = ['xlsx', 'xls'] engine = 'dummy' def save(self): @@ -2377,12 +2377,9 @@ def check_called(func): with pd.option_context('io.excel.xlsx.writer', 'dummy'): register_writer(DummyClass) - writer = ExcelWriter('something.test') + writer = ExcelWriter('something.xlsx') assert isinstance(writer, DummyClass) df = tm.makeCustomDataframe(1, 1) - - func = lambda: df.to_excel('something.test') - check_called(func) check_called(lambda: df.to_excel('something.xlsx')) check_called( lambda: df.to_excel( From 66d486ef2cb622475c0d48a3faeafcf927fc1a9c Mon Sep 17 00:00:00 2001 From: tamuhey Date: Wed, 20 Feb 2019 19:28:43 +0900 Subject: [PATCH 135/215] ENH: indexing and __getitem__ of dataframe and series accept zerodim integer np.array as int (#24924) --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/core/frame.py | 1 + pandas/core/indexing.py | 3 +++ pandas/tests/indexing/test_iloc.py | 13 +++++++++++++ pandas/tests/indexing/test_loc.py | 13 +++++++++++++ pandas/tests/indexing/test_scalar.py | 13 +++++++++++++ 6 files changed, 44 insertions(+) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index afde665407d18..ef004af0ea6f7 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -19,6 +19,7 @@ including other versions of pandas. Other Enhancements ^^^^^^^^^^^^^^^^^^ +- Indexing of ``DataFrame`` and ``Series`` now accepts zerodim ``np.ndarray`` (:issue:`24919`) - :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`) - :meth:`DataFrame.at_time` and :meth:`Series.at_time` now support :meth:`datetime.time` objects with timezones (:issue:`24043`) - diff --git a/pandas/core/frame.py b/pandas/core/frame.py index a239ff4b4d5db..79f209f9ebc0a 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2838,6 +2838,7 @@ def _ixs(self, i, axis=0): return result def __getitem__(self, key): + key = lib.item_from_zerodim(key) key = com.apply_if_callable(key, self) # shortcut if the key is in columns diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 539da0beaefb4..623a48acdd48b 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -5,6 +5,7 @@ import numpy as np from pandas._libs.indexing import _NDFrameIndexerBase +from pandas._libs.lib import item_from_zerodim import pandas.compat as compat from pandas.compat import range, zip from pandas.errors import AbstractMethodError @@ -1856,6 +1857,7 @@ def _getitem_axis(self, key, axis=None): if axis is None: axis = self.axis or 0 + key = item_from_zerodim(key) if is_iterator(key): key = list(key) @@ -2222,6 +2224,7 @@ def _getitem_axis(self, key, axis=None): # a single integer else: + key = item_from_zerodim(key) if not is_integer(key): raise TypeError("Cannot index by location index with a " "non-integer key") diff --git a/pandas/tests/indexing/test_iloc.py b/pandas/tests/indexing/test_iloc.py index 5c87d553daba3..69ec6454e952a 100644 --- a/pandas/tests/indexing/test_iloc.py +++ b/pandas/tests/indexing/test_iloc.py @@ -697,3 +697,16 @@ def test_identity_slice_returns_new_object(self): # should also be a shallow copy original_series[:3] = [7, 8, 9] assert all(sliced_series[:3] == [7, 8, 9]) + + def test_indexing_zerodim_np_array(self): + # GH24919 + df = DataFrame([[1, 2], [3, 4]]) + result = df.iloc[np.array(0)] + s = pd.Series([1, 2], name=0) + tm.assert_series_equal(result, s) + + def test_series_indexing_zerodim_np_array(self): + # GH24919 + s = Series([1, 2]) + result = s.iloc[np.array(0)] + assert result == 1 diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 3bf4a6bee4af9..29f70929624fc 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -778,3 +778,16 @@ def test_loc_setitem_empty_append_raises(self): msg = "cannot copy sequence with size 2 to array axis with dimension 0" with pytest.raises(ValueError, match=msg): df.loc[0:2, 'x'] = data + + def test_indexing_zerodim_np_array(self): + # GH24924 + df = DataFrame([[1, 2], [3, 4]]) + result = df.loc[np.array(0)] + s = pd.Series([1, 2], name=0) + tm.assert_series_equal(result, s) + + def test_series_indexing_zerodim_np_array(self): + # GH24924 + s = Series([1, 2]) + result = s.loc[np.array(0)] + assert result == 1 diff --git a/pandas/tests/indexing/test_scalar.py b/pandas/tests/indexing/test_scalar.py index 6d607ce86c08e..0cd41562541d1 100644 --- a/pandas/tests/indexing/test_scalar.py +++ b/pandas/tests/indexing/test_scalar.py @@ -221,3 +221,16 @@ def test_iat_setter_incompatible_assignment(self): result.iat[0, 0] = None expected = DataFrame({"a": [None, 1], "b": [4, 5]}) tm.assert_frame_equal(result, expected) + + def test_getitem_zerodim_np_array(self): + # GH24924 + # dataframe __getitem__ + df = DataFrame([[1, 2], [3, 4]]) + result = df[np.array(0)] + expected = Series([1, 3], name=0) + tm.assert_series_equal(result, expected) + + # series __getitem__ + s = Series([1, 2]) + result = s[np.array(0)] + assert result == 1 From def8b962e50d88efdc99d78c807a14519b19cb36 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 20 Feb 2019 02:52:01 -0800 Subject: [PATCH 136/215] REGR: fix TimedeltaIndex sum and datetime subtraction with NaT (#25282, #25317) (#25329) --- doc/source/whatsnew/v0.24.2.rst | 2 ++ pandas/core/arrays/datetimes.py | 6 +++--- pandas/core/arrays/timedeltas.py | 5 +++++ pandas/core/indexes/base.py | 3 ++- pandas/tests/arithmetic/test_datetime64.py | 14 ++++++++++++++ pandas/tests/arrays/test_timedeltas.py | 22 ++++++++++++++++++++++ 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index f528c058d2868..a7e522d27f8e2 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -26,6 +26,8 @@ Fixed Regressions - Fixed regression in :meth:`DataFrame.duplicated()`, where empty dataframe was not returning a boolean dtyped Series. (:issue:`25184`) - Fixed regression in :meth:`Series.min` and :meth:`Series.max` where ``numeric_only=True`` was ignored when the ``Series`` contained ```Categorical`` data (:issue:`25299`) +- Fixed regression in subtraction between :class:`Series` objects with ``datetime64[ns]`` dtype incorrectly raising ``OverflowError`` when the `Series` on the right contains null values (:issue:`25317`) +- Fixed regression in :class:`TimedeltaIndex` where `np.sum(index)` incorrectly returned a zero-dimensional object instead of a scalar (:issue:`25282`) - Fixed regression in ``IntervalDtype`` construction where passing an incorrect string with 'Interval' as a prefix could result in a ``RecursionError``. (:issue:`25338`) .. _whatsnew_0242.enhancements: diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index cd8e8ed520ddc..75cf658423210 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -720,11 +720,11 @@ def _sub_datetime_arraylike(self, other): self_i8 = self.asi8 other_i8 = other.asi8 + arr_mask = self._isnan | other._isnan new_values = checked_add_with_arr(self_i8, -other_i8, - arr_mask=self._isnan) + arr_mask=arr_mask) if self._hasnans or other._hasnans: - mask = (self._isnan) | (other._isnan) - new_values[mask] = iNaT + new_values[arr_mask] = iNaT return new_values.view('timedelta64[ns]') def _add_offset(self, offset): diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 06e2bf76fcf96..74fe8072e6924 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -190,6 +190,8 @@ def __init__(self, values, dtype=_TD_DTYPE, freq=None, copy=False): "ndarray, or Series or Index containing one of those." ) raise ValueError(msg.format(type(values).__name__)) + if values.ndim != 1: + raise ValueError("Only 1-dimensional input arrays are supported.") if values.dtype == 'i8': # for compat with datetime/timedelta/period shared methods, @@ -945,6 +947,9 @@ def sequence_to_td64ns(data, copy=False, unit="ns", errors="raise"): .format(dtype=data.dtype)) data = np.array(data, copy=copy) + if data.ndim != 1: + raise ValueError("Only 1-dimensional input arrays are supported.") + assert data.dtype == 'm8[ns]', data return data, inferred_freq diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index f2c8ac6e9b413..b5f3c929a7f36 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -665,7 +665,8 @@ def __array_wrap__(self, result, context=None): """ Gets called after a ufunc. """ - if is_bool_dtype(result): + result = lib.item_from_zerodim(result) + if is_bool_dtype(result) or lib.is_scalar(result): return result attrs = self._get_attributes_dict() diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 405dc0805a285..c81a371f37dc1 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -1440,6 +1440,20 @@ def test_dt64arr_add_sub_offset_ndarray(self, tz_naive_fixture, class TestDatetime64OverflowHandling(object): # TODO: box + de-duplicate + def test_dt64_overflow_masking(self, box_with_array): + # GH#25317 + left = Series([Timestamp('1969-12-31')]) + right = Series([NaT]) + + left = tm.box_expected(left, box_with_array) + right = tm.box_expected(right, box_with_array) + + expected = TimedeltaIndex([NaT]) + expected = tm.box_expected(expected, box_with_array) + + result = left - right + tm.assert_equal(result, expected) + def test_dt64_series_arith_overflow(self): # GH#12534, fixed by GH#19024 dt = pd.Timestamp('1700-01-31') diff --git a/pandas/tests/arrays/test_timedeltas.py b/pandas/tests/arrays/test_timedeltas.py index 6b4662ca02e80..1fec533a14a6f 100644 --- a/pandas/tests/arrays/test_timedeltas.py +++ b/pandas/tests/arrays/test_timedeltas.py @@ -9,6 +9,18 @@ class TestTimedeltaArrayConstructor(object): + def test_only_1dim_accepted(self): + # GH#25282 + arr = np.array([0, 1, 2, 3], dtype='m8[h]').astype('m8[ns]') + + with pytest.raises(ValueError, match="Only 1-dimensional"): + # 2-dim + TimedeltaArray(arr.reshape(2, 2)) + + with pytest.raises(ValueError, match="Only 1-dimensional"): + # 0-dim + TimedeltaArray(arr[[0]].squeeze()) + def test_freq_validation(self): # ensure that the public constructor cannot create an invalid instance arr = np.array([0, 0, 1], dtype=np.int64) * 3600 * 10**9 @@ -51,6 +63,16 @@ def test_copy(self): class TestTimedeltaArray(object): + def test_np_sum(self): + # GH#25282 + vals = np.arange(5, dtype=np.int64).view('m8[h]').astype('m8[ns]') + arr = TimedeltaArray(vals) + result = np.sum(arr) + assert result == vals.sum() + + result = np.sum(pd.TimedeltaIndex(arr)) + assert result == vals.sum() + def test_from_sequence_dtype(self): msg = "dtype .*object.* cannot be converted to timedelta64" with pytest.raises(ValueError, match=msg): From 13a505d5541a8fab878c2175f60328f5816b4d25 Mon Sep 17 00:00:00 2001 From: Saurav Chakravorty Date: Wed, 20 Feb 2019 19:36:49 +0530 Subject: [PATCH 137/215] edited whatsnew typo (#25381) --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index ef004af0ea6f7..6e225185ecf84 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -29,7 +29,7 @@ Other Enhancements Backwards incompatible API changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- :meth:`Timestamp.strptime` will now rise a NotImplementedError (:issue:`21257`) +- :meth:`Timestamp.strptime` will now raise a NotImplementedError (:issue:`25016`) .. _whatsnew_0250.api.other: From 6c4cb6f08a83b20a82e0ea8cc0f2474b0fab4350 Mon Sep 17 00:00:00 2001 From: knuu Date: Thu, 21 Feb 2019 00:50:58 +0900 Subject: [PATCH 138/215] fix typo of see also in DataFrame stat funcs (#25388) --- pandas/core/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 6e79c02d7dbdd..3647565123523 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -10866,7 +10866,7 @@ def _doc_parms(cls): Series.max : Return the maximum. Series.idxmin : Return the index of the minimum. Series.idxmax : Return the index of the maximum. -DataFrame.min : Return the sum over the requested axis. +DataFrame.sum : Return the sum over the requested axis. DataFrame.min : Return the minimum over the requested axis. DataFrame.max : Return the maximum over the requested axis. DataFrame.idxmin : Return the index of the minimum over the requested axis. From 54492791116108199c24734a0220560974eb3372 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 20 Feb 2019 16:47:35 +0000 Subject: [PATCH 139/215] API: more consistent error message for MultiIndex.from_arrays (#25189) --- pandas/core/indexes/multi.py | 8 +++++++- .../tests/indexes/multi/test_constructor.py | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index c19b6f61f2caa..492d28476e1f0 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -324,11 +324,17 @@ def from_arrays(cls, arrays, sortorder=None, names=None): codes=[[0, 0, 1, 1], [1, 0, 1, 0]], names=['number', 'color']) """ + error_msg = "Input must be a list / sequence of array-likes." if not is_list_like(arrays): - raise TypeError("Input must be a list / sequence of array-likes.") + raise TypeError(error_msg) elif is_iterator(arrays): arrays = list(arrays) + # Check if elements of array are list-like + for array in arrays: + if not is_list_like(array): + raise TypeError(error_msg) + # Check if lengths of all arrays are equal or not, # raise ValueError, if not for i in range(1, len(arrays)): diff --git a/pandas/tests/indexes/multi/test_constructor.py b/pandas/tests/indexes/multi/test_constructor.py index 055d54c613260..fe90e85cf93c8 100644 --- a/pandas/tests/indexes/multi/test_constructor.py +++ b/pandas/tests/indexes/multi/test_constructor.py @@ -142,6 +142,15 @@ def test_from_arrays_iterator(idx): MultiIndex.from_arrays(0) +def test_from_arrays_tuples(idx): + arrays = tuple(tuple(np.asarray(lev).take(level_codes)) + for lev, level_codes in zip(idx.levels, idx.codes)) + + # tuple of tuples as input + result = MultiIndex.from_arrays(arrays, names=idx.names) + tm.assert_index_equal(result, idx) + + def test_from_arrays_index_series_datetimetz(): idx1 = pd.date_range('2015-01-01 10:00', freq='D', periods=3, tz='US/Eastern') @@ -254,11 +263,13 @@ def test_from_arrays_empty(): @pytest.mark.parametrize('invalid_sequence_of_arrays', [ - 1, [1], [1, 2], [[1], 2], 'a', ['a'], ['a', 'b'], [['a'], 'b']]) + 1, [1], [1, 2], [[1], 2], [1, [2]], 'a', ['a'], ['a', 'b'], [['a'], 'b'], + (1,), (1, 2), ([1], 2), (1, [2]), 'a', ('a',), ('a', 'b'), (['a'], 'b'), + [(1,), 2], [1, (2,)], [('a',), 'b'], + ((1,), 2), (1, (2,)), (('a',), 'b') +]) def test_from_arrays_invalid_input(invalid_sequence_of_arrays): - msg = (r"Input must be a list / sequence of array-likes|" - r"Input must be list-like|" - r"object of type 'int' has no len\(\)") + msg = "Input must be a list / sequence of array-likes" with pytest.raises(TypeError, match=msg): MultiIndex.from_arrays(arrays=invalid_sequence_of_arrays) From 9c0f6a8d703b6bee48918f2c5d16418a7ff736e3 Mon Sep 17 00:00:00 2001 From: h-vetinari <33685575+h-vetinari@users.noreply.github.com> Date: Thu, 21 Feb 2019 14:53:42 +0100 Subject: [PATCH 140/215] CLN: (re-)enable infer_dtype to catch complex (#25382) --- pandas/_libs/lib.pyx | 4 ++++ pandas/tests/dtypes/test_inference.py | 31 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx index 1f0f0a408aee8..34ceeb20e260e 100644 --- a/pandas/_libs/lib.pyx +++ b/pandas/_libs/lib.pyx @@ -939,6 +939,7 @@ _TYPE_MAP = { 'float32': 'floating', 'float64': 'floating', 'f': 'floating', + 'complex64': 'complex', 'complex128': 'complex', 'c': 'complex', 'string': 'string' if PY2 else 'bytes', @@ -1305,6 +1306,9 @@ def infer_dtype(value: object, skipna: object=None) -> str: elif is_decimal(val): return 'decimal' + elif is_complex(val): + return 'complex' + elif util.is_float_object(val): if is_float_array(values): return 'floating' diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 49a66efaffc11..187b37d4f788e 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -618,6 +618,37 @@ def test_decimals(self): result = lib.infer_dtype(arr, skipna=True) assert result == 'decimal' + # complex is compatible with nan, so skipna has no effect + @pytest.mark.parametrize('skipna', [True, False]) + def test_complex(self, skipna): + # gets cast to complex on array construction + arr = np.array([1.0, 2.0, 1 + 1j]) + result = lib.infer_dtype(arr, skipna=skipna) + assert result == 'complex' + + arr = np.array([1.0, 2.0, 1 + 1j], dtype='O') + result = lib.infer_dtype(arr, skipna=skipna) + assert result == 'mixed' + + # gets cast to complex on array construction + arr = np.array([1, np.nan, 1 + 1j]) + result = lib.infer_dtype(arr, skipna=skipna) + assert result == 'complex' + + arr = np.array([1.0, np.nan, 1 + 1j], dtype='O') + result = lib.infer_dtype(arr, skipna=skipna) + assert result == 'mixed' + + # complex with nans stays complex + arr = np.array([1 + 1j, np.nan, 3 + 3j], dtype='O') + result = lib.infer_dtype(arr, skipna=skipna) + assert result == 'complex' + + # test smaller complex dtype; will pass through _try_infer_map fastpath + arr = np.array([1 + 1j, np.nan, 3 + 3j], dtype=np.complex64) + result = lib.infer_dtype(arr, skipna=skipna) + assert result == 'complex' + def test_string(self): pass From b6731886db957b3e667449f67b6c95a638f2ac81 Mon Sep 17 00:00:00 2001 From: Wouter De Coster Date: Fri, 22 Feb 2019 16:13:30 +0100 Subject: [PATCH 141/215] DOC: Edited docstring of Interval (#25410) The docstring contained a repeated segment, which I removed. --- pandas/_libs/interval.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/pandas/_libs/interval.pyx b/pandas/_libs/interval.pyx index eb511b1adb28a..e86b692e9915e 100644 --- a/pandas/_libs/interval.pyx +++ b/pandas/_libs/interval.pyx @@ -150,9 +150,6 @@ cdef class Interval(IntervalMixin): Left bound for the interval. right : orderable scalar Right bound for the interval. - closed : {'left', 'right', 'both', 'neither'}, default 'right' - Whether the interval is closed on the left-side, right-side, both or - neither. closed : {'right', 'left', 'both', 'neither'}, default 'right' Whether the interval is closed on the left-side, right-side, both or neither. See the Notes for more detailed explanation. From fbe67d5ce06d6d3766beec1c38dceca89e6ca942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Fri, 22 Feb 2019 20:04:25 +0100 Subject: [PATCH 142/215] Mark test_pct_max_many_rows as high memory (#25400) Fixes issue #25384 --- pandas/tests/frame/test_rank.py | 1 + pandas/tests/series/test_rank.py | 1 + pandas/tests/test_algos.py | 1 + 3 files changed, 3 insertions(+) diff --git a/pandas/tests/frame/test_rank.py b/pandas/tests/frame/test_rank.py index 10c42e0d1a1cf..6bb9dea15d1ce 100644 --- a/pandas/tests/frame/test_rank.py +++ b/pandas/tests/frame/test_rank.py @@ -310,6 +310,7 @@ def test_rank_pct_true(self, method, exp): tm.assert_frame_equal(result, expected) @pytest.mark.single + @pytest.mark.high_memory def test_pct_max_many_rows(self): # GH 18271 df = DataFrame({'A': np.arange(2**24 + 1), diff --git a/pandas/tests/series/test_rank.py b/pandas/tests/series/test_rank.py index 510a51e002918..dfcda889269ee 100644 --- a/pandas/tests/series/test_rank.py +++ b/pandas/tests/series/test_rank.py @@ -499,6 +499,7 @@ def test_rank_first_pct(dtype, ser, exp): @pytest.mark.single +@pytest.mark.high_memory def test_pct_max_many_rows(): # GH 18271 s = Series(np.arange(2**24 + 1)) diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py index 888cf78a1c66a..cb7426ce2f7c9 100644 --- a/pandas/tests/test_algos.py +++ b/pandas/tests/test_algos.py @@ -1484,6 +1484,7 @@ def test_too_many_ndims(self): algos.rank(arr) @pytest.mark.single + @pytest.mark.high_memory @pytest.mark.parametrize('values', [ np.arange(2**24 + 1), np.arange(2**25 + 2).reshape(2**24 + 1, 2)], From f2fbebdf7b8da3d354d3957ba5e87d736849aa15 Mon Sep 17 00:00:00 2001 From: willweil <32082133+willweil@users.noreply.github.com> Date: Fri, 22 Feb 2019 19:16:37 -0500 Subject: [PATCH 143/215] Correct a typo of version number for interpolate() (#25418) --- doc/source/user_guide/missing_data.rst | 2 +- pandas/core/generic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/user_guide/missing_data.rst b/doc/source/user_guide/missing_data.rst index a462f01dcd14f..7883814e91c94 100644 --- a/doc/source/user_guide/missing_data.rst +++ b/doc/source/user_guide/missing_data.rst @@ -335,7 +335,7 @@ examined :ref:`in the API `. Interpolation ~~~~~~~~~~~~~ -.. versionadded:: 0.21.0 +.. versionadded:: 0.23.0 The ``limit_area`` keyword argument was added. diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 3647565123523..eb84a9a5810f4 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -6648,7 +6648,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, (interpolate). * 'outside': Only fill NaNs outside valid values (extrapolate). - .. versionadded:: 0.21.0 + .. versionadded:: 0.23.0 downcast : optional, 'infer' or None, defaults to None Downcast dtypes if possible. From 7408c9be6631697b8d15a3746bbb9eb6261aef22 Mon Sep 17 00:00:00 2001 From: h-vetinari <33685575+h-vetinari@users.noreply.github.com> Date: Sat, 23 Feb 2019 19:36:37 +0100 Subject: [PATCH 144/215] DEP: add pytest-mock to environment.yml (#25417) --- environment.yml | 1 + requirements-dev.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/environment.yml b/environment.yml index 47fe8e4c2a640..ce68dccca0c07 100644 --- a/environment.yml +++ b/environment.yml @@ -20,6 +20,7 @@ dependencies: - isort - moto - pytest>=4.0 + - pytest-mock - sphinx - numpydoc diff --git a/requirements-dev.txt b/requirements-dev.txt index 76aaeefa648f4..22c01ebcef7f0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,6 +11,7 @@ hypothesis>=3.82 isort moto pytest>=4.0 +pytest-mock sphinx numpydoc beautifulsoup4>=4.2.1 From 15d8178a3f81febe4d8ddb033d07e57359eb6167 Mon Sep 17 00:00:00 2001 From: Albert Villanova del Moral Date: Sat, 23 Feb 2019 20:12:32 +0100 Subject: [PATCH 145/215] BUG: Fix type coercion in read_json orient='table' (#21345) (#25219) --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/io/json/json.py | 20 +++++++++++++++---- .../tests/io/json/test_json_table_schema.py | 4 ++-- pandas/tests/io/json/test_pandas.py | 15 ++++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 6e225185ecf84..c0e00c7bf6f54 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -160,6 +160,7 @@ I/O ^^^ - Fixed bug in missing text when using :meth:`to_clipboard` if copying utf-16 characters in Python 3 on Windows (:issue:`25040`) +- Bug in :func:`read_json` for ``orient='table'`` when it tries to infer dtypes by default, which is not applicable as dtypes are already defined in the JSON schema (:issue:`21345`) - - - diff --git a/pandas/io/json/json.py b/pandas/io/json/json.py index 4bbccc8339d7c..725e2d28ffd67 100644 --- a/pandas/io/json/json.py +++ b/pandas/io/json/json.py @@ -226,7 +226,7 @@ def _write(self, obj, orient, double_precision, ensure_ascii, return serialized -def read_json(path_or_buf=None, orient=None, typ='frame', dtype=True, +def read_json(path_or_buf=None, orient=None, typ='frame', dtype=None, convert_axes=True, convert_dates=True, keep_default_dates=True, numpy=False, precise_float=False, date_unit=None, encoding=None, lines=False, chunksize=None, compression='infer'): @@ -278,8 +278,15 @@ def read_json(path_or_buf=None, orient=None, typ='frame', dtype=True, typ : type of object to recover (series or frame), default 'frame' dtype : boolean or dict, default True - If True, infer dtypes, if a dict of column to dtype, then use those, + If True, infer dtypes; if a dict of column to dtype, then use those; if False, then don't infer dtypes at all, applies only to the data. + + Not applicable with ``orient='table'``. + + .. versionchanged:: 0.25 + + Not applicable with ``orient='table'``. + convert_axes : boolean, default True Try to convert the axes to the proper dtypes. convert_dates : boolean, default True @@ -408,6 +415,11 @@ def read_json(path_or_buf=None, orient=None, typ='frame', dtype=True, {"index": "row 2", "col 1": "c", "col 2": "d"}]}' """ + if orient == 'table' and dtype: + raise ValueError("cannot pass both dtype and orient='table'") + + dtype = orient != 'table' if dtype is None else dtype + compression = _infer_compression(path_or_buf, compression) filepath_or_buffer, _, compression, should_close = get_filepath_or_buffer( path_or_buf, encoding=encoding, compression=compression, @@ -600,15 +612,15 @@ class Parser(object): 'us': long(31536000000000), 'ns': long(31536000000000000)} - def __init__(self, json, orient, dtype=True, convert_axes=True, + def __init__(self, json, orient, dtype=None, convert_axes=True, convert_dates=True, keep_default_dates=False, numpy=False, precise_float=False, date_unit=None): self.json = json if orient is None: orient = self._default_orient - self.orient = orient + self.dtype = dtype if orient == "split": diff --git a/pandas/tests/io/json/test_json_table_schema.py b/pandas/tests/io/json/test_json_table_schema.py index 6fa3b5b3b2ed4..3002d1dfb5f8a 100644 --- a/pandas/tests/io/json/test_json_table_schema.py +++ b/pandas/tests/io/json/test_json_table_schema.py @@ -502,12 +502,12 @@ class TestTableOrientReader(object): @pytest.mark.parametrize("vals", [ {'ints': [1, 2, 3, 4]}, {'objects': ['a', 'b', 'c', 'd']}, + {'objects': ['1', '2', '3', '4']}, {'date_ranges': pd.date_range('2016-01-01', freq='d', periods=4)}, {'categoricals': pd.Series(pd.Categorical(['a', 'b', 'c', 'c']))}, {'ordered_cats': pd.Series(pd.Categorical(['a', 'b', 'c', 'c'], ordered=True))}, - pytest.param({'floats': [1., 2., 3., 4.]}, - marks=pytest.mark.xfail), + {'floats': [1., 2., 3., 4.]}, {'floats': [1.1, 2.2, 3.3, 4.4]}, {'bools': [True, False, False, True]}]) def test_read_json_table_orient(self, index_nm, vals, recwarn): diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py index 0ffc8c978a228..fecd0f0572757 100644 --- a/pandas/tests/io/json/test_pandas.py +++ b/pandas/tests/io/json/test_pandas.py @@ -1202,6 +1202,21 @@ def test_data_frame_size_after_to_json(self): assert size_before == size_after + def test_from_json_to_json_table_dtypes(self): + # GH21345 + expected = pd.DataFrame({'a': [1, 2], 'b': [3., 4.], 'c': ['5', '6']}) + dfjson = expected.to_json(orient='table') + result = pd.read_json(dfjson, orient='table') + assert_frame_equal(result, expected) + + @pytest.mark.parametrize('dtype', [True, {'b': int, 'c': int}]) + def test_read_json_table_dtype_raises(self, dtype): + # GH21345 + df = pd.DataFrame({'a': [1, 2], 'b': [3., 4.], 'c': ['5', '6']}) + dfjson = df.to_json(orient='table') + with pytest.raises(ValueError): + pd.read_json(dfjson, orient='table', dtype=dtype) + @pytest.mark.parametrize('data, expected', [ (DataFrame([[1, 2], [4, 5]], columns=['a', 'b']), {'columns': ['a', 'b'], 'data': [[1, 2], [4, 5]]}), From 5557e3627df67211f5b6406d07995dccb02196f9 Mon Sep 17 00:00:00 2001 From: sudhir mohanraj Date: Sat, 23 Feb 2019 14:47:46 -0500 Subject: [PATCH 146/215] ERR: doc update for ParsingError (#25414) Closes gh-22881 --- pandas/errors/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index c57d27ff03ac6..493ee65f63c6a 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -32,6 +32,8 @@ class UnsortedIndexError(KeyError): class ParserError(ValueError): """ Exception that is raised by an error encountered in `pd.read_csv`. + + e.g. HTML Parsing will raise this error. """ From 3855a27be4f04d15e7ba7aee12f0220c93148d3d Mon Sep 17 00:00:00 2001 From: Paul Reidy Date: Sat, 23 Feb 2019 21:43:20 +0000 Subject: [PATCH 147/215] ENH: Add in sort keyword to DatetimeIndex.union (#25110) --- doc/source/styled.xlsx | Bin 0 -> 5682 bytes doc/source/whatsnew/v0.25.0.rst | 1 + pandas/core/indexes/datetimes.py | 38 ++++- pandas/tests/indexes/datetimes/test_setops.py | 138 ++++++++++++------ 4 files changed, 123 insertions(+), 54 deletions(-) create mode 100644 doc/source/styled.xlsx diff --git a/doc/source/styled.xlsx b/doc/source/styled.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1233ff2b8692bad1cfcd54f59d26eb315cfa728c GIT binary patch literal 5682 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 literal 0 HcmV?d00001 diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index c0e00c7bf6f54..83ca93bdfa703 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -22,6 +22,7 @@ Other Enhancements - Indexing of ``DataFrame`` and ``Series`` now accepts zerodim ``np.ndarray`` (:issue:`24919`) - :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`) - :meth:`DataFrame.at_time` and :meth:`Series.at_time` now support :meth:`datetime.time` objects with timezones (:issue:`24043`) +- :meth:`DatetimeIndex.union` now supports the ``sort`` argument. The behaviour of the sort parameter matches that of :meth:`Index.union` (:issue:`24994`) - .. _whatsnew_0250.api_breaking: diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 1037e2d9a3bd6..a6697e8879b08 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -460,7 +460,7 @@ def _formatter_func(self): # -------------------------------------------------------------------- # Set Operation Methods - def union(self, other): + def union(self, other, sort=None): """ Specialized union for DatetimeIndex objects. If combine overlapping ranges with the same DateOffset, will be much @@ -469,15 +469,29 @@ def union(self, other): Parameters ---------- other : DatetimeIndex or array-like + sort : bool or None, default None + Whether to sort the resulting Index. + + * None : Sort the result, except when + + 1. `self` and `other` are equal. + 2. `self` or `other` has length 0. + 3. Some values in `self` or `other` cannot be compared. + A RuntimeWarning is issued in this case. + + * False : do not sort the result + + .. versionadded:: 0.25.0 Returns ------- y : Index or DatetimeIndex """ + self._validate_sort_keyword(sort) self._assert_can_do_setop(other) if len(other) == 0 or self.equals(other) or len(self) == 0: - return super(DatetimeIndex, self).union(other) + return super(DatetimeIndex, self).union(other, sort=sort) if not isinstance(other, DatetimeIndex): try: @@ -488,9 +502,9 @@ def union(self, other): this, other = self._maybe_utc_convert(other) if this._can_fast_union(other): - return this._fast_union(other) + return this._fast_union(other, sort=sort) else: - result = Index.union(this, other) + result = Index.union(this, other, sort=sort) if isinstance(result, DatetimeIndex): # TODO: we shouldn't be setting attributes like this; # in all the tests this equality already holds @@ -563,16 +577,28 @@ def _can_fast_union(self, other): # this will raise return False - def _fast_union(self, other): + def _fast_union(self, other, sort=None): if len(other) == 0: return self.view(type(self)) if len(self) == 0: return other.view(type(self)) - # to make our life easier, "sort" the two ranges + # Both DTIs are monotonic. Check if they are already + # in the "correct" order if self[0] <= other[0]: left, right = self, other + # DTIs are not in the "correct" order and we don't want + # to sort but want to remove overlaps + elif sort is False: + left, right = self, other + left_start = left[0] + loc = right.searchsorted(left_start, side='left') + right_chunk = right.values[:loc] + dates = _concat._concat_compat((left.values, right_chunk)) + return self._shallow_copy(dates) + # DTIs are not in the "correct" order and we want + # to sort else: left, right = other, self diff --git a/pandas/tests/indexes/datetimes/test_setops.py b/pandas/tests/indexes/datetimes/test_setops.py index 19009e45ee83a..cf1f75234ec62 100644 --- a/pandas/tests/indexes/datetimes/test_setops.py +++ b/pandas/tests/indexes/datetimes/test_setops.py @@ -21,83 +21,107 @@ class TestDatetimeIndexSetOps(object): 'dateutil/US/Pacific'] # TODO: moved from test_datetimelike; dedup with version below - def test_union2(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union2(self, sort): everything = tm.makeDateIndex(10) first = everything[:5] second = everything[5:] - union = first.union(second) - assert tm.equalContents(union, everything) + union = first.union(second, sort=sort) + tm.assert_index_equal(union, everything) # GH 10149 cases = [klass(second.values) for klass in [np.array, Series, list]] for case in cases: - result = first.union(case) - assert tm.equalContents(result, everything) + result = first.union(case, sort=sort) + tm.assert_index_equal(result, everything) @pytest.mark.parametrize("tz", tz) - def test_union(self, tz): + @pytest.mark.parametrize("sort", [None, False]) + def test_union(self, tz, sort): rng1 = pd.date_range('1/1/2000', freq='D', periods=5, tz=tz) other1 = pd.date_range('1/6/2000', freq='D', periods=5, tz=tz) expected1 = pd.date_range('1/1/2000', freq='D', periods=10, tz=tz) + expected1_notsorted = pd.DatetimeIndex(list(other1) + list(rng1)) rng2 = pd.date_range('1/1/2000', freq='D', periods=5, tz=tz) other2 = pd.date_range('1/4/2000', freq='D', periods=5, tz=tz) expected2 = pd.date_range('1/1/2000', freq='D', periods=8, tz=tz) + expected2_notsorted = pd.DatetimeIndex(list(other2) + list(rng2[:3])) rng3 = pd.date_range('1/1/2000', freq='D', periods=5, tz=tz) other3 = pd.DatetimeIndex([], tz=tz) expected3 = pd.date_range('1/1/2000', freq='D', periods=5, tz=tz) + expected3_notsorted = rng3 - for rng, other, expected in [(rng1, other1, expected1), - (rng2, other2, expected2), - (rng3, other3, expected3)]: + for rng, other, exp, exp_notsorted in [(rng1, other1, expected1, + expected1_notsorted), + (rng2, other2, expected2, + expected2_notsorted), + (rng3, other3, expected3, + expected3_notsorted)]: - result_union = rng.union(other) - tm.assert_index_equal(result_union, expected) + result_union = rng.union(other, sort=sort) + tm.assert_index_equal(result_union, exp) - def test_union_coverage(self): + result_union = other.union(rng, sort=sort) + if sort is None: + tm.assert_index_equal(result_union, exp) + else: + tm.assert_index_equal(result_union, exp_notsorted) + + @pytest.mark.parametrize("sort", [None, False]) + def test_union_coverage(self, sort): idx = DatetimeIndex(['2000-01-03', '2000-01-01', '2000-01-02']) ordered = DatetimeIndex(idx.sort_values(), freq='infer') - result = ordered.union(idx) + result = ordered.union(idx, sort=sort) tm.assert_index_equal(result, ordered) - result = ordered[:0].union(ordered) + result = ordered[:0].union(ordered, sort=sort) tm.assert_index_equal(result, ordered) assert result.freq == ordered.freq - def test_union_bug_1730(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union_bug_1730(self, sort): rng_a = date_range('1/1/2012', periods=4, freq='3H') rng_b = date_range('1/1/2012', periods=4, freq='4H') - result = rng_a.union(rng_b) + result = rng_a.union(rng_b, sort=sort) exp = DatetimeIndex(sorted(set(list(rng_a)) | set(list(rng_b)))) tm.assert_index_equal(result, exp) - def test_union_bug_1745(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union_bug_1745(self, sort): left = DatetimeIndex(['2012-05-11 15:19:49.695000']) right = DatetimeIndex(['2012-05-29 13:04:21.322000', '2012-05-11 15:27:24.873000', '2012-05-11 15:31:05.350000']) - result = left.union(right) - exp = DatetimeIndex(sorted(set(list(left)) | set(list(right)))) + result = left.union(right, sort=sort) + exp = DatetimeIndex(['2012-05-11 15:19:49.695000', + '2012-05-29 13:04:21.322000', + '2012-05-11 15:27:24.873000', + '2012-05-11 15:31:05.350000']) + if sort is None: + exp = exp.sort_values() tm.assert_index_equal(result, exp) - def test_union_bug_4564(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union_bug_4564(self, sort): from pandas import DateOffset left = date_range("2013-01-01", "2013-02-01") right = left + DateOffset(minutes=15) - result = left.union(right) + result = left.union(right, sort=sort) exp = DatetimeIndex(sorted(set(list(left)) | set(list(right)))) tm.assert_index_equal(result, exp) - def test_union_freq_both_none(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union_freq_both_none(self, sort): # GH11086 expected = bdate_range('20150101', periods=10) expected.freq = None - result = expected.union(expected) + result = expected.union(expected, sort=sort) tm.assert_index_equal(result, expected) assert result.freq is None @@ -112,11 +136,14 @@ def test_union_dataframe_index(self): exp = pd.date_range('1/1/1980', '1/1/2012', freq='MS') tm.assert_index_equal(df.index, exp) - def test_union_with_DatetimeIndex(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union_with_DatetimeIndex(self, sort): i1 = Int64Index(np.arange(0, 20, 2)) i2 = date_range(start='2012-01-03 00:00:00', periods=10, freq='D') - i1.union(i2) # Works - i2.union(i1) # Fails with "AttributeError: can't set attribute" + # Works + i1.union(i2, sort=sort) + # Fails with "AttributeError: can't set attribute" + i2.union(i1, sort=sort) # TODO: moved from test_datetimelike; de-duplicate with version below def test_intersection2(self): @@ -262,11 +289,12 @@ def test_datetimeindex_diff(self, sort): periods=98) assert len(dti1.difference(dti2, sort)) == 2 - def test_datetimeindex_union_join_empty(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_datetimeindex_union_join_empty(self, sort): dti = date_range(start='1/1/2001', end='2/1/2001', freq='D') empty = Index([]) - result = dti.union(empty) + result = dti.union(empty, sort=sort) assert isinstance(result, DatetimeIndex) assert result is result @@ -287,35 +315,40 @@ class TestBusinessDatetimeIndex(object): def setup_method(self, method): self.rng = bdate_range(START, END) - def test_union(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union(self, sort): # overlapping left = self.rng[:10] right = self.rng[5:10] - the_union = left.union(right) + the_union = left.union(right, sort=sort) assert isinstance(the_union, DatetimeIndex) # non-overlapping, gap in middle left = self.rng[:5] right = self.rng[10:] - the_union = left.union(right) + the_union = left.union(right, sort=sort) assert isinstance(the_union, Index) # non-overlapping, no gap left = self.rng[:5] right = self.rng[5:10] - the_union = left.union(right) + the_union = left.union(right, sort=sort) assert isinstance(the_union, DatetimeIndex) # order does not matter - tm.assert_index_equal(right.union(left), the_union) + if sort is None: + tm.assert_index_equal(right.union(left, sort=sort), the_union) + else: + expected = pd.DatetimeIndex(list(right) + list(left)) + tm.assert_index_equal(right.union(left, sort=sort), expected) # overlapping, but different offset rng = date_range(START, END, freq=BMonthEnd()) - the_union = self.rng.union(rng) + the_union = self.rng.union(rng, sort=sort) assert isinstance(the_union, DatetimeIndex) def test_outer_join(self): @@ -350,16 +383,21 @@ def test_outer_join(self): assert isinstance(the_join, DatetimeIndex) assert the_join.freq is None - def test_union_not_cacheable(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union_not_cacheable(self, sort): rng = date_range('1/1/2000', periods=50, freq=Minute()) rng1 = rng[10:] rng2 = rng[:25] - the_union = rng1.union(rng2) - tm.assert_index_equal(the_union, rng) + the_union = rng1.union(rng2, sort=sort) + if sort is None: + tm.assert_index_equal(the_union, rng) + else: + expected = pd.DatetimeIndex(list(rng[10:]) + list(rng[:10])) + tm.assert_index_equal(the_union, expected) rng1 = rng[10:] rng2 = rng[15:35] - the_union = rng1.union(rng2) + the_union = rng1.union(rng2, sort=sort) expected = rng[10:] tm.assert_index_equal(the_union, expected) @@ -388,7 +426,8 @@ def test_intersection_bug(self): result = a.intersection(b) tm.assert_index_equal(result, b) - def test_month_range_union_tz_pytz(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_month_range_union_tz_pytz(self, sort): from pytz import timezone tz = timezone('US/Eastern') @@ -403,10 +442,11 @@ def test_month_range_union_tz_pytz(self): late_dr = date_range(start=late_start, end=late_end, tz=tz, freq=MonthEnd()) - early_dr.union(late_dr) + early_dr.union(late_dr, sort=sort) @td.skip_if_windows_python_3 - def test_month_range_union_tz_dateutil(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_month_range_union_tz_dateutil(self, sort): from pandas._libs.tslibs.timezones import dateutil_gettz tz = dateutil_gettz('US/Eastern') @@ -421,7 +461,7 @@ def test_month_range_union_tz_dateutil(self): late_dr = date_range(start=late_start, end=late_end, tz=tz, freq=MonthEnd()) - early_dr.union(late_dr) + early_dr.union(late_dr, sort=sort) class TestCustomDatetimeIndex(object): @@ -429,35 +469,37 @@ class TestCustomDatetimeIndex(object): def setup_method(self, method): self.rng = bdate_range(START, END, freq='C') - def test_union(self): + @pytest.mark.parametrize("sort", [None, False]) + def test_union(self, sort): # overlapping left = self.rng[:10] right = self.rng[5:10] - the_union = left.union(right) + the_union = left.union(right, sort=sort) assert isinstance(the_union, DatetimeIndex) # non-overlapping, gap in middle left = self.rng[:5] right = self.rng[10:] - the_union = left.union(right) + the_union = left.union(right, sort) assert isinstance(the_union, Index) # non-overlapping, no gap left = self.rng[:5] right = self.rng[5:10] - the_union = left.union(right) + the_union = left.union(right, sort=sort) assert isinstance(the_union, DatetimeIndex) # order does not matter - tm.assert_index_equal(right.union(left), the_union) + if sort is None: + tm.assert_index_equal(right.union(left, sort=sort), the_union) # overlapping, but different offset rng = date_range(START, END, freq=BMonthEnd()) - the_union = self.rng.union(rng) + the_union = self.rng.union(rng, sort=sort) assert isinstance(the_union, DatetimeIndex) def test_outer_join(self): From 183dc02e3538f559c675b82c84fc282a6bb95741 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Sat, 23 Feb 2019 19:27:12 -0800 Subject: [PATCH 148/215] DOC: Rewriting of ParserError doc + minor spacing (#25421) Follow-up to gh-25414. --- pandas/errors/__init__.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index 493ee65f63c6a..7d5a7f1a99e41 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -9,10 +9,10 @@ class PerformanceWarning(Warning): """ - Warning raised when there is a possible - performance impact. + Warning raised when there is a possible performance impact. """ + class UnsupportedFunctionCall(ValueError): """ Exception raised when attempting to call a numpy function @@ -20,6 +20,7 @@ class UnsupportedFunctionCall(ValueError): the object e.g. ``np.cumsum(groupby_object)``. """ + class UnsortedIndexError(KeyError): """ Error raised when attempting to get a slice of a MultiIndex, @@ -31,9 +32,15 @@ class UnsortedIndexError(KeyError): class ParserError(ValueError): """ - Exception that is raised by an error encountered in `pd.read_csv`. + Exception that is raised by an error encountered in parsing file contents. + + This is a generic error raised for errors encountered when functions like + `read_csv` or `read_html` are parsing contents of a file. - e.g. HTML Parsing will raise this error. + See Also + -------- + read_csv : Read CSV (comma-separated) file into a DataFrame. + read_html : Read HTML table into a DataFrame. """ @@ -182,4 +189,4 @@ def __str__(self): else: name = self.class_instance.__class__.__name__ msg = "This {methodtype} must be defined in the concrete class {name}" - return (msg.format(methodtype=self.methodtype, name=name)) + return msg.format(methodtype=self.methodtype, name=name) From 5ae9b48eee11fbb05fd930f97f232120a7bc4713 Mon Sep 17 00:00:00 2001 From: h-vetinari <33685575+h-vetinari@users.noreply.github.com> Date: Sun, 24 Feb 2019 04:34:18 +0100 Subject: [PATCH 149/215] API/ERR: allow iterators in df.set_index & improve errors (#24984) --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/compat/__init__.py | 2 ++ pandas/core/frame.py | 43 ++++++++++++++++++++++++-- pandas/tests/frame/test_alter_axes.py | 44 +++++++++++++++++++++------ 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 83ca93bdfa703..4ea5d935a6920 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -22,6 +22,7 @@ Other Enhancements - Indexing of ``DataFrame`` and ``Series`` now accepts zerodim ``np.ndarray`` (:issue:`24919`) - :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`) - :meth:`DataFrame.at_time` and :meth:`Series.at_time` now support :meth:`datetime.time` objects with timezones (:issue:`24043`) +- :meth:`DataFrame.set_index` now works for instances of ``abc.Iterator``, provided their output is of the same length as the calling frame (:issue:`22484`, :issue:`24984`) - :meth:`DatetimeIndex.union` now supports the ``sort`` argument. The behaviour of the sort parameter matches that of :meth:`Index.union` (:issue:`24994`) - diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index d7ca7f8963f70..4036af85b7212 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -137,6 +137,7 @@ def lfilter(*args, **kwargs): reload = reload Hashable = collections.abc.Hashable Iterable = collections.abc.Iterable + Iterator = collections.abc.Iterator Mapping = collections.abc.Mapping MutableMapping = collections.abc.MutableMapping Sequence = collections.abc.Sequence @@ -199,6 +200,7 @@ def get_range_parameters(data): Hashable = collections.Hashable Iterable = collections.Iterable + Iterator = collections.Iterator Mapping = collections.Mapping MutableMapping = collections.MutableMapping Sequence = collections.Sequence diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 79f209f9ebc0a..608e5c53ec094 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -33,7 +33,7 @@ from pandas import compat from pandas.compat import (range, map, zip, lmap, lzip, StringIO, u, - PY36, raise_with_traceback, + PY36, raise_with_traceback, Iterator, string_and_binary_types) from pandas.compat.numpy import function as nv from pandas.core.dtypes.cast import ( @@ -4025,7 +4025,8 @@ def set_index(self, keys, drop=True, append=False, inplace=False, This parameter can be either a single column key, a single array of the same length as the calling DataFrame, or a list containing an arbitrary combination of column keys and arrays. Here, "array" - encompasses :class:`Series`, :class:`Index` and ``np.ndarray``. + encompasses :class:`Series`, :class:`Index`, ``np.ndarray``, and + instances of :class:`abc.Iterator`. drop : bool, default True Delete columns to be used as the new index. append : bool, default False @@ -4104,6 +4105,32 @@ def set_index(self, keys, drop=True, append=False, inplace=False, if not isinstance(keys, list): keys = [keys] + err_msg = ('The parameter "keys" may be a column key, one-dimensional ' + 'array, or a list containing only valid column keys and ' + 'one-dimensional arrays.') + + missing = [] + for col in keys: + if isinstance(col, (ABCIndexClass, ABCSeries, np.ndarray, + list, Iterator)): + # arrays are fine as long as they are one-dimensional + # iterators get converted to list below + if getattr(col, 'ndim', 1) != 1: + raise ValueError(err_msg) + else: + # everything else gets tried as a key; see GH 24969 + try: + found = col in self.columns + except TypeError: + raise TypeError(err_msg + ' Received column of ' + 'type {}'.format(type(col))) + else: + if not found: + missing.append(col) + + if missing: + raise KeyError('None of {} are in the columns'.format(missing)) + if inplace: frame = self else: @@ -4132,6 +4159,9 @@ def set_index(self, keys, drop=True, append=False, inplace=False, elif isinstance(col, (list, np.ndarray)): arrays.append(col) names.append(None) + elif isinstance(col, Iterator): + arrays.append(list(col)) + names.append(None) # from here, col can only be a column label else: arrays.append(frame[col]._values) @@ -4139,6 +4169,15 @@ def set_index(self, keys, drop=True, append=False, inplace=False, if drop: to_remove.append(col) + if len(arrays[-1]) != len(self): + # check newest element against length of calling frame, since + # ensure_index_from_sequences would not raise for append=False. + raise ValueError('Length mismatch: Expected {len_self} rows, ' + 'received array of length {len_col}'.format( + len_self=len(self), + len_col=len(arrays[-1]) + )) + index = ensure_index_from_sequences(arrays, names) if verify_integrity and not index.is_unique: diff --git a/pandas/tests/frame/test_alter_axes.py b/pandas/tests/frame/test_alter_axes.py index cc3687f856b4e..a25e893e08900 100644 --- a/pandas/tests/frame/test_alter_axes.py +++ b/pandas/tests/frame/test_alter_axes.py @@ -178,10 +178,10 @@ def test_set_index_pass_arrays(self, frame_of_index_cols, # MultiIndex constructor does not work directly on Series -> lambda # We also emulate a "constructor" for the label -> lambda # also test index name if append=True (name is duplicate here for A) - @pytest.mark.parametrize('box2', [Series, Index, np.array, list, + @pytest.mark.parametrize('box2', [Series, Index, np.array, list, iter, lambda x: MultiIndex.from_arrays([x]), lambda x: x.name]) - @pytest.mark.parametrize('box1', [Series, Index, np.array, list, + @pytest.mark.parametrize('box1', [Series, Index, np.array, list, iter, lambda x: MultiIndex.from_arrays([x]), lambda x: x.name]) @pytest.mark.parametrize('append, index_name', [(True, None), @@ -195,6 +195,9 @@ def test_set_index_pass_arrays_duplicate(self, frame_of_index_cols, drop, keys = [box1(df['A']), box2(df['A'])] result = df.set_index(keys, drop=drop, append=append) + # if either box is iter, it has been consumed; re-read + keys = [box1(df['A']), box2(df['A'])] + # need to adapt first drop for case that both keys are 'A' -- # cannot drop the same column twice; # use "is" because == would give ambiguous Boolean error for containers @@ -253,25 +256,48 @@ def test_set_index_raise_keys(self, frame_of_index_cols, drop, append): df.set_index(['A', df['A'], tuple(df['A'])], drop=drop, append=append) - @pytest.mark.xfail(reason='broken due to revert, see GH 25085') @pytest.mark.parametrize('append', [True, False]) @pytest.mark.parametrize('drop', [True, False]) - @pytest.mark.parametrize('box', [set, iter, lambda x: (y for y in x)], - ids=['set', 'iter', 'generator']) + @pytest.mark.parametrize('box', [set], ids=['set']) def test_set_index_raise_on_type(self, frame_of_index_cols, box, drop, append): df = frame_of_index_cols msg = 'The parameter "keys" may be a column key, .*' - # forbidden type, e.g. set/iter/generator + # forbidden type, e.g. set with pytest.raises(TypeError, match=msg): df.set_index(box(df['A']), drop=drop, append=append) - # forbidden type in list, e.g. set/iter/generator + # forbidden type in list, e.g. set with pytest.raises(TypeError, match=msg): df.set_index(['A', df['A'], box(df['A'])], drop=drop, append=append) + # MultiIndex constructor does not work directly on Series -> lambda + @pytest.mark.parametrize('box', [Series, Index, np.array, iter, + lambda x: MultiIndex.from_arrays([x])], + ids=['Series', 'Index', 'np.array', + 'iter', 'MultiIndex']) + @pytest.mark.parametrize('length', [4, 6], ids=['too_short', 'too_long']) + @pytest.mark.parametrize('append', [True, False]) + @pytest.mark.parametrize('drop', [True, False]) + def test_set_index_raise_on_len(self, frame_of_index_cols, box, length, + drop, append): + # GH 24984 + df = frame_of_index_cols # has length 5 + + values = np.random.randint(0, 10, (length,)) + + msg = 'Length mismatch: Expected 5 rows, received array of length.*' + + # wrong length directly + with pytest.raises(ValueError, match=msg): + df.set_index(box(values), drop=drop, append=append) + + # wrong length in list + with pytest.raises(ValueError, match=msg): + df.set_index(['A', df.A, box(values)], drop=drop, append=append) + def test_set_index_custom_label_type(self): # GH 24969 @@ -341,7 +367,7 @@ def __repr__(self): # missing key thing3 = Thing(['Three', 'pink']) - msg = '.*' # due to revert, see GH 25085 + msg = r"frozenset\(\{'Three', 'pink'\}\)" with pytest.raises(KeyError, match=msg): # missing label directly df.set_index(thing3) @@ -366,7 +392,7 @@ def __str__(self): thing2 = Thing('Two', 'blue') df = DataFrame([[0, 2], [1, 3]], columns=[thing1, thing2]) - msg = 'unhashable type.*' + msg = 'The parameter "keys" may be a column key, .*' with pytest.raises(TypeError, match=msg): # use custom label directly From fc1fe838a1976c38762b2632d6bac2bea76cd039 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Sat, 23 Feb 2019 19:37:39 -0800 Subject: [PATCH 150/215] BUG: Indexing with UTC offset string no longer ignored (#25263) --- doc/source/user_guide/timeseries.rst | 10 +++ doc/source/whatsnew/v0.25.0.rst | 34 +++++++- pandas/core/indexes/base.py | 21 ++++- pandas/core/indexes/datetimes.py | 80 ++++++++++--------- .../tests/indexes/datetimes/test_datetime.py | 2 +- .../indexes/datetimes/test_partial_slicing.py | 27 +++++++ 6 files changed, 129 insertions(+), 45 deletions(-) diff --git a/doc/source/user_guide/timeseries.rst b/doc/source/user_guide/timeseries.rst index 23f1aabd69ff3..4e2c428415926 100644 --- a/doc/source/user_guide/timeseries.rst +++ b/doc/source/user_guide/timeseries.rst @@ -633,6 +633,16 @@ We are stopping on the included end-point as it is part of the index: dft2 = dft2.swaplevel(0, 1).sort_index() dft2.loc[idx[:, '2013-01-05'], :] +.. versionadded:: 0.25.0 + +Slicing with string indexing also honors UTC offset. + +.. ipython:: python + + df = pd.DataFrame([0], index=pd.DatetimeIndex(['2019-01-01'], tz='US/Pacific')) + df + df['2019-01-01 12:00:00+04:00':'2019-01-01 13:00:00+04:00'] + .. _timeseries.slice_vs_exact_match: Slice vs. Exact Match diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 4ea5d935a6920..a1734532668b8 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -31,7 +31,37 @@ Other Enhancements Backwards incompatible API changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- :meth:`Timestamp.strptime` will now raise a NotImplementedError (:issue:`25016`) +.. _whatsnew_0250.api_breaking.utc_offset_indexing: + +Indexing with date strings with UTC offsets +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Indexing a :class:`DataFrame` or :class:`Series` with a :class:`DatetimeIndex` with a +date string with a UTC offset would previously ignore the UTC offset. Now, the UTC offset +is respected in indexing. (:issue:`24076`, :issue:`16785`) + +*Previous Behavior*: + +.. code-block:: ipython + + In [1]: df = pd.DataFrame([0], index=pd.DatetimeIndex(['2019-01-01'], tz='US/Pacific')) + + In [2]: df + Out[2]: + 0 + 2019-01-01 00:00:00-08:00 0 + + In [3]: df['2019-01-01 00:00:00+04:00':'2019-01-01 01:00:00+04:00'] + Out[3]: + 0 + 2019-01-01 00:00:00-08:00 0 + +*New Behavior*: + +.. ipython:: ipython + + df = pd.DataFrame([0], index=pd.DatetimeIndex(['2019-01-01'], tz='US/Pacific')) + df['2019-01-01 12:00:00+04:00':'2019-01-01 13:00:00+04:00'] .. _whatsnew_0250.api.other: @@ -40,7 +70,7 @@ Other API Changes - :class:`DatetimeTZDtype` will now standardize pytz timezones to a common timezone instance (:issue:`24713`) - ``Timestamp`` and ``Timedelta`` scalars now implement the :meth:`to_numpy` method as aliases to :meth:`Timestamp.to_datetime64` and :meth:`Timedelta.to_timedelta64`, respectively. (:issue:`24653`) -- +- :meth:`Timestamp.strptime` will now rise a ``NotImplementedError`` (:issue:`25016`) - .. _whatsnew_0250.deprecations: diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index b5f3c929a7f36..1cdacc908b663 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -6,9 +6,10 @@ import numpy as np from pandas._libs import ( - Timedelta, algos as libalgos, index as libindex, join as libjoin, lib, - tslibs) + algos as libalgos, index as libindex, join as libjoin, lib) from pandas._libs.lib import is_datetime_array +from pandas._libs.tslibs import OutOfBoundsDatetime, Timedelta, Timestamp +from pandas._libs.tslibs.timezones import tz_compare import pandas.compat as compat from pandas.compat import range, set_function_name, u from pandas.compat.numpy import function as nv @@ -447,7 +448,7 @@ def __new__(cls, data=None, dtype=None, copy=False, name=None, try: return DatetimeIndex(subarr, copy=copy, name=name, **kwargs) - except tslibs.OutOfBoundsDatetime: + except OutOfBoundsDatetime: pass elif inferred.startswith('timedelta'): @@ -4867,6 +4868,20 @@ def slice_locs(self, start=None, end=None, step=None, kind=None): # If it's a reverse slice, temporarily swap bounds. start, end = end, start + # GH 16785: If start and end happen to be date strings with UTC offsets + # attempt to parse and check that the offsets are the same + if (isinstance(start, (compat.string_types, datetime)) + and isinstance(end, (compat.string_types, datetime))): + try: + ts_start = Timestamp(start) + ts_end = Timestamp(end) + except (ValueError, TypeError): + pass + else: + if not tz_compare(ts_start.tzinfo, ts_end.tzinfo): + raise ValueError("Both dates must have the " + "same UTC offset") + start_slice = None if start is not None: start_slice = self.get_slice_bound(start, 'left', kind) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index a6697e8879b08..b8d052ce7be04 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -32,9 +32,8 @@ from pandas.core.ops import get_op_result_name import pandas.core.tools.datetimes as tools -from pandas.tseries import offsets from pandas.tseries.frequencies import Resolution, to_offset -from pandas.tseries.offsets import CDay, prefix_mapping +from pandas.tseries.offsets import CDay, Nano, prefix_mapping def _new_DatetimeIndex(cls, d): @@ -852,54 +851,57 @@ def _parsed_string_to_bounds(self, reso, parsed): lower, upper: pd.Timestamp """ + valid_resos = {'year', 'month', 'quarter', 'day', 'hour', 'minute', + 'second', 'minute', 'second', 'microsecond'} + if reso not in valid_resos: + raise KeyError if reso == 'year': - return (Timestamp(datetime(parsed.year, 1, 1), tz=self.tz), - Timestamp(datetime(parsed.year, 12, 31, 23, - 59, 59, 999999), tz=self.tz)) + start = Timestamp(parsed.year, 1, 1) + end = Timestamp(parsed.year, 12, 31, 23, 59, 59, 999999) elif reso == 'month': d = ccalendar.get_days_in_month(parsed.year, parsed.month) - return (Timestamp(datetime(parsed.year, parsed.month, 1), - tz=self.tz), - Timestamp(datetime(parsed.year, parsed.month, d, 23, - 59, 59, 999999), tz=self.tz)) + start = Timestamp(parsed.year, parsed.month, 1) + end = Timestamp(parsed.year, parsed.month, d, 23, 59, 59, 999999) elif reso == 'quarter': qe = (((parsed.month - 1) + 2) % 12) + 1 # two months ahead d = ccalendar.get_days_in_month(parsed.year, qe) # at end of month - return (Timestamp(datetime(parsed.year, parsed.month, 1), - tz=self.tz), - Timestamp(datetime(parsed.year, qe, d, 23, 59, - 59, 999999), tz=self.tz)) + start = Timestamp(parsed.year, parsed.month, 1) + end = Timestamp(parsed.year, qe, d, 23, 59, 59, 999999) elif reso == 'day': - st = datetime(parsed.year, parsed.month, parsed.day) - return (Timestamp(st, tz=self.tz), - Timestamp(Timestamp(st + offsets.Day(), - tz=self.tz).value - 1)) + start = Timestamp(parsed.year, parsed.month, parsed.day) + end = start + timedelta(days=1) - Nano(1) elif reso == 'hour': - st = datetime(parsed.year, parsed.month, parsed.day, - hour=parsed.hour) - return (Timestamp(st, tz=self.tz), - Timestamp(Timestamp(st + offsets.Hour(), - tz=self.tz).value - 1)) + start = Timestamp(parsed.year, parsed.month, parsed.day, + parsed.hour) + end = start + timedelta(hours=1) - Nano(1) elif reso == 'minute': - st = datetime(parsed.year, parsed.month, parsed.day, - hour=parsed.hour, minute=parsed.minute) - return (Timestamp(st, tz=self.tz), - Timestamp(Timestamp(st + offsets.Minute(), - tz=self.tz).value - 1)) + start = Timestamp(parsed.year, parsed.month, parsed.day, + parsed.hour, parsed.minute) + end = start + timedelta(minutes=1) - Nano(1) elif reso == 'second': - st = datetime(parsed.year, parsed.month, parsed.day, - hour=parsed.hour, minute=parsed.minute, - second=parsed.second) - return (Timestamp(st, tz=self.tz), - Timestamp(Timestamp(st + offsets.Second(), - tz=self.tz).value - 1)) + start = Timestamp(parsed.year, parsed.month, parsed.day, + parsed.hour, parsed.minute, parsed.second) + end = start + timedelta(seconds=1) - Nano(1) elif reso == 'microsecond': - st = datetime(parsed.year, parsed.month, parsed.day, - parsed.hour, parsed.minute, parsed.second, - parsed.microsecond) - return (Timestamp(st, tz=self.tz), Timestamp(st, tz=self.tz)) - else: - raise KeyError + start = Timestamp(parsed.year, parsed.month, parsed.day, + parsed.hour, parsed.minute, parsed.second, + parsed.microsecond) + end = start + timedelta(microseconds=1) - Nano(1) + # GH 24076 + # If an incoming date string contained a UTC offset, need to localize + # the parsed date to this offset first before aligning with the index's + # timezone + if parsed.tzinfo is not None: + if self.tz is None: + raise ValueError("The index must be timezone aware " + "when indexing with a date string with a " + "UTC offset") + start = start.tz_localize(parsed.tzinfo).tz_convert(self.tz) + end = end.tz_localize(parsed.tzinfo).tz_convert(self.tz) + elif self.tz is not None: + start = start.tz_localize(self.tz) + end = end.tz_localize(self.tz) + return start, end def _partial_date_slice(self, reso, parsed, use_lhs=True, use_rhs=True): is_monotonic = self.is_monotonic diff --git a/pandas/tests/indexes/datetimes/test_datetime.py b/pandas/tests/indexes/datetimes/test_datetime.py index e1ba0e1708442..a3ee5fe39769f 100644 --- a/pandas/tests/indexes/datetimes/test_datetime.py +++ b/pandas/tests/indexes/datetimes/test_datetime.py @@ -102,7 +102,7 @@ def test_stringified_slice_with_tz(self): # GH#2658 import datetime start = datetime.datetime.now() - idx = date_range(start=start, freq="1d", periods=10) + idx = date_range(start=start, freq="1d", periods=10, tz='US/Eastern') df = DataFrame(lrange(10), index=idx) df["2013-01-14 23:44:34.437768-05:00":] # no exception here diff --git a/pandas/tests/indexes/datetimes/test_partial_slicing.py b/pandas/tests/indexes/datetimes/test_partial_slicing.py index a0c9d9f02385c..64693324521b3 100644 --- a/pandas/tests/indexes/datetimes/test_partial_slicing.py +++ b/pandas/tests/indexes/datetimes/test_partial_slicing.py @@ -396,3 +396,30 @@ def test_selection_by_datetimelike(self, datetimelike, op, expected): result = op(df.A, datetimelike) expected = Series(expected, name='A') tm.assert_series_equal(result, expected) + + @pytest.mark.parametrize('start', [ + '2018-12-02 21:50:00+00:00', pd.Timestamp('2018-12-02 21:50:00+00:00'), + pd.Timestamp('2018-12-02 21:50:00+00:00').to_pydatetime() + ]) + @pytest.mark.parametrize('end', [ + '2018-12-02 21:52:00+00:00', pd.Timestamp('2018-12-02 21:52:00+00:00'), + pd.Timestamp('2018-12-02 21:52:00+00:00').to_pydatetime() + ]) + def test_getitem_with_datestring_with_UTC_offset(self, start, end): + # GH 24076 + idx = pd.date_range(start='2018-12-02 14:50:00-07:00', + end='2018-12-02 14:50:00-07:00', freq='1min') + df = pd.DataFrame(1, index=idx, columns=['A']) + result = df[start:end] + expected = df.iloc[0:3, :] + tm.assert_frame_equal(result, expected) + + # GH 16785 + start = str(start) + end = str(end) + with pytest.raises(ValueError, match="Both dates must"): + df[start:end[:-4] + '1:00'] + + with pytest.raises(ValueError, match="The index must be timezone"): + df = df.tz_localize(None) + df[start:end] From df039bfe57aad0419378f4fe9476e07e35ba204a Mon Sep 17 00:00:00 2001 From: topper-123 Date: Sun, 24 Feb 2019 03:40:07 +0000 Subject: [PATCH 151/215] PERF/REF: improve performance of Series.searchsorted, PandasArray.searchsorted, collect functionality (#22034) --- asv_bench/benchmarks/series_methods.py | 19 ++++++ doc/source/whatsnew/v0.25.0.rst | 3 +- pandas/core/algorithms.py | 85 +++++++++++++++++++++++++- pandas/core/arrays/base.py | 18 +++--- pandas/core/arrays/numpy_.py | 7 +++ pandas/core/base.py | 6 +- pandas/core/series.py | 8 +-- pandas/tests/arrays/test_array.py | 49 +++++++++++++++ 8 files changed, 175 insertions(+), 20 deletions(-) diff --git a/asv_bench/benchmarks/series_methods.py b/asv_bench/benchmarks/series_methods.py index f7d0083b86a01..3303483c50e20 100644 --- a/asv_bench/benchmarks/series_methods.py +++ b/asv_bench/benchmarks/series_methods.py @@ -124,6 +124,25 @@ def time_dropna(self, dtype): self.s.dropna() +class SearchSorted(object): + + goal_time = 0.2 + params = ['int8', 'int16', 'int32', 'int64', + 'uint8', 'uint16', 'uint32', 'uint64', + 'float16', 'float32', 'float64', + 'str'] + param_names = ['dtype'] + + def setup(self, dtype): + N = 10**5 + data = np.array([1] * N + [2] * N + [3] * N).astype(dtype) + self.s = Series(data) + + def time_searchsorted(self, dtype): + key = '2' if dtype == 'str' else 2 + self.s.searchsorted(key) + + class Map(object): params = ['dict', 'Series'] diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index a1734532668b8..170e7f14da397 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -96,7 +96,8 @@ Performance Improvements - Significant speedup in `SparseArray` initialization that benefits most operations, fixing performance regression introduced in v0.20.0 (:issue:`24985`) - `DataFrame.to_stata()` is now faster when outputting data with any string or non-native endian columns (:issue:`25045`) -- +- Improved performance of :meth:`Series.searchsorted`. The speedup is especially large when the dtype is + int8/int16/int32 and the searched key is within the integer bounds for the dtype (:issue:`22034`) .. _whatsnew_0250.bug_fixes: diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index c5c8f47ad6dba..b056a357d0a51 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -19,7 +19,7 @@ ensure_float64, ensure_int64, ensure_object, ensure_platform_int, ensure_uint64, is_array_like, is_bool_dtype, is_categorical_dtype, is_complex_dtype, is_datetime64_any_dtype, is_datetime64tz_dtype, - is_datetimelike, is_extension_array_dtype, is_float_dtype, + is_datetimelike, is_extension_array_dtype, is_float_dtype, is_integer, is_integer_dtype, is_interval_dtype, is_list_like, is_numeric_dtype, is_object_dtype, is_period_dtype, is_scalar, is_signed_integer_dtype, is_sparse, is_timedelta64_dtype, is_unsigned_integer_dtype, @@ -1724,6 +1724,89 @@ def func(arr, indexer, out, fill_value=np.nan): return out +# ------------ # +# searchsorted # +# ------------ # + +def searchsorted(arr, value, side="left", sorter=None): + """ + Find indices where elements should be inserted to maintain order. + + .. versionadded:: 0.25.0 + + Find the indices into a sorted array `arr` (a) such that, if the + corresponding elements in `value` were inserted before the indices, + the order of `arr` would be preserved. + + Assuming that `arr` is sorted: + + ====== ================================ + `side` returned index `i` satisfies + ====== ================================ + left ``arr[i-1] < value <= self[i]`` + right ``arr[i-1] <= value < self[i]`` + ====== ================================ + + Parameters + ---------- + arr: array-like + Input array. If `sorter` is None, then it must be sorted in + ascending order, otherwise `sorter` must be an array of indices + that sort it. + value : array_like + Values to insert into `arr`. + side : {'left', 'right'}, optional + If 'left', the index of the first suitable location found is given. + If 'right', return the last such index. If there is no suitable + index, return either 0 or N (where N is the length of `self`). + sorter : 1-D array_like, optional + Optional array of integer indices that sort array a into ascending + order. They are typically the result of argsort. + + Returns + ------- + array of ints + Array of insertion points with the same shape as `value`. + + See Also + -------- + numpy.searchsorted : Similar method from NumPy. + """ + if sorter is not None: + sorter = ensure_platform_int(sorter) + + if isinstance(arr, np.ndarray) and is_integer_dtype(arr) and ( + is_integer(value) or is_integer_dtype(value)): + from .arrays.array_ import array + # if `arr` and `value` have different dtypes, `arr` would be + # recast by numpy, causing a slow search. + # Before searching below, we therefore try to give `value` the + # same dtype as `arr`, while guarding against integer overflows. + iinfo = np.iinfo(arr.dtype.type) + value_arr = np.array([value]) if is_scalar(value) else np.array(value) + if (value_arr >= iinfo.min).all() and (value_arr <= iinfo.max).all(): + # value within bounds, so no overflow, so can convert value dtype + # to dtype of arr + dtype = arr.dtype + else: + dtype = value_arr.dtype + + if is_scalar(value): + value = dtype.type(value) + else: + value = array(value, dtype=dtype) + elif not (is_object_dtype(arr) or is_numeric_dtype(arr) or + is_categorical_dtype(arr)): + from pandas.core.series import Series + # E.g. if `arr` is an array with dtype='datetime64[ns]' + # and `value` is a pd.Timestamp, we may need to convert value + value_ser = Series(value)._values + value = value_ser[0] if is_scalar(value) else value_ser + + result = arr.searchsorted(value, side=side, sorter=sorter) + return result + + # ---- # # diff # # ---- # diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index 7aaefef3d03e5..e770281596134 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -555,17 +555,17 @@ def searchsorted(self, value, side="left", sorter=None): .. versionadded:: 0.24.0 Find the indices into a sorted array `self` (a) such that, if the - corresponding elements in `v` were inserted before the indices, the - order of `self` would be preserved. + corresponding elements in `value` were inserted before the indices, + the order of `self` would be preserved. - Assuming that `a` is sorted: + Assuming that `self` is sorted: - ====== ============================ + ====== ================================ `side` returned index `i` satisfies - ====== ============================ - left ``self[i-1] < v <= self[i]`` - right ``self[i-1] <= v < self[i]`` - ====== ============================ + ====== ================================ + left ``self[i-1] < value <= self[i]`` + right ``self[i-1] <= value < self[i]`` + ====== ================================ Parameters ---------- @@ -581,7 +581,7 @@ def searchsorted(self, value, side="left", sorter=None): Returns ------- - indices : array of ints + array of ints Array of insertion points with the same shape as `value`. See Also diff --git a/pandas/core/arrays/numpy_.py b/pandas/core/arrays/numpy_.py index 791ff44303e96..8e2ab586cacb6 100644 --- a/pandas/core/arrays/numpy_.py +++ b/pandas/core/arrays/numpy_.py @@ -4,6 +4,7 @@ from pandas._libs import lib from pandas.compat.numpy import function as nv +from pandas.util._decorators import Appender from pandas.util._validators import validate_fillna_kwargs from pandas.core.dtypes.dtypes import ExtensionDtype @@ -12,6 +13,7 @@ from pandas import compat from pandas.core import nanops +from pandas.core.algorithms import searchsorted from pandas.core.missing import backfill_1d, pad_1d from .base import ExtensionArray, ExtensionOpsMixin @@ -423,6 +425,11 @@ def to_numpy(self, dtype=None, copy=False): return result + @Appender(ExtensionArray.searchsorted.__doc__) + def searchsorted(self, value, side='left', sorter=None): + return searchsorted(self.to_numpy(), value, + side=side, sorter=sorter) + # ------------------------------------------------------------------------ # Ops diff --git a/pandas/core/base.py b/pandas/core/base.py index 7fdc64a8d9f85..f896596dd5216 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -1522,11 +1522,11 @@ def factorize(self, sort=False, na_sentinel=-1): array([3]) """) - @Substitution(klass='IndexOpsMixin') + @Substitution(klass='Index') @Appender(_shared_docs['searchsorted']) def searchsorted(self, value, side='left', sorter=None): - # needs coercion on the key (DatetimeIndex does already) - return self._values.searchsorted(value, side=side, sorter=sorter) + return algorithms.searchsorted(self._values, value, + side=side, sorter=sorter) def drop_duplicates(self, keep='first', inplace=False): inplace = validate_bool_kwarg(inplace, 'inplace') diff --git a/pandas/core/series.py b/pandas/core/series.py index a5dfe8d43c336..ad7c6af21f637 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -2392,12 +2392,8 @@ def __rmatmul__(self, other): @Substitution(klass='Series') @Appender(base._shared_docs['searchsorted']) def searchsorted(self, value, side='left', sorter=None): - if sorter is not None: - sorter = ensure_platform_int(sorter) - result = self._values.searchsorted(Series(value)._values, - side=side, sorter=sorter) - - return result[0] if is_scalar(value) else result + return algorithms.searchsorted(self._values, value, + side=side, sorter=sorter) # ------------------------------------------------------------------- # Combination diff --git a/pandas/tests/arrays/test_array.py b/pandas/tests/arrays/test_array.py index 9fea1989e46df..b68ec2bf348b4 100644 --- a/pandas/tests/arrays/test_array.py +++ b/pandas/tests/arrays/test_array.py @@ -9,6 +9,7 @@ import pandas as pd from pandas.api.extensions import register_extension_dtype +from pandas.api.types import is_scalar from pandas.core.arrays import PandasArray, integer_array, period_array from pandas.tests.extension.decimal import ( DecimalArray, DecimalDtype, to_decimal) @@ -254,3 +255,51 @@ def test_array_not_registered(registry_without_decimal): result = pd.array(data, dtype=DecimalDtype) expected = DecimalArray._from_sequence(data) tm.assert_equal(result, expected) + + +class TestArrayAnalytics(object): + def test_searchsorted(self, string_dtype): + arr = pd.array(['a', 'b', 'c'], dtype=string_dtype) + + result = arr.searchsorted('a', side='left') + assert is_scalar(result) + assert result == 0 + + result = arr.searchsorted('a', side='right') + assert is_scalar(result) + assert result == 1 + + def test_searchsorted_numeric_dtypes_scalar(self, any_real_dtype): + arr = pd.array([1, 3, 90], dtype=any_real_dtype) + result = arr.searchsorted(30) + assert is_scalar(result) + assert result == 2 + + result = arr.searchsorted([30]) + expected = np.array([2], dtype=np.intp) + tm.assert_numpy_array_equal(result, expected) + + def test_searchsorted_numeric_dtypes_vector(self, any_real_dtype): + arr = pd.array([1, 3, 90], dtype=any_real_dtype) + result = arr.searchsorted([2, 30]) + expected = np.array([1, 2], dtype=np.intp) + tm.assert_numpy_array_equal(result, expected) + + @pytest.mark.parametrize('arr, val', [ + [pd.date_range('20120101', periods=10, freq='2D'), + pd.Timestamp('20120102')], + [pd.date_range('20120101', periods=10, freq='2D', tz='Asia/Hong_Kong'), + pd.Timestamp('20120102', tz='Asia/Hong_Kong')], + [pd.timedelta_range(start='1 day', end='10 days', periods=10), + pd.Timedelta('2 days')]]) + def test_search_sorted_datetime64_scalar(self, arr, val): + arr = pd.array(arr) + result = arr.searchsorted(val) + assert is_scalar(result) + assert result == 1 + + def test_searchsorted_sorter(self, any_real_dtype): + arr = pd.array([3, 1, 2], dtype=any_real_dtype) + result = arr.searchsorted([0, 3], sorter=np.argsort(arr)) + expected = np.array([0, 2], dtype=np.intp) + tm.assert_numpy_array_equal(result, expected) From f59a6ab2e993f0e2f78babd02e39297adfb4333a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 23 Feb 2019 19:45:28 -0800 Subject: [PATCH 152/215] TST: remove never-used singleton fixtures (#24885) --- pandas/tests/frame/conftest.py | 54 ---------------------------------- 1 file changed, 54 deletions(-) diff --git a/pandas/tests/frame/conftest.py b/pandas/tests/frame/conftest.py index 377e737a53158..69ee614ab8d2a 100644 --- a/pandas/tests/frame/conftest.py +++ b/pandas/tests/frame/conftest.py @@ -29,16 +29,6 @@ def float_frame_with_na(): return df -@pytest.fixture -def float_frame2(): - """ - Fixture for DataFrame of floats with index of unique strings - - Columns are ['D', 'C', 'B', 'A'] - """ - return DataFrame(tm.getSeriesData(), columns=['D', 'C', 'B', 'A']) - - @pytest.fixture def bool_frame_with_na(): """ @@ -104,21 +94,6 @@ def mixed_float_frame(): return df -@pytest.fixture -def mixed_float_frame2(): - """ - Fixture for DataFrame of different float types with index of unique strings - - Columns are ['A', 'B', 'C', 'D']. - """ - df = DataFrame(tm.getSeriesData()) - df.D = df.D.astype('float32') - df.C = df.C.astype('float32') - df.B = df.B.astype('float16') - df.D = df.D.astype('float64') - return df - - @pytest.fixture def mixed_int_frame(): """ @@ -135,19 +110,6 @@ def mixed_int_frame(): return df -@pytest.fixture -def mixed_type_frame(): - """ - Fixture for DataFrame of float/int/string columns with RangeIndex - - Columns are ['a', 'b', 'c', 'float32', 'int32']. - """ - return DataFrame({'a': 1., 'b': 2, 'c': 'foo', - 'float32': np.array([1.] * 10, dtype='float32'), - 'int32': np.array([1] * 10, dtype='int32')}, - index=np.arange(10)) - - @pytest.fixture def timezone_frame(): """ @@ -173,22 +135,6 @@ def empty_frame(): return DataFrame({}) -@pytest.fixture -def datetime_series(): - """ - Fixture for Series of floats with DatetimeIndex - """ - return tm.makeTimeSeries(nper=30) - - -@pytest.fixture -def datetime_series_short(): - """ - Fixture for Series of floats with DatetimeIndex - """ - return tm.makeTimeSeries(nper=30)[5:] - - @pytest.fixture def simple_frame(): """ From 85572de5e7bb188cfecc575ee56786406e79dc79 Mon Sep 17 00:00:00 2001 From: Josh Date: Sat, 23 Feb 2019 22:47:23 -0500 Subject: [PATCH 153/215] BUG: fixed merging with empty frame containing an Int64 column (#25183) (#25289) --- doc/source/whatsnew/v0.24.2.rst | 2 +- pandas/core/internals/concat.py | 2 + pandas/tests/reshape/merge/test_merge.py | 78 ++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index a7e522d27f8e2..8f4beb3f484a4 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -96,7 +96,7 @@ Bug Fixes **Other** - Bug in :meth:`Series.is_unique` where single occurrences of ``NaN`` were not considered unique (:issue:`25180`) -- +- Bug in :func:`merge` when merging an empty ``DataFrame`` with an ``Int64`` column or a non-empty ``DataFrame`` with an ``Int64`` column that is all ``NaN`` (:issue:`25183`) - .. _whatsnew_0.242.contributors: diff --git a/pandas/core/internals/concat.py b/pandas/core/internals/concat.py index 640587b7f9f31..cb98274962656 100644 --- a/pandas/core/internals/concat.py +++ b/pandas/core/internals/concat.py @@ -190,6 +190,8 @@ def get_reindexed_values(self, empty_dtype, upcasted_na): pass elif getattr(self.block, 'is_sparse', False): pass + elif getattr(self.block, 'is_extension', False): + pass else: missing_arr = np.empty(self.shape, dtype=empty_dtype) missing_arr.fill(fill_value) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index 25487ccc76e62..7a97368504fd6 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -39,6 +39,54 @@ def get_test_data(ngroups=NGROUPS, n=N): return arr +def get_series(): + return [ + pd.Series([1], dtype='int64'), + pd.Series([1], dtype='Int64'), + pd.Series([1.23]), + pd.Series(['foo']), + pd.Series([True]), + pd.Series([pd.Timestamp('2018-01-01')]), + pd.Series([pd.Timestamp('2018-01-01', tz='US/Eastern')]), + ] + + +def get_series_na(): + return [ + pd.Series([np.nan], dtype='Int64'), + pd.Series([np.nan], dtype='float'), + pd.Series([np.nan], dtype='object'), + pd.Series([pd.NaT]), + ] + + +@pytest.fixture(params=get_series(), ids=lambda x: x.dtype.name) +def series_of_dtype(request): + """ + A parametrized fixture returning a variety of Series of different + dtypes + """ + return request.param + + +@pytest.fixture(params=get_series(), ids=lambda x: x.dtype.name) +def series_of_dtype2(request): + """ + A duplicate of the series_of_dtype fixture, so that it can be used + twice by a single function + """ + return request.param + + +@pytest.fixture(params=get_series_na(), ids=lambda x: x.dtype.name) +def series_of_dtype_all_na(request): + """ + A parametrized fixture returning a variety of Series with all NA + values + """ + return request.param + + class TestMerge(object): def setup_method(self, method): @@ -428,6 +476,36 @@ def check2(exp, kwarg): check1(exp_in, kwarg) check2(exp_out, kwarg) + def test_merge_empty_frame(self, series_of_dtype, series_of_dtype2): + # GH 25183 + df = pd.DataFrame({'key': series_of_dtype, 'value': series_of_dtype2}, + columns=['key', 'value']) + df_empty = df[:0] + expected = pd.DataFrame({ + 'value_x': pd.Series(dtype=df.dtypes['value']), + 'key': pd.Series(dtype=df.dtypes['key']), + 'value_y': pd.Series(dtype=df.dtypes['value']), + }, columns=['value_x', 'key', 'value_y']) + actual = df_empty.merge(df, on='key') + assert_frame_equal(actual, expected) + + def test_merge_all_na_column(self, series_of_dtype, + series_of_dtype_all_na): + # GH 25183 + df_left = pd.DataFrame( + {'key': series_of_dtype, 'value': series_of_dtype_all_na}, + columns=['key', 'value']) + df_right = pd.DataFrame( + {'key': series_of_dtype, 'value': series_of_dtype_all_na}, + columns=['key', 'value']) + expected = pd.DataFrame({ + 'key': series_of_dtype, + 'value_x': series_of_dtype_all_na, + 'value_y': series_of_dtype_all_na, + }, columns=['key', 'value_x', 'value_y']) + actual = df_left.merge(df_right, on='key') + assert_frame_equal(actual, expected) + def test_merge_nosort(self): # #2098, anything to do? From aa084162bcaa7ce0efdc044bc8077f6bfca70674 Mon Sep 17 00:00:00 2001 From: ThibTrip <40694343+ThibTrip@users.noreply.github.com> Date: Mon, 25 Feb 2019 23:35:45 +0100 Subject: [PATCH 154/215] DOC: fixed geo accessor example in extending.rst (#25420) I realised "lon" and "lat" had just been switched with "longitude" and "latitude" in the following code block. So I used those names here as well. --- doc/source/development/extending.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/source/development/extending.rst b/doc/source/development/extending.rst index e6928d9efde06..9e5034f6d3db0 100644 --- a/doc/source/development/extending.rst +++ b/doc/source/development/extending.rst @@ -33,8 +33,9 @@ decorate a class, providing the name of attribute to add. The class's @staticmethod def _validate(obj): - if 'lat' not in obj.columns or 'lon' not in obj.columns: - raise AttributeError("Must have 'lat' and 'lon'.") + # verify there is a column latitude and a column longitude + if 'latitude' not in obj.columns or 'longitude' not in obj.columns: + raise AttributeError("Must have 'latitude' and 'longitude'.") @property def center(self): From fe1654faa86836a0007bb513504e57c5c9935b8b Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 27 Feb 2019 14:40:32 +0000 Subject: [PATCH 155/215] TST: numpy RuntimeWarning with Series.round() (#25432) --- pandas/tests/frame/test_analytics.py | 13 ++++++++++++- pandas/tests/series/test_analytics.py | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index 2e690ebbfa121..43a45bb915819 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -8,7 +8,7 @@ import numpy as np import pytest -from pandas.compat import PY35, lrange +from pandas.compat import PY2, PY35, is_platform_windows, lrange import pandas.util._test_decorators as td import pandas as pd @@ -1842,6 +1842,17 @@ def test_numpy_round(self): with pytest.raises(ValueError, match=msg): np.round(df, decimals=0, out=df) + @pytest.mark.xfail( + PY2 and is_platform_windows(), reason="numpy/numpy#7882", + raises=AssertionError, strict=True) + def test_numpy_round_nan(self): + # See gh-14197 + df = Series([1.53, np.nan, 0.06]).to_frame() + with tm.assert_produces_warning(None): + result = df.round() + expected = Series([2., np.nan, 0.]).to_frame() + tm.assert_frame_equal(result, expected) + def test_round_mixed_type(self): # GH 11885 df = DataFrame({'col1': [1.1, 2.2, 3.3, 4.4], diff --git a/pandas/tests/series/test_analytics.py b/pandas/tests/series/test_analytics.py index 6811e370726b2..1f265d574da15 100644 --- a/pandas/tests/series/test_analytics.py +++ b/pandas/tests/series/test_analytics.py @@ -9,7 +9,7 @@ from numpy import nan import pytest -from pandas.compat import PY35, lrange, range +from pandas.compat import PY2, PY35, is_platform_windows, lrange, range import pandas.util._test_decorators as td import pandas as pd @@ -285,6 +285,17 @@ def test_numpy_round(self): with pytest.raises(ValueError, match=msg): np.round(s, decimals=0, out=s) + @pytest.mark.xfail( + PY2 and is_platform_windows(), reason="numpy/numpy#7882", + raises=AssertionError, strict=True) + def test_numpy_round_nan(self): + # See gh-14197 + s = Series([1.53, np.nan, 0.06]) + with tm.assert_produces_warning(None): + result = s.round() + expected = Series([2., np.nan, 0.]) + assert_series_equal(result, expected) + def test_built_in_round(self): if not compat.PY3: pytest.skip( From 1490d0c790ba974b2e85c3c46ff721aee679d54a Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 27 Feb 2019 22:26:53 +0000 Subject: [PATCH 156/215] CI: add __init__.py to isort skip list (#25455) --- ci/deps/azure-27-compat.yaml | 1 + ci/deps/azure-27-locale.yaml | 1 + ci/deps/azure-36-locale_slow.yaml | 1 + ci/deps/azure-37-locale.yaml | 1 + ci/deps/azure-37-numpydev.yaml | 1 + ci/deps/azure-macos-35.yaml | 1 + ci/deps/azure-windows-27.yaml | 1 + ci/deps/azure-windows-36.yaml | 1 + ci/deps/travis-27.yaml | 1 + ci/deps/travis-36-doc.yaml | 1 + ci/deps/travis-36-locale.yaml | 1 + ci/deps/travis-36-slow.yaml | 1 + ci/deps/travis-36.yaml | 1 + ci/deps/travis-37.yaml | 1 + setup.cfg | 20 ++++++++++++++++++++ 15 files changed, 34 insertions(+) diff --git a/ci/deps/azure-27-compat.yaml b/ci/deps/azure-27-compat.yaml index 986855c464852..c68b51fbd6644 100644 --- a/ci/deps/azure-27-compat.yaml +++ b/ci/deps/azure-27-compat.yaml @@ -21,6 +21,7 @@ dependencies: - pytest - pytest-xdist - pytest-mock + - isort - pip: - html5lib==1.0b2 - beautifulsoup4==4.2.1 diff --git a/ci/deps/azure-27-locale.yaml b/ci/deps/azure-27-locale.yaml index f73079ecbe3d2..5679c503caddc 100644 --- a/ci/deps/azure-27-locale.yaml +++ b/ci/deps/azure-27-locale.yaml @@ -24,6 +24,7 @@ dependencies: - pytest-xdist - pytest-mock - hypothesis>=3.58.0 + - isort - pip: - html5lib==1.0b2 - beautifulsoup4==4.2.1 diff --git a/ci/deps/azure-36-locale_slow.yaml b/ci/deps/azure-36-locale_slow.yaml index 6b8d38fd25082..de1f4ad0e9a76 100644 --- a/ci/deps/azure-36-locale_slow.yaml +++ b/ci/deps/azure-36-locale_slow.yaml @@ -30,5 +30,6 @@ dependencies: - pytest-xdist - pytest-mock - moto + - isort - pip: - hypothesis>=3.58.0 diff --git a/ci/deps/azure-37-locale.yaml b/ci/deps/azure-37-locale.yaml index 569b71dae003b..a89e63a2b7d3a 100644 --- a/ci/deps/azure-37-locale.yaml +++ b/ci/deps/azure-37-locale.yaml @@ -28,6 +28,7 @@ dependencies: - pytest - pytest-xdist - pytest-mock + - isort - pip: - hypothesis>=3.58.0 - moto # latest moto in conda-forge fails with 3.7, move to conda dependencies when this is fixed diff --git a/ci/deps/azure-37-numpydev.yaml b/ci/deps/azure-37-numpydev.yaml index a37be124cc546..3132de891299c 100644 --- a/ci/deps/azure-37-numpydev.yaml +++ b/ci/deps/azure-37-numpydev.yaml @@ -10,6 +10,7 @@ dependencies: - pytest-xdist - pytest-mock - hypothesis>=3.58.0 + - isort - pip: - "git+git://github.com/dateutil/dateutil.git" - "-f https://7933911d6844c6c53a7d-47bd50c35cd79bd838daf386af554a83.ssl.cf2.rackcdn.com" diff --git a/ci/deps/azure-macos-35.yaml b/ci/deps/azure-macos-35.yaml index d1fe926744ecd..9710bcb5bf43d 100644 --- a/ci/deps/azure-macos-35.yaml +++ b/ci/deps/azure-macos-35.yaml @@ -25,6 +25,7 @@ dependencies: - pytest - pytest-xdist - pytest-mock + - isort - pip: - python-dateutil==2.5.3 - hypothesis>=3.58.0 diff --git a/ci/deps/azure-windows-27.yaml b/ci/deps/azure-windows-27.yaml index 74faeed83c387..093c055e69553 100644 --- a/ci/deps/azure-windows-27.yaml +++ b/ci/deps/azure-windows-27.yaml @@ -30,3 +30,4 @@ dependencies: - pytest-mock - moto - hypothesis>=3.58.0 + - isort diff --git a/ci/deps/azure-windows-36.yaml b/ci/deps/azure-windows-36.yaml index 94d67b3d37788..e9db271a75d9d 100644 --- a/ci/deps/azure-windows-36.yaml +++ b/ci/deps/azure-windows-36.yaml @@ -27,3 +27,4 @@ dependencies: - pytest-xdist - pytest-mock - hypothesis>=3.58.0 + - isort diff --git a/ci/deps/travis-27.yaml b/ci/deps/travis-27.yaml index 4915c003bce4e..71b224b2c68c2 100644 --- a/ci/deps/travis-27.yaml +++ b/ci/deps/travis-27.yaml @@ -44,6 +44,7 @@ dependencies: - pytest-mock - moto==1.3.4 - hypothesis>=3.58.0 + - isort - pip: - backports.lzma - pandas-gbq diff --git a/ci/deps/travis-36-doc.yaml b/ci/deps/travis-36-doc.yaml index 26f3a17432ab2..1a65d292ef085 100644 --- a/ci/deps/travis-36-doc.yaml +++ b/ci/deps/travis-36-doc.yaml @@ -43,3 +43,4 @@ dependencies: # universal - pytest - pytest-xdist + - isort diff --git a/ci/deps/travis-36-locale.yaml b/ci/deps/travis-36-locale.yaml index 2a7692f10752c..36dbb8013104a 100644 --- a/ci/deps/travis-36-locale.yaml +++ b/ci/deps/travis-36-locale.yaml @@ -32,5 +32,6 @@ dependencies: - pytest-xdist - pytest-mock - moto + - isort - pip: - hypothesis>=3.58.0 diff --git a/ci/deps/travis-36-slow.yaml b/ci/deps/travis-36-slow.yaml index 7934d179c8618..f4b9091c4300b 100644 --- a/ci/deps/travis-36-slow.yaml +++ b/ci/deps/travis-36-slow.yaml @@ -30,3 +30,4 @@ dependencies: - pytest-mock - moto - hypothesis>=3.58.0 + - isort diff --git a/ci/deps/travis-36.yaml b/ci/deps/travis-36.yaml index 857c3fadfdaeb..e22529784b5ec 100644 --- a/ci/deps/travis-36.yaml +++ b/ci/deps/travis-36.yaml @@ -38,6 +38,7 @@ dependencies: - pytest-cov - pytest-mock - hypothesis>=3.58.0 + - isort - pip: - brotlipy - coverage diff --git a/ci/deps/travis-37.yaml b/ci/deps/travis-37.yaml index 125750191de7d..a8a5df5894ba5 100644 --- a/ci/deps/travis-37.yaml +++ b/ci/deps/travis-37.yaml @@ -17,5 +17,6 @@ dependencies: - pytest-mock - hypothesis>=3.58.0 - s3fs + - isort - pip: - moto diff --git a/setup.cfg b/setup.cfg index b15c3ce8a110a..956aa23839e73 100644 --- a/setup.cfg +++ b/setup.cfg @@ -152,3 +152,23 @@ skip= asv_bench/benchmarks/dtypes.py asv_bench/benchmarks/strings.py asv_bench/benchmarks/period.py + pandas/__init__.py + pandas/plotting/__init__.py + pandas/tests/extension/decimal/__init__.py + pandas/tests/extension/base/__init__.py + pandas/io/msgpack/__init__.py + pandas/io/json/__init__.py + pandas/io/clipboard/__init__.py + pandas/io/excel/__init__.py + pandas/compat/__init__.py + pandas/compat/numpy/__init__.py + pandas/core/arrays/__init__.py + pandas/core/groupby/__init__.py + pandas/core/internals/__init__.py + pandas/api/__init__.py + pandas/api/extensions/__init__.py + pandas/api/types/__init__.py + pandas/_libs/__init__.py + pandas/_libs/tslibs/__init__.py + pandas/util/__init__.py + pandas/arrays/__init__.py From c9863865c217867583e8f6592ba88d9200601992 Mon Sep 17 00:00:00 2001 From: topper-123 Date: Thu, 28 Feb 2019 00:30:33 +0000 Subject: [PATCH 157/215] DOC: CategoricalIndex doc string (#24852) --- pandas/core/indexes/category.py | 75 +++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index c6d31339f950d..b494c41c3b58c 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -42,20 +42,35 @@ typ='method', overwrite=True) class CategoricalIndex(Index, accessor.PandasDelegate): """ - Immutable Index implementing an ordered, sliceable set. CategoricalIndex - represents a sparsely populated Index with an underlying Categorical. + Index based on an underlying :class:`Categorical`. + + CategoricalIndex, like Categorical, can only take on a limited, + and usually fixed, number of possible values (`categories`). Also, + like Categorical, it might have an order, but numerical operations + (additions, divisions, ...) are not possible. Parameters ---------- - data : array-like or Categorical, (1-dimensional) - categories : optional, array-like - categories for the CategoricalIndex - ordered : boolean, - designating if the categories are ordered - copy : bool - Make a copy of input ndarray - name : object - Name to be stored in the index + data : array-like (1-dimensional) + The values of the categorical. If `categories` are given, values not in + `categories` will be replaced with NaN. + categories : index-like, optional + The categories for the categorical. Items need to be unique. + If the categories are not given here (and also not in `dtype`), they + will be inferred from the `data`. + ordered : bool, optional + Whether or not this categorical is treated as an ordered + categorical. If not given here or in `dtype`, the resulting + categorical will be unordered. + dtype : CategoricalDtype or the string "category", optional + If :class:`CategoricalDtype`, cannot be used together with + `categories` or `ordered`. + + .. versionadded:: 0.21.0 + copy : bool, default False + Make a copy of input ndarray. + name : object, optional + Name to be stored in the index. Attributes ---------- @@ -75,9 +90,45 @@ class CategoricalIndex(Index, accessor.PandasDelegate): as_unordered map + Raises + ------ + ValueError + If the categories do not validate. + TypeError + If an explicit ``ordered=True`` is given but no `categories` and the + `values` are not sortable. + See Also -------- - Categorical, Index + Index : The base pandas Index type. + Categorical : A categorical array. + CategoricalDtype : Type for categorical data. + + Notes + ----- + See the `user guide + `_ + for more. + + Examples + -------- + >>> pd.CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c']) + CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['a', 'b', 'c'], ordered=False, dtype='category') # noqa + + ``CategoricalIndex`` can also be instantiated from a ``Categorical``: + + >>> c = pd.Categorical(['a', 'b', 'c', 'a', 'b', 'c']) + >>> pd.CategoricalIndex(c) + CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['a', 'b', 'c'], ordered=False, dtype='category') # noqa + + Ordered ``CategoricalIndex`` can have a min and max value. + + >>> ci = pd.CategoricalIndex(['a','b','c','a','b','c'], ordered=True, + ... categories=['c', 'b', 'a']) + >>> ci + CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['c', 'b', 'a'], ordered=True, dtype='category') # noqa + >>> ci.min() + 'c' """ _typ = 'categoricalindex' From 70802c2a4bdd5e377b36b5dff0203cf844460fd6 Mon Sep 17 00:00:00 2001 From: Max van Deursen Date: Thu, 28 Feb 2019 13:41:07 +0100 Subject: [PATCH 158/215] DataFrame.drop Raises KeyError definition (#25474) --- pandas/core/frame.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 608e5c53ec094..a40733b7076b0 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -3797,7 +3797,12 @@ def drop(self, labels=None, axis=0, index=None, columns=None, axis : {0 or 'index', 1 or 'columns'}, default 0 Whether to drop labels from the index (0 or 'index') or columns (1 or 'columns'). - index, columns : single label or list-like + index : single label or list-like + Alternative to specifying axis (``labels, axis=0`` + is equivalent to ``index=labels``). + + .. versionadded:: 0.21.0 + columns : single label or list-like Alternative to specifying axis (``labels, axis=1`` is equivalent to ``columns=labels``). @@ -3813,11 +3818,12 @@ def drop(self, labels=None, axis=0, index=None, columns=None, Returns ------- DataFrame + DataFrame without the removed index or column labels. Raises ------ KeyError - If none of the labels are found in the selected axis + If any of the labels is not found in the selected axis. See Also -------- @@ -3830,7 +3836,7 @@ def drop(self, labels=None, axis=0, index=None, columns=None, Examples -------- - >>> df = pd.DataFrame(np.arange(12).reshape(3,4), + >>> df = pd.DataFrame(np.arange(12).reshape(3, 4), ... columns=['A', 'B', 'C', 'D']) >>> df A B C D @@ -3867,7 +3873,7 @@ def drop(self, labels=None, axis=0, index=None, columns=None, >>> df = pd.DataFrame(index=midx, columns=['big', 'small'], ... data=[[45, 30], [200, 100], [1.5, 1], [30, 20], ... [250, 150], [1.5, 0.8], [320, 250], - ... [1, 0.8], [0.3,0.2]]) + ... [1, 0.8], [0.3, 0.2]]) >>> df big small lama speed 45.0 30.0 From 3b570e33e2047e8261b3002955dcddd578f4c251 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Thu, 28 Feb 2019 07:42:54 -0500 Subject: [PATCH 159/215] BUG: Keep column level name in resample nunique (#25469) Closes gh-23222 xref gh-23645 --- doc/source/reference/groupby.rst | 1 + doc/source/whatsnew/v0.25.0.rst | 1 + pandas/core/groupby/generic.py | 1 + pandas/tests/groupby/test_function.py | 9 +++++++++ pandas/tests/resample/test_datetime_index.py | 9 +++++++++ 5 files changed, 21 insertions(+) diff --git a/doc/source/reference/groupby.rst b/doc/source/reference/groupby.rst index 6ed85ff2fac43..c7f9113b53c22 100644 --- a/doc/source/reference/groupby.rst +++ b/doc/source/reference/groupby.rst @@ -99,6 +99,7 @@ application to columns of a specific data type. DataFrameGroupBy.idxmax DataFrameGroupBy.idxmin DataFrameGroupBy.mad + DataFrameGroupBy.nunique DataFrameGroupBy.pct_change DataFrameGroupBy.plot DataFrameGroupBy.quantile diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 170e7f14da397..ee16246a1421d 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -210,6 +210,7 @@ Groupby/Resample/Rolling ^^^^^^^^^^^^^^^^^^^^^^^^ - Bug in :meth:`pandas.core.resample.Resampler.agg` with a timezone aware index where ``OverflowError`` would raise when passing a list of functions (:issue:`22660`) +- Bug in :meth:`pandas.core.groupby.DataFrameGroupBy.nunique` in which the names of column levels were lost (:issue:`23222`) - - diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py index 52056a6842ed9..683c21f7bd47a 100644 --- a/pandas/core/groupby/generic.py +++ b/pandas/core/groupby/generic.py @@ -1579,6 +1579,7 @@ def groupby_series(obj, col=None): from pandas.core.reshape.concat import concat results = [groupby_series(obj[col], col) for col in obj.columns] results = concat(results, axis=1) + results.columns.names = obj.columns.names if not self.as_index: results.index = ibase.default_index(len(results)) diff --git a/pandas/tests/groupby/test_function.py b/pandas/tests/groupby/test_function.py index a884a37840f8a..1788b29a11082 100644 --- a/pandas/tests/groupby/test_function.py +++ b/pandas/tests/groupby/test_function.py @@ -897,6 +897,15 @@ def test_nunique_with_timegrouper(): tm.assert_series_equal(result, expected) +def test_nunique_preserves_column_level_names(): + # GH 23222 + test = pd.DataFrame([1, 2, 2], + columns=pd.Index(['A'], name="level_0")) + result = test.groupby([0, 0, 0]).nunique() + expected = pd.DataFrame([2], columns=test.columns) + tm.assert_frame_equal(result, expected) + + # count # -------------------------------- diff --git a/pandas/tests/resample/test_datetime_index.py b/pandas/tests/resample/test_datetime_index.py index 71b100401ec21..ce675893d9907 100644 --- a/pandas/tests/resample/test_datetime_index.py +++ b/pandas/tests/resample/test_datetime_index.py @@ -1135,6 +1135,15 @@ def test_resample_nunique(): assert_series_equal(result, expected) +def test_resample_nunique_preserves_column_level_names(): + # see gh-23222 + df = tm.makeTimeDataFrame(freq="1D").abs() + df.columns = pd.MultiIndex.from_arrays([df.columns.tolist()] * 2, + names=["lev0", "lev1"]) + result = df.resample("1h").nunique() + tm.assert_index_equal(df.columns, result.columns) + + def test_resample_nunique_with_date_gap(): # GH 13453 index = pd.date_range('1-1-2000', '2-15-2000', freq='h') From 84875c1e35014179213bf2556bb712337938c3e5 Mon Sep 17 00:00:00 2001 From: gfyoung Date: Thu, 28 Feb 2019 07:44:43 -0500 Subject: [PATCH 160/215] ERR: Correct error message in to_datetime (#25467) * ERR: Correct error message in to_datetime Closes gh-23830 xref gh-23969 --- doc/source/whatsnew/v0.25.0.rst | 3 ++- pandas/_libs/tslib.pyx | 8 +++++--- pandas/tests/indexes/datetimes/test_tools.py | 9 +++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index ee16246a1421d..496a7c91f3ce9 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -104,7 +104,8 @@ Performance Improvements Bug Fixes ~~~~~~~~~ - +- Bug in :func:`to_datetime` which would raise an (incorrect) ``ValueError`` when called with a date far into the future and the ``format`` argument specified instead of raising ``OutOfBoundsDatetime`` (:issue:`23830`) +- - Categorical diff --git a/pandas/_libs/tslib.pyx b/pandas/_libs/tslib.pyx index f932e236b5218..624872c1c56c6 100644 --- a/pandas/_libs/tslib.pyx +++ b/pandas/_libs/tslib.pyx @@ -670,9 +670,11 @@ cpdef array_to_datetime(ndarray[object] values, str errors='raise', # dateutil parser will return incorrect result because # it will ignore nanoseconds if is_raise: - raise ValueError("time data {val} doesn't " - "match format specified" - .format(val=val)) + + # Still raise OutOfBoundsDatetime, + # as error message is informative. + raise + assert is_ignore return values, tz_out raise diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index b94935d2521eb..dd914d8a79837 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -1868,6 +1868,15 @@ def test_invalid_origins_tzinfo(self): pd.to_datetime(1, unit='D', origin=datetime(2000, 1, 1, tzinfo=pytz.utc)) + @pytest.mark.parametrize("format", [ + None, "%Y-%m-%d %H:%M:%S" + ]) + def test_to_datetime_out_of_bounds_with_format_arg(self, format): + # see gh-23830 + msg = "Out of bounds nanosecond timestamp" + with pytest.raises(OutOfBoundsDatetime, match=msg): + to_datetime("2417-10-27 00:00:00", format=format) + def test_processing_order(self): # make sure we handle out-of-bounds *before* # constructing the dates From ece6074694daa787237c5b221e0eb3254cbf62be Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Thu, 28 Feb 2019 13:45:15 +0100 Subject: [PATCH 161/215] Fix minor typo (#25458) Signed-off-by: Philippe Ombredanne --- pandas/core/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index eb84a9a5810f4..523543ada235c 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1815,7 +1815,7 @@ def __hash__(self): ' hashed'.format(self.__class__.__name__)) def __iter__(self): - """Iterate over infor axis""" + """Iterate over info axis""" return iter(self._info_axis) # can we get a better explanation of this? From 778affc64bca3cb6c298c095d5562df7d1e75276 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Thu, 28 Feb 2019 12:59:32 +0000 Subject: [PATCH 162/215] CI: Set pytest minversion to 4.0.2 (#25402) * CI: Set pytest minversion to 4.0.2 --- ci/deps/azure-27-compat.yaml | 2 +- ci/deps/azure-27-locale.yaml | 2 +- ci/deps/azure-36-locale_slow.yaml | 2 +- ci/deps/azure-37-locale.yaml | 2 +- ci/deps/azure-37-numpydev.yaml | 2 +- ci/deps/azure-macos-35.yaml | 8 ++++---- ci/deps/azure-windows-27.yaml | 2 +- ci/deps/azure-windows-36.yaml | 2 +- ci/deps/travis-27.yaml | 2 +- ci/deps/travis-36-doc.yaml | 2 +- ci/deps/travis-36-locale.yaml | 2 +- ci/deps/travis-36-slow.yaml | 2 +- ci/deps/travis-36.yaml | 2 +- ci/deps/travis-37.yaml | 2 +- doc/source/development/contributing.rst | 2 +- doc/source/install.rst | 2 +- doc/source/whatsnew/v0.25.0.rst | 14 ++++++++++++++ environment.yml | 2 +- pandas/util/_tester.py | 2 +- requirements-dev.txt | 2 +- setup.cfg | 1 + 21 files changed, 37 insertions(+), 22 deletions(-) diff --git a/ci/deps/azure-27-compat.yaml b/ci/deps/azure-27-compat.yaml index c68b51fbd6644..a7784f17d1956 100644 --- a/ci/deps/azure-27-compat.yaml +++ b/ci/deps/azure-27-compat.yaml @@ -18,7 +18,7 @@ dependencies: - xlsxwriter=0.5.2 - xlwt=0.7.5 # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - isort diff --git a/ci/deps/azure-27-locale.yaml b/ci/deps/azure-27-locale.yaml index 5679c503caddc..8636a63d02fed 100644 --- a/ci/deps/azure-27-locale.yaml +++ b/ci/deps/azure-27-locale.yaml @@ -20,7 +20,7 @@ dependencies: - xlsxwriter=0.5.2 - xlwt=0.7.5 # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - hypothesis>=3.58.0 diff --git a/ci/deps/azure-36-locale_slow.yaml b/ci/deps/azure-36-locale_slow.yaml index de1f4ad0e9a76..3f788e5ddcf39 100644 --- a/ci/deps/azure-36-locale_slow.yaml +++ b/ci/deps/azure-36-locale_slow.yaml @@ -26,7 +26,7 @@ dependencies: - xlsxwriter - xlwt # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - moto diff --git a/ci/deps/azure-37-locale.yaml b/ci/deps/azure-37-locale.yaml index a89e63a2b7d3a..9d598cddce91a 100644 --- a/ci/deps/azure-37-locale.yaml +++ b/ci/deps/azure-37-locale.yaml @@ -25,7 +25,7 @@ dependencies: - xlsxwriter - xlwt # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - isort diff --git a/ci/deps/azure-37-numpydev.yaml b/ci/deps/azure-37-numpydev.yaml index 3132de891299c..e58c1f599279c 100644 --- a/ci/deps/azure-37-numpydev.yaml +++ b/ci/deps/azure-37-numpydev.yaml @@ -6,7 +6,7 @@ dependencies: - pytz - Cython>=0.28.2 # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - hypothesis>=3.58.0 diff --git a/ci/deps/azure-macos-35.yaml b/ci/deps/azure-macos-35.yaml index 9710bcb5bf43d..2326e8092cc85 100644 --- a/ci/deps/azure-macos-35.yaml +++ b/ci/deps/azure-macos-35.yaml @@ -21,11 +21,11 @@ dependencies: - xlrd - xlsxwriter - xlwt - # universal - - pytest - - pytest-xdist - - pytest-mock - isort - pip: - python-dateutil==2.5.3 + # universal + - pytest>=4.0.2 + - pytest-xdist + - pytest-mock - hypothesis>=3.58.0 diff --git a/ci/deps/azure-windows-27.yaml b/ci/deps/azure-windows-27.yaml index 093c055e69553..f40efdfca3cbd 100644 --- a/ci/deps/azure-windows-27.yaml +++ b/ci/deps/azure-windows-27.yaml @@ -25,7 +25,7 @@ dependencies: - xlwt # universal - cython>=0.28.2 - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - moto diff --git a/ci/deps/azure-windows-36.yaml b/ci/deps/azure-windows-36.yaml index e9db271a75d9d..8517d340f2ba8 100644 --- a/ci/deps/azure-windows-36.yaml +++ b/ci/deps/azure-windows-36.yaml @@ -23,7 +23,7 @@ dependencies: - xlwt # universal - cython>=0.28.2 - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - hypothesis>=3.58.0 diff --git a/ci/deps/travis-27.yaml b/ci/deps/travis-27.yaml index 71b224b2c68c2..a910af36a6b10 100644 --- a/ci/deps/travis-27.yaml +++ b/ci/deps/travis-27.yaml @@ -39,7 +39,7 @@ dependencies: - xlsxwriter=0.5.2 - xlwt=0.7.5 # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - moto==1.3.4 diff --git a/ci/deps/travis-36-doc.yaml b/ci/deps/travis-36-doc.yaml index 1a65d292ef085..6f33bc58a8b21 100644 --- a/ci/deps/travis-36-doc.yaml +++ b/ci/deps/travis-36-doc.yaml @@ -41,6 +41,6 @@ dependencies: - xlsxwriter - xlwt # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - isort diff --git a/ci/deps/travis-36-locale.yaml b/ci/deps/travis-36-locale.yaml index 36dbb8013104a..34b289e6c0c2f 100644 --- a/ci/deps/travis-36-locale.yaml +++ b/ci/deps/travis-36-locale.yaml @@ -28,7 +28,7 @@ dependencies: - xlsxwriter - xlwt # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - moto diff --git a/ci/deps/travis-36-slow.yaml b/ci/deps/travis-36-slow.yaml index f4b9091c4300b..46875d59411d9 100644 --- a/ci/deps/travis-36-slow.yaml +++ b/ci/deps/travis-36-slow.yaml @@ -25,7 +25,7 @@ dependencies: - xlsxwriter - xlwt # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - moto diff --git a/ci/deps/travis-36.yaml b/ci/deps/travis-36.yaml index e22529784b5ec..06fc0d76a3d16 100644 --- a/ci/deps/travis-36.yaml +++ b/ci/deps/travis-36.yaml @@ -33,7 +33,7 @@ dependencies: - xlsxwriter - xlwt # universal - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-cov - pytest-mock diff --git a/ci/deps/travis-37.yaml b/ci/deps/travis-37.yaml index a8a5df5894ba5..f71d29fe13378 100644 --- a/ci/deps/travis-37.yaml +++ b/ci/deps/travis-37.yaml @@ -12,7 +12,7 @@ dependencies: - nomkl - pyarrow - pytz - - pytest + - pytest>=4.0.2 - pytest-xdist - pytest-mock - hypothesis>=3.58.0 diff --git a/doc/source/development/contributing.rst b/doc/source/development/contributing.rst index 511936467641e..1270bfec098e8 100644 --- a/doc/source/development/contributing.rst +++ b/doc/source/development/contributing.rst @@ -731,7 +731,7 @@ extensions in `numpy.testing .. note:: - The earliest supported pytest version is 3.6.0. + The earliest supported pytest version is 4.0.2. Writing tests ~~~~~~~~~~~~~ diff --git a/doc/source/install.rst b/doc/source/install.rst index 92364fcc9ebd2..5310667c403e5 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -202,7 +202,7 @@ pandas is equipped with an exhaustive set of unit tests, covering about 97% of the code base as of this writing. To run it on your machine to verify that everything is working (and that you have all of the dependencies, soft and hard, installed), make sure you have `pytest -`__ >= 3.6 and `Hypothesis +`__ >= 4.0.2 and `Hypothesis `__ >= 3.58, then run: :: diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 496a7c91f3ce9..d1fffbc9e2225 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -63,6 +63,20 @@ is respected in indexing. (:issue:`24076`, :issue:`16785`) df = pd.DataFrame([0], index=pd.DatetimeIndex(['2019-01-01'], tz='US/Pacific')) df['2019-01-01 12:00:00+04:00':'2019-01-01 13:00:00+04:00'] +.. _whatsnew_0250.api_breaking.deps: + +Increased minimum versions for dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We have updated our minimum supported versions of dependencies (:issue:`23519`). +If installed, we now require: + ++-----------------+-----------------+----------+ +| Package | Minimum Version | Required | ++=================+=================+==========+ +| pytest (dev) | 4.0.2 | | ++-----------------+-----------------+----------+ + .. _whatsnew_0250.api.other: Other API Changes diff --git a/environment.yml b/environment.yml index ce68dccca0c07..c1669c9f49017 100644 --- a/environment.yml +++ b/environment.yml @@ -19,7 +19,7 @@ dependencies: - hypothesis>=3.82 - isort - moto - - pytest>=4.0 + - pytest>=4.0.2 - pytest-mock - sphinx - numpydoc diff --git a/pandas/util/_tester.py b/pandas/util/_tester.py index 18e8d415459fd..19b1cc700261c 100644 --- a/pandas/util/_tester.py +++ b/pandas/util/_tester.py @@ -11,7 +11,7 @@ def test(extra_args=None): try: import pytest except ImportError: - raise ImportError("Need pytest>=3.0 to run tests") + raise ImportError("Need pytest>=4.0.2 to run tests") try: import hypothesis # noqa except ImportError: diff --git a/requirements-dev.txt b/requirements-dev.txt index 22c01ebcef7f0..be84c6f29fdeb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,7 +10,7 @@ gitpython hypothesis>=3.82 isort moto -pytest>=4.0 +pytest>=4.0.2 pytest-mock sphinx numpydoc diff --git a/setup.cfg b/setup.cfg index 956aa23839e73..84b8f69a83f16 100644 --- a/setup.cfg +++ b/setup.cfg @@ -57,6 +57,7 @@ split_penalty_after_opening_bracket = 1000000 split_penalty_logical_operator = 30 [tool:pytest] +minversion = 4.0.2 testpaths = pandas markers = single: mark a test as single cpu only From 72367b7d2eed54e5063c17360a1506b853f9df30 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Thu, 28 Feb 2019 13:33:31 +0000 Subject: [PATCH 163/215] STY: use pytest.raises context manager (indexes) (#25447) --- .../tests/indexes/interval/test_interval.py | 38 ++++++--- pandas/tests/indexes/test_base.py | 47 ++++++----- pandas/tests/indexes/test_category.py | 77 +++++++++++-------- pandas/tests/indexes/test_common.py | 20 ++++- pandas/tests/indexes/test_numeric.py | 71 ++++++++++++----- .../indexes/timedeltas/test_arithmetic.py | 26 +++++-- .../indexes/timedeltas/test_construction.py | 11 ++- pandas/tests/indexes/timedeltas/test_ops.py | 4 +- .../timedeltas/test_partial_slicing.py | 4 +- .../indexes/timedeltas/test_timedelta.py | 10 ++- pandas/tests/indexes/timedeltas/test_tools.py | 16 +++- 11 files changed, 222 insertions(+), 102 deletions(-) diff --git a/pandas/tests/indexes/interval/test_interval.py b/pandas/tests/indexes/interval/test_interval.py index e4f25ff143273..ba451da10573a 100644 --- a/pandas/tests/indexes/interval/test_interval.py +++ b/pandas/tests/indexes/interval/test_interval.py @@ -403,13 +403,16 @@ def test_get_item(self, closed): # To be removed, replaced by test_interval_new.py (see #16316, #16386) def test_get_loc_value(self): - pytest.raises(KeyError, self.index.get_loc, 0) + with pytest.raises(KeyError, match="^0$"): + self.index.get_loc(0) assert self.index.get_loc(0.5) == 0 assert self.index.get_loc(1) == 0 assert self.index.get_loc(1.5) == 1 assert self.index.get_loc(2) == 1 - pytest.raises(KeyError, self.index.get_loc, -1) - pytest.raises(KeyError, self.index.get_loc, 3) + with pytest.raises(KeyError, match="^-1$"): + self.index.get_loc(-1) + with pytest.raises(KeyError, match="^3$"): + self.index.get_loc(3) idx = IntervalIndex.from_tuples([(0, 2), (1, 3)]) assert idx.get_loc(0.5) == 0 @@ -419,10 +422,12 @@ def test_get_loc_value(self): tm.assert_numpy_array_equal(np.sort(idx.get_loc(2)), np.array([0, 1], dtype='intp')) assert idx.get_loc(3) == 1 - pytest.raises(KeyError, idx.get_loc, 3.5) + with pytest.raises(KeyError, match=r"^3\.5$"): + idx.get_loc(3.5) idx = IntervalIndex.from_arrays([0, 2], [1, 3]) - pytest.raises(KeyError, idx.get_loc, 1.5) + with pytest.raises(KeyError, match=r"^1\.5$"): + idx.get_loc(1.5) # To be removed, replaced by test_interval_new.py (see #16316, #16386) def slice_locs_cases(self, breaks): @@ -486,7 +491,9 @@ def test_slice_locs_decreasing_float64(self): # To be removed, replaced by test_interval_new.py (see #16316, #16386) def test_slice_locs_fails(self): index = IntervalIndex.from_tuples([(1, 2), (0, 1), (2, 3)]) - with pytest.raises(KeyError): + msg = ("'can only get slices from an IntervalIndex if bounds are" + " non-overlapping and all monotonic increasing or decreasing'") + with pytest.raises(KeyError, match=msg): index.slice_locs(1, 2) # To be removed, replaced by test_interval_new.py (see #16316, #16386) @@ -494,9 +501,12 @@ def test_get_loc_interval(self): assert self.index.get_loc(Interval(0, 1)) == 0 assert self.index.get_loc(Interval(0, 0.5)) == 0 assert self.index.get_loc(Interval(0, 1, 'left')) == 0 - pytest.raises(KeyError, self.index.get_loc, Interval(2, 3)) - pytest.raises(KeyError, self.index.get_loc, - Interval(-1, 0, 'left')) + msg = r"Interval\(2, 3, closed='right'\)" + with pytest.raises(KeyError, match=msg): + self.index.get_loc(Interval(2, 3)) + msg = r"Interval\(-1, 0, closed='left'\)" + with pytest.raises(KeyError, match=msg): + self.index.get_loc(Interval(-1, 0, 'left')) # Make consistent with test_interval_new.py (see #16316, #16386) @pytest.mark.parametrize('item', [3, Interval(1, 4)]) @@ -981,9 +991,11 @@ def test_comparison(self): self.index > 0 with pytest.raises(TypeError, match='unorderable types'): self.index <= 0 - with pytest.raises(TypeError): + msg = r"unorderable types: Interval\(\) > int\(\)" + with pytest.raises(TypeError, match=msg): self.index > np.arange(2) - with pytest.raises(ValueError): + msg = "Lengths must match to compare" + with pytest.raises(ValueError, match=msg): self.index > np.arange(3) def test_missing_values(self, closed): @@ -993,7 +1005,9 @@ def test_missing_values(self, closed): [np.nan, 0, 1], [np.nan, 1, 2], closed=closed) assert idx.equals(idx2) - with pytest.raises(ValueError): + msg = ("missing values must be missing in the same location both left" + " and right sides") + with pytest.raises(ValueError, match=msg): IntervalIndex.from_arrays( [np.nan, 0, 1], np.array([0, 1, 2]), closed=closed) diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 8415bab802239..26dcf7d6bc234 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta import math import operator +import re import sys import numpy as np @@ -107,7 +108,10 @@ def test_constructor_copy(self): def test_constructor_corner(self): # corner case - pytest.raises(TypeError, Index, 0) + msg = (r"Index\(\.\.\.\) must be called with a collection of some" + " kind, 0 was passed") + with pytest.raises(TypeError, match=msg): + Index(0) @pytest.mark.parametrize("index_vals", [ [('A', 1), 'B'], ['B', ('A', 1)]]) @@ -488,21 +492,22 @@ def test_constructor_cast(self): Index(["a", "b", "c"], dtype=float) def test_view_with_args(self): - restricted = ['unicodeIndex', 'strIndex', 'catIndex', 'boolIndex', 'empty'] - - for i in restricted: - ind = self.indices[i] - - # with arguments - pytest.raises(TypeError, lambda: ind.view('i8')) - - # these are ok for i in list(set(self.indices.keys()) - set(restricted)): ind = self.indices[i] + ind.view('i8') - # with arguments + @pytest.mark.parametrize('index_type', [ + 'unicodeIndex', + 'strIndex', + pytest.param('catIndex', marks=pytest.mark.xfail(reason="gh-25464")), + 'boolIndex', + 'empty']) + def test_view_with_args_object_array_raises(self, index_type): + ind = self.indices[index_type] + msg = "Cannot change data-type for object array" + with pytest.raises(TypeError, match=msg): ind.view('i8') def test_astype(self): @@ -565,8 +570,8 @@ def test_delete(self, pos, expected): def test_delete_raises(self): index = Index(['a', 'b', 'c', 'd'], name='index') - with pytest.raises((IndexError, ValueError)): - # either depending on numpy version + msg = "index 5 is out of bounds for axis 0 with size 4" + with pytest.raises(IndexError, match=msg): index.delete(5) def test_identical(self): @@ -683,7 +688,9 @@ def test_empty_fancy_raises(self, attr): assert index[[]].identical(empty_index) # np.ndarray only accepts ndarray of int & bool dtypes, so should Index - pytest.raises(IndexError, index.__getitem__, empty_farr) + msg = r"arrays used as indices must be of integer \(or boolean\) type" + with pytest.raises(IndexError, match=msg): + index[empty_farr] @pytest.mark.parametrize("sort", [None, False]) def test_intersection(self, sort): @@ -1426,13 +1433,14 @@ def test_get_indexer_strings(self, method, expected): def test_get_indexer_strings_raises(self): index = pd.Index(['b', 'c']) - with pytest.raises(TypeError): + msg = r"unsupported operand type\(s\) for -: 'str' and 'str'" + with pytest.raises(TypeError, match=msg): index.get_indexer(['a', 'b', 'c', 'd'], method='nearest') - with pytest.raises(TypeError): + with pytest.raises(TypeError, match=msg): index.get_indexer(['a', 'b', 'c', 'd'], method='pad', tolerance=2) - with pytest.raises(TypeError): + with pytest.raises(TypeError, match=msg): index.get_indexer(['a', 'b', 'c', 'd'], method='pad', tolerance=[2, 2, 2, 2]) @@ -1685,8 +1693,11 @@ def test_drop_tuple(self, values, to_drop): tm.assert_index_equal(result, expected) removed = index.drop(to_drop[1]) + msg = r"\"\[{}\] not found in axis\"".format( + re.escape(to_drop[1].__repr__())) for drop_me in to_drop[1], [to_drop[1]]: - pytest.raises(KeyError, removed.drop, drop_me) + with pytest.raises(KeyError, match=msg): + removed.drop(drop_me) @pytest.mark.parametrize("method,expected,sort", [ ('intersection', np.array([(1, 'A'), (2, 'A'), (1, 'B'), (2, 'B')], diff --git a/pandas/tests/indexes/test_category.py b/pandas/tests/indexes/test_category.py index d889135160ae2..95fac2f6ae05b 100644 --- a/pandas/tests/indexes/test_category.py +++ b/pandas/tests/indexes/test_category.py @@ -181,18 +181,21 @@ def test_create_categorical(self): expected = Categorical(['a', 'b', 'c']) tm.assert_categorical_equal(result, expected) - def test_disallow_set_ops(self): - + @pytest.mark.parametrize('func,op_name', [ + (lambda idx: idx - idx, '__sub__'), + (lambda idx: idx + idx, '__add__'), + (lambda idx: idx - ['a', 'b'], '__sub__'), + (lambda idx: idx + ['a', 'b'], '__add__'), + (lambda idx: ['a', 'b'] - idx, '__rsub__'), + (lambda idx: ['a', 'b'] + idx, '__radd__'), + ]) + def test_disallow_set_ops(self, func, op_name): # GH 10039 # set ops (+/-) raise TypeError idx = pd.Index(pd.Categorical(['a', 'b'])) - - pytest.raises(TypeError, lambda: idx - idx) - pytest.raises(TypeError, lambda: idx + idx) - pytest.raises(TypeError, lambda: idx - ['a', 'b']) - pytest.raises(TypeError, lambda: idx + ['a', 'b']) - pytest.raises(TypeError, lambda: ['a', 'b'] - idx) - pytest.raises(TypeError, lambda: ['a', 'b'] + idx) + msg = "cannot perform {} with this index type: CategoricalIndex" + with pytest.raises(TypeError, match=msg.format(op_name)): + func(idx) def test_method_delegation(self): @@ -231,8 +234,9 @@ def test_method_delegation(self): list('aabbca'), categories=list('cabdef'), ordered=True)) # invalid - pytest.raises(ValueError, lambda: ci.set_categories( - list('cab'), inplace=True)) + msg = "cannot use inplace with CategoricalIndex" + with pytest.raises(ValueError, match=msg): + ci.set_categories(list('cab'), inplace=True) def test_contains(self): @@ -357,12 +361,11 @@ def test_append(self): tm.assert_index_equal(result, ci, exact=True) # appending with different categories or reordered is not ok - pytest.raises( - TypeError, - lambda: ci.append(ci.values.set_categories(list('abcd')))) - pytest.raises( - TypeError, - lambda: ci.append(ci.values.reorder_categories(list('abc')))) + msg = "all inputs must be Index" + with pytest.raises(TypeError, match=msg): + ci.append(ci.values.set_categories(list('abcd'))) + with pytest.raises(TypeError, match=msg): + ci.append(ci.values.reorder_categories(list('abc'))) # with objects result = ci.append(Index(['c', 'a'])) @@ -370,7 +373,9 @@ def test_append(self): tm.assert_index_equal(result, expected, exact=True) # invalid objects - pytest.raises(TypeError, lambda: ci.append(Index(['a', 'd']))) + msg = "cannot append a non-category item to a CategoricalIndex" + with pytest.raises(TypeError, match=msg): + ci.append(Index(['a', 'd'])) # GH14298 - if base object is not categorical -> coerce to object result = Index(['c', 'a']).append(ci) @@ -406,7 +411,10 @@ def test_insert(self): tm.assert_index_equal(result, expected, exact=True) # invalid - pytest.raises(TypeError, lambda: ci.insert(0, 'd')) + msg = ("cannot insert an item into a CategoricalIndex that is not" + " already an existing category") + with pytest.raises(TypeError, match=msg): + ci.insert(0, 'd') # GH 18295 (test missing) expected = CategoricalIndex(['a', np.nan, 'a', 'b', 'c', 'b']) @@ -633,12 +641,16 @@ def test_get_indexer(self): r1 = idx1.get_indexer(idx2) assert_almost_equal(r1, np.array([0, 1, 2, -1], dtype=np.intp)) - pytest.raises(NotImplementedError, - lambda: idx2.get_indexer(idx1, method='pad')) - pytest.raises(NotImplementedError, - lambda: idx2.get_indexer(idx1, method='backfill')) - pytest.raises(NotImplementedError, - lambda: idx2.get_indexer(idx1, method='nearest')) + msg = ("method='pad' and method='backfill' not implemented yet for" + " CategoricalIndex") + with pytest.raises(NotImplementedError, match=msg): + idx2.get_indexer(idx1, method='pad') + with pytest.raises(NotImplementedError, match=msg): + idx2.get_indexer(idx1, method='backfill') + + msg = "method='nearest' not implemented yet for CategoricalIndex" + with pytest.raises(NotImplementedError, match=msg): + idx2.get_indexer(idx1, method='nearest') def test_get_loc(self): # GH 12531 @@ -776,12 +788,15 @@ def test_equals_categorical(self): # invalid comparisons with pytest.raises(ValueError, match="Lengths must match"): ci1 == Index(['a', 'b', 'c']) - pytest.raises(TypeError, lambda: ci1 == ci2) - pytest.raises( - TypeError, lambda: ci1 == Categorical(ci1.values, ordered=False)) - pytest.raises( - TypeError, - lambda: ci1 == Categorical(ci1.values, categories=list('abc'))) + + msg = ("categorical index comparisons must have the same categories" + " and ordered attributes") + with pytest.raises(TypeError, match=msg): + ci1 == ci2 + with pytest.raises(TypeError, match=msg): + ci1 == Categorical(ci1.values, ordered=False) + with pytest.raises(TypeError, match=msg): + ci1 == Categorical(ci1.values, categories=list('abc')) # tests # make sure that we are testing for category inclusion properly diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py index fd356202a8ce5..03448129a48fc 100644 --- a/pandas/tests/indexes/test_common.py +++ b/pandas/tests/indexes/test_common.py @@ -3,6 +3,8 @@ any index subclass. Makes use of the `indices` fixture defined in pandas/tests/indexes/conftest.py. """ +import re + import numpy as np import pytest @@ -189,8 +191,14 @@ def test_unique(self, indices): result = indices.unique(level=level) tm.assert_index_equal(result, expected) - for level in 3, 'wrong': - pytest.raises((IndexError, KeyError), indices.unique, level=level) + msg = "Too many levels: Index has only 1 level, not 4" + with pytest.raises(IndexError, match=msg): + indices.unique(level=3) + + msg = r"Level wrong must be same as name \({}\)".format( + re.escape(indices.name.__repr__())) + with pytest.raises(KeyError, match=msg): + indices.unique(level='wrong') def test_get_unique_index(self, indices): # MultiIndex tested separately @@ -239,12 +247,16 @@ def test_get_unique_index(self, indices): tm.assert_index_equal(result, expected) def test_sort(self, indices): - pytest.raises(TypeError, indices.sort) + msg = "cannot sort an Index object in-place, use sort_values instead" + with pytest.raises(TypeError, match=msg): + indices.sort() def test_mutability(self, indices): if not len(indices): pytest.skip('Skip check for empty Index') - pytest.raises(TypeError, indices.__setitem__, 0, indices[0]) + msg = "Index does not support mutable operations" + with pytest.raises(TypeError, match=msg): + indices[0] = indices[0] def test_view(self, indices): assert indices.view().name == indices.name diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index a64340c02cd22..26413f4519eff 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -1,15 +1,17 @@ # -*- coding: utf-8 -*- from datetime import datetime +import re import numpy as np import pytest from pandas._libs.tslibs import Timestamp -from pandas.compat import range +from pandas.compat import PY2, range import pandas as pd from pandas import Float64Index, Index, Int64Index, Series, UInt64Index +from pandas.api.types import pandas_dtype from pandas.tests.indexes.common import Base import pandas.util.testing as tm @@ -153,12 +155,22 @@ def test_constructor(self): result = Index(np.array([np.nan])) assert pd.isna(result.values).all() + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_constructor_invalid(self): # invalid - pytest.raises(TypeError, Float64Index, 0.) - pytest.raises(TypeError, Float64Index, ['a', 'b', 0.]) - pytest.raises(TypeError, Float64Index, [Timestamp('20130101')]) + msg = (r"Float64Index\(\.\.\.\) must be called with a collection of" + r" some kind, 0\.0 was passed") + with pytest.raises(TypeError, match=msg): + Float64Index(0.) + msg = ("String dtype not supported, you may need to explicitly cast to" + " a numeric type") + with pytest.raises(TypeError, match=msg): + Float64Index(['a', 'b', 0.]) + msg = (r"float\(\) argument must be a string or a number, not" + " 'Timestamp'") + with pytest.raises(TypeError, match=msg): + Float64Index([Timestamp('20130101')]) def test_constructor_coerce(self): @@ -216,12 +228,17 @@ def test_astype(self): # invalid for dtype in ['M8[ns]', 'm8[ns]']: - pytest.raises(TypeError, lambda: i.astype(dtype)) + msg = ("Cannot convert Float64Index to dtype {}; integer values" + " are required for conversion").format(pandas_dtype(dtype)) + with pytest.raises(TypeError, match=re.escape(msg)): + i.astype(dtype) # GH 13149 for dtype in ['int16', 'int32', 'int64']: i = Float64Index([0, 1.1, np.NAN]) - pytest.raises(ValueError, lambda: i.astype(dtype)) + msg = "Cannot convert NA to integer" + with pytest.raises(ValueError, match=msg): + i.astype(dtype) def test_type_coercion_fail(self, any_int_dtype): # see gh-15832 @@ -275,12 +292,16 @@ def test_get_loc(self): assert idx.get_loc(1.1, method) == loc assert idx.get_loc(1.1, method, tolerance=0.9) == loc - pytest.raises(KeyError, idx.get_loc, 'foo') - pytest.raises(KeyError, idx.get_loc, 1.5) - pytest.raises(KeyError, idx.get_loc, 1.5, method='pad', - tolerance=0.1) - pytest.raises(KeyError, idx.get_loc, True) - pytest.raises(KeyError, idx.get_loc, False) + with pytest.raises(KeyError, match="^'foo'$"): + idx.get_loc('foo') + with pytest.raises(KeyError, match=r"^1\.5$"): + idx.get_loc(1.5) + with pytest.raises(KeyError, match=r"^1\.5$"): + idx.get_loc(1.5, method='pad', tolerance=0.1) + with pytest.raises(KeyError, match="^True$"): + idx.get_loc(True) + with pytest.raises(KeyError, match="^False$"): + idx.get_loc(False) with pytest.raises(ValueError, match='must be numeric'): idx.get_loc(1.4, method='nearest', tolerance='foo') @@ -310,15 +331,20 @@ def test_get_loc_na(self): # not representable by slice idx = Float64Index([np.nan, 1, np.nan, np.nan]) assert idx.get_loc(1) == 1 - pytest.raises(KeyError, idx.slice_locs, np.nan) + msg = "'Cannot get left slice bound for non-unique label: nan" + with pytest.raises(KeyError, match=msg): + idx.slice_locs(np.nan) def test_get_loc_missing_nan(self): # GH 8569 idx = Float64Index([1, 2]) assert idx.get_loc(1) == 0 - pytest.raises(KeyError, idx.get_loc, 3) - pytest.raises(KeyError, idx.get_loc, np.nan) - pytest.raises(KeyError, idx.get_loc, [np.nan]) + with pytest.raises(KeyError, match=r"^3\.0$"): + idx.get_loc(3) + with pytest.raises(KeyError, match="^nan$"): + idx.get_loc(np.nan) + with pytest.raises(KeyError, match=r"^\[nan\]$"): + idx.get_loc([np.nan]) def test_contains_nans(self): i = Float64Index([1.0, 2.0, np.nan]) @@ -499,13 +525,17 @@ def test_union_noncomparable(self): tm.assert_index_equal(result, expected) def test_cant_or_shouldnt_cast(self): + msg = ("String dtype not supported, you may need to explicitly cast to" + " a numeric type") # can't data = ['foo', 'bar', 'baz'] - pytest.raises(TypeError, self._holder, data) + with pytest.raises(TypeError, match=msg): + self._holder(data) # shouldn't data = ['0', '1', '2'] - pytest.raises(TypeError, self._holder, data) + with pytest.raises(TypeError, match=msg): + self._holder(data) def test_view_index(self): self.index.view(Index) @@ -576,7 +606,10 @@ def test_constructor(self): tm.assert_index_equal(index, expected) # scalar raise Exception - pytest.raises(TypeError, Int64Index, 5) + msg = (r"Int64Index\(\.\.\.\) must be called with a collection of some" + " kind, 5 was passed") + with pytest.raises(TypeError, match=msg): + Int64Index(5) # copy arr = self.index.values diff --git a/pandas/tests/indexes/timedeltas/test_arithmetic.py b/pandas/tests/indexes/timedeltas/test_arithmetic.py index 04977023d7c62..3173252e174ab 100644 --- a/pandas/tests/indexes/timedeltas/test_arithmetic.py +++ b/pandas/tests/indexes/timedeltas/test_arithmetic.py @@ -198,20 +198,34 @@ def test_ops_ndarray(self): expected = pd.to_timedelta(['2 days']).values tm.assert_numpy_array_equal(td + other, expected) tm.assert_numpy_array_equal(other + td, expected) - pytest.raises(TypeError, lambda: td + np.array([1])) - pytest.raises(TypeError, lambda: np.array([1]) + td) + msg = r"unsupported operand type\(s\) for \+: 'Timedelta' and 'int'" + with pytest.raises(TypeError, match=msg): + td + np.array([1]) + msg = (r"unsupported operand type\(s\) for \+: 'numpy.ndarray' and" + " 'Timedelta'") + with pytest.raises(TypeError, match=msg): + np.array([1]) + td expected = pd.to_timedelta(['0 days']).values tm.assert_numpy_array_equal(td - other, expected) tm.assert_numpy_array_equal(-other + td, expected) - pytest.raises(TypeError, lambda: td - np.array([1])) - pytest.raises(TypeError, lambda: np.array([1]) - td) + msg = r"unsupported operand type\(s\) for -: 'Timedelta' and 'int'" + with pytest.raises(TypeError, match=msg): + td - np.array([1]) + msg = (r"unsupported operand type\(s\) for -: 'numpy.ndarray' and" + " 'Timedelta'") + with pytest.raises(TypeError, match=msg): + np.array([1]) - td expected = pd.to_timedelta(['2 days']).values tm.assert_numpy_array_equal(td * np.array([2]), expected) tm.assert_numpy_array_equal(np.array([2]) * td, expected) - pytest.raises(TypeError, lambda: td * other) - pytest.raises(TypeError, lambda: other * td) + msg = ("ufunc multiply cannot use operands with types" + r" dtype\(' Date: Thu, 28 Feb 2019 13:34:21 +0000 Subject: [PATCH 164/215] STY: use pytest.raises context manager (tests/test_*) (#25452) * STY: use pytest.raises context manager (tests/test_*) * fix ci failures * skip py2 ci failure --- pandas/tests/test_algos.py | 25 +++++--- pandas/tests/test_config.py | 104 +++++++++++++++++++++----------- pandas/tests/test_multilevel.py | 10 ++- pandas/tests/test_nanops.py | 18 ++++-- pandas/tests/test_sorting.py | 12 +++- pandas/tests/test_strings.py | 50 +++++++++------ pandas/tests/test_window.py | 66 ++++++++++++-------- 7 files changed, 193 insertions(+), 92 deletions(-) diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py index cb7426ce2f7c9..c56bf944699e2 100644 --- a/pandas/tests/test_algos.py +++ b/pandas/tests/test_algos.py @@ -11,7 +11,7 @@ from pandas._libs import ( algos as libalgos, groupby as libgroupby, hashtable as ht) -from pandas.compat import lrange, range +from pandas.compat import PY2, lrange, range from pandas.compat.numpy import np_array_datetime64_compat import pandas.util._test_decorators as td @@ -224,11 +224,16 @@ def test_factorize_tuple_list(self, data, expected_label, expected_level): dtype=object) tm.assert_numpy_array_equal(result[1], expected_level_array) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_complex_sorting(self): # gh 12666 - check no segfault x17 = np.array([complex(i) for i in range(17)], dtype=object) - pytest.raises(TypeError, algos.factorize, x17[::-1], sort=True) + msg = ("'<' not supported between instances of 'complex' and" + r" 'complex'|" + r"unorderable types: complex\(\) > complex\(\)") + with pytest.raises(TypeError, match=msg): + algos.factorize(x17[::-1], sort=True) def test_float64_factorize(self, writable): data = np.array([1.0, 1e8, 1.0, 1e-8, 1e8, 1.0], dtype=np.float64) @@ -589,9 +594,14 @@ class TestIsin(object): def test_invalid(self): - pytest.raises(TypeError, lambda: algos.isin(1, 1)) - pytest.raises(TypeError, lambda: algos.isin(1, [1])) - pytest.raises(TypeError, lambda: algos.isin([1], 1)) + msg = (r"only list-like objects are allowed to be passed to isin\(\)," + r" you passed a \[int\]") + with pytest.raises(TypeError, match=msg): + algos.isin(1, 1) + with pytest.raises(TypeError, match=msg): + algos.isin(1, [1]) + with pytest.raises(TypeError, match=msg): + algos.isin([1], 1) def test_basic(self): @@ -819,8 +829,9 @@ def test_value_counts_dtypes(self): result = algos.value_counts(Series([1, 1., '1'])) # object assert len(result) == 2 - pytest.raises(TypeError, lambda s: algos.value_counts(s, bins=1), - ['1', 1]) + msg = "bins argument only works with numeric data" + with pytest.raises(TypeError, match=msg): + algos.value_counts(['1', 1], bins=1) def test_value_counts_nat(self): td = Series([np.timedelta64(10000), pd.NaT], dtype='timedelta64[ns]') diff --git a/pandas/tests/test_config.py b/pandas/tests/test_config.py index 54db3887850ea..baca66e0361ad 100644 --- a/pandas/tests/test_config.py +++ b/pandas/tests/test_config.py @@ -3,7 +3,10 @@ import pytest +from pandas.compat import PY2 + import pandas as pd +from pandas.core.config import OptionError class TestConfig(object): @@ -48,26 +51,35 @@ def test_is_one_of_factory(self): v(12) v(None) - pytest.raises(ValueError, v, 1.1) + msg = r"Value must be one of None\|12" + with pytest.raises(ValueError, match=msg): + v(1.1) def test_register_option(self): self.cf.register_option('a', 1, 'doc') # can't register an already registered option - pytest.raises(KeyError, self.cf.register_option, 'a', 1, 'doc') + msg = "Option 'a' has already been registered" + with pytest.raises(OptionError, match=msg): + self.cf.register_option('a', 1, 'doc') # can't register an already registered option - pytest.raises(KeyError, self.cf.register_option, 'a.b.c.d1', 1, - 'doc') - pytest.raises(KeyError, self.cf.register_option, 'a.b.c.d2', 1, - 'doc') + msg = "Path prefix to option 'a' is already an option" + with pytest.raises(OptionError, match=msg): + self.cf.register_option('a.b.c.d1', 1, 'doc') + with pytest.raises(OptionError, match=msg): + self.cf.register_option('a.b.c.d2', 1, 'doc') # no python keywords - pytest.raises(ValueError, self.cf.register_option, 'for', 0) - pytest.raises(ValueError, self.cf.register_option, 'a.for.b', 0) + msg = "for is a python keyword" + with pytest.raises(ValueError, match=msg): + self.cf.register_option('for', 0) + with pytest.raises(ValueError, match=msg): + self.cf.register_option('a.for.b', 0) # must be valid identifier (ensure attribute access works) - pytest.raises(ValueError, self.cf.register_option, - 'Oh my Goddess!', 0) + msg = "oh my goddess! is not a valid identifier" + with pytest.raises(ValueError, match=msg): + self.cf.register_option('Oh my Goddess!', 0) # we can register options several levels deep # without predefining the intermediate steps @@ -90,7 +102,9 @@ def test_describe_option(self): self.cf.register_option('l', "foo") # non-existent keys raise KeyError - pytest.raises(KeyError, self.cf.describe_option, 'no.such.key') + msg = r"No such keys\(s\)" + with pytest.raises(OptionError, match=msg): + self.cf.describe_option('no.such.key') # we can get the description for any key we registered assert 'doc' in self.cf.describe_option('a', _print_desc=False) @@ -122,7 +136,9 @@ def test_case_insensitive(self): assert self.cf.get_option('kAnBaN') == 2 # gets of non-existent keys fail - pytest.raises(KeyError, self.cf.get_option, 'no_such_option') + msg = r"No such keys\(s\): 'no_such_option'" + with pytest.raises(OptionError, match=msg): + self.cf.get_option('no_such_option') self.cf.deprecate_option('KanBan') assert self.cf._is_deprecated('kAnBaN') @@ -138,7 +154,9 @@ def test_get_option(self): assert self.cf.get_option('b.b') is None # gets of non-existent keys fail - pytest.raises(KeyError, self.cf.get_option, 'no_such_option') + msg = r"No such keys\(s\): 'no_such_option'" + with pytest.raises(OptionError, match=msg): + self.cf.get_option('no_such_option') def test_set_option(self): self.cf.register_option('a', 1, 'doc') @@ -157,16 +175,24 @@ def test_set_option(self): assert self.cf.get_option('b.c') == 'wurld' assert self.cf.get_option('b.b') == 1.1 - pytest.raises(KeyError, self.cf.set_option, 'no.such.key', None) + msg = r"No such keys\(s\): 'no.such.key'" + with pytest.raises(OptionError, match=msg): + self.cf.set_option('no.such.key', None) def test_set_option_empty_args(self): - pytest.raises(ValueError, self.cf.set_option) + msg = "Must provide an even number of non-keyword arguments" + with pytest.raises(ValueError, match=msg): + self.cf.set_option() def test_set_option_uneven_args(self): - pytest.raises(ValueError, self.cf.set_option, 'a.b', 2, 'b.c') + msg = "Must provide an even number of non-keyword arguments" + with pytest.raises(ValueError, match=msg): + self.cf.set_option('a.b', 2, 'b.c') def test_set_option_invalid_single_argument_type(self): - pytest.raises(ValueError, self.cf.set_option, 2) + msg = "Must provide an even number of non-keyword arguments" + with pytest.raises(ValueError, match=msg): + self.cf.set_option(2) def test_set_option_multiple(self): self.cf.register_option('a', 1, 'doc') @@ -183,27 +209,36 @@ def test_set_option_multiple(self): assert self.cf.get_option('b.c') is None assert self.cf.get_option('b.b') == 10.0 + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_validation(self): self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int) self.cf.register_option('b.c', 'hullo', 'doc2', validator=self.cf.is_text) - pytest.raises(ValueError, self.cf.register_option, 'a.b.c.d2', - 'NO', 'doc', validator=self.cf.is_int) + msg = "Value must have type ''" + with pytest.raises(ValueError, match=msg): + self.cf.register_option( + 'a.b.c.d2', 'NO', 'doc', validator=self.cf.is_int) self.cf.set_option('a', 2) # int is_int self.cf.set_option('b.c', 'wurld') # str is_str - pytest.raises( - ValueError, self.cf.set_option, 'a', None) # None not is_int - pytest.raises(ValueError, self.cf.set_option, 'a', 'ab') - pytest.raises(ValueError, self.cf.set_option, 'b.c', 1) + # None not is_int + with pytest.raises(ValueError, match=msg): + self.cf.set_option('a', None) + with pytest.raises(ValueError, match=msg): + self.cf.set_option('a', 'ab') + + msg = r"Value must be an instance of \|" + with pytest.raises(ValueError, match=msg): + self.cf.set_option('b.c', 1) validator = self.cf.is_one_of_factory([None, self.cf.is_callable]) self.cf.register_option('b', lambda: None, 'doc', validator=validator) self.cf.set_option('b', '%.1f'.format) # Formatter is callable self.cf.set_option('b', None) # Formatter is none (default) - pytest.raises(ValueError, self.cf.set_option, 'b', '%.1f') + with pytest.raises(ValueError, match="Value must be a callable"): + self.cf.set_option('b', '%.1f') def test_reset_option(self): self.cf.register_option('a', 1, 'doc', validator=self.cf.is_int) @@ -267,8 +302,9 @@ def test_deprecate_option(self): assert 'eprecated' in str(w[-1]) # we get the default message assert 'nifty_ver' in str(w[-1]) # with the removal_ver quoted - pytest.raises( - KeyError, self.cf.deprecate_option, 'a') # can't depr. twice + msg = "Option 'a' has already been defined as deprecated" + with pytest.raises(OptionError, match=msg): + self.cf.deprecate_option('a') self.cf.deprecate_option('b.c', 'zounds!') with warnings.catch_warnings(record=True) as w: @@ -374,12 +410,6 @@ def eq(val): def test_attribute_access(self): holder = [] - def f(): - options.b = 1 - - def f2(): - options.display = 1 - def f3(key): holder.append(True) @@ -397,8 +427,11 @@ def f3(key): self.cf.reset_option("a") assert options.a == self.cf.get_option("a", 0) - pytest.raises(KeyError, f) - pytest.raises(KeyError, f2) + msg = "You can only set the value of existing options" + with pytest.raises(OptionError, match=msg): + options.b = 1 + with pytest.raises(OptionError, match=msg): + options.display = 1 # make sure callback kicks when using this form of setting options.c = 1 @@ -429,5 +462,6 @@ def test_option_context_scope(self): def test_dictwrapper_getattr(self): options = self.cf.options # GH 19789 - pytest.raises(self.cf.OptionError, getattr, options, 'bananas') + with pytest.raises(OptionError, match="No such option"): + options.bananas assert not hasattr(options, 'bananas') diff --git a/pandas/tests/test_multilevel.py b/pandas/tests/test_multilevel.py index 4ea7e9b8ec9a4..a9a59c6d95373 100644 --- a/pandas/tests/test_multilevel.py +++ b/pandas/tests/test_multilevel.py @@ -886,8 +886,11 @@ def test_count(self): tm.assert_series_equal(result, expect, check_names=False) assert result.index.name == 'a' - pytest.raises(KeyError, series.count, 'x') - pytest.raises(KeyError, frame.count, level='x') + msg = "Level x not found" + with pytest.raises(KeyError, match=msg): + series.count('x') + with pytest.raises(KeyError, match=msg): + frame.count(level='x') @pytest.mark.parametrize('op', AGG_FUNCTIONS) @pytest.mark.parametrize('level', [0, 1]) @@ -1119,7 +1122,8 @@ def test_level_with_tuples(self): tm.assert_series_equal(result, expected) tm.assert_series_equal(result2, expected) - pytest.raises(KeyError, series.__getitem__, (('foo', 'bar', 0), 2)) + with pytest.raises(KeyError, match=r"^\(\('foo', 'bar', 0\), 2\)$"): + series[('foo', 'bar', 0), 2] result = frame.loc[('foo', 'bar', 0)] result2 = frame.xs(('foo', 'bar', 0)) diff --git a/pandas/tests/test_nanops.py b/pandas/tests/test_nanops.py index cf5ef6cf15eca..d1893b7efbc41 100644 --- a/pandas/tests/test_nanops.py +++ b/pandas/tests/test_nanops.py @@ -7,6 +7,7 @@ import numpy as np import pytest +from pandas.compat import PY2 from pandas.compat.numpy import _np_version_under1p13 import pandas.util._test_decorators as td @@ -728,6 +729,7 @@ def test_numeric_values(self): # Test complex assert nanops._ensure_numeric(1 + 2j) == 1 + 2j + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_ndarray(self): # Test numeric ndarray values = np.array([1, 2, 3]) @@ -743,7 +745,9 @@ def test_ndarray(self): # Test non-convertible string ndarray s_values = np.array(['foo', 'bar', 'baz'], dtype=object) - pytest.raises(ValueError, lambda: nanops._ensure_numeric(s_values)) + msg = r"could not convert string to float: '(foo|baz)'" + with pytest.raises(ValueError, match=msg): + nanops._ensure_numeric(s_values) def test_convertable_values(self): assert np.allclose(nanops._ensure_numeric('1'), 1.0) @@ -751,9 +755,15 @@ def test_convertable_values(self): assert np.allclose(nanops._ensure_numeric('1+1j'), 1 + 1j) def test_non_convertable_values(self): - pytest.raises(TypeError, lambda: nanops._ensure_numeric('foo')) - pytest.raises(TypeError, lambda: nanops._ensure_numeric({})) - pytest.raises(TypeError, lambda: nanops._ensure_numeric([])) + msg = "Could not convert foo to numeric" + with pytest.raises(TypeError, match=msg): + nanops._ensure_numeric('foo') + msg = "Could not convert {} to numeric" + with pytest.raises(TypeError, match=msg): + nanops._ensure_numeric({}) + msg = r"Could not convert \[\] to numeric" + with pytest.raises(TypeError, match=msg): + nanops._ensure_numeric([]) class TestNanvarFixedValues(object): diff --git a/pandas/tests/test_sorting.py b/pandas/tests/test_sorting.py index 7500cbb3cfc3a..e83bdb1af9121 100644 --- a/pandas/tests/test_sorting.py +++ b/pandas/tests/test_sorting.py @@ -7,6 +7,8 @@ from numpy import nan import pytest +from pandas.compat import PY2 + from pandas import DataFrame, MultiIndex, Series, compat, concat, merge from pandas.core import common as com from pandas.core.sorting import ( @@ -403,15 +405,21 @@ def test_mixed_integer_from_list(self): expected = np.array([0, 0, 1, 'a', 'b', 'b'], dtype=object) tm.assert_numpy_array_equal(result, expected) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_unsortable(self): # GH 13714 arr = np.array([1, 2, datetime.now(), 0, 3], dtype=object) + msg = ("'<' not supported between instances of 'datetime.datetime'" + r" and 'int'|" + r"unorderable types: int\(\) > datetime.datetime\(\)") if compat.PY2: # RuntimeWarning: tp_compare didn't return -1 or -2 for exception with warnings.catch_warnings(): - pytest.raises(TypeError, safe_sort, arr) + with pytest.raises(TypeError, match=msg): + safe_sort(arr) else: - pytest.raises(TypeError, safe_sort, arr) + with pytest.raises(TypeError, match=msg): + safe_sort(arr) def test_exceptions(self): with pytest.raises(TypeError, diff --git a/pandas/tests/test_strings.py b/pandas/tests/test_strings.py index 7cea3be03d1a7..bbcdc24f58f9b 100644 --- a/pandas/tests/test_strings.py +++ b/pandas/tests/test_strings.py @@ -10,7 +10,7 @@ import pytest import pandas.compat as compat -from pandas.compat import PY3, range, u +from pandas.compat import PY2, PY3, range, u from pandas import DataFrame, Index, MultiIndex, Series, concat, isna, notna import pandas.core.strings as strings @@ -1002,11 +1002,13 @@ def test_replace(self): tm.assert_series_equal(result, exp) # GH 13438 + msg = "repl must be a string or callable" for klass in (Series, Index): for repl in (None, 3, {'a': 'b'}): for data in (['a', 'b', None], ['a', 'b', 'c', 'ad']): values = klass(data) - pytest.raises(TypeError, values.str.replace, 'a', repl) + with pytest.raises(TypeError, match=msg): + values.str.replace('a', repl) def test_replace_callable(self): # GH 15055 @@ -1123,10 +1125,14 @@ def test_replace_literal(self): callable_repl = lambda m: m.group(0).swapcase() compiled_pat = re.compile('[a-z][A-Z]{2}') - pytest.raises(ValueError, values.str.replace, 'abc', callable_repl, - regex=False) - pytest.raises(ValueError, values.str.replace, compiled_pat, '', - regex=False) + msg = "Cannot use a callable replacement when regex=False" + with pytest.raises(ValueError, match=msg): + values.str.replace('abc', callable_repl, regex=False) + + msg = ("Cannot use a compiled regex as replacement pattern with" + " regex=False") + with pytest.raises(ValueError, match=msg): + values.str.replace(compiled_pat, '', regex=False) def test_repeat(self): values = Series(['a', 'b', NA, 'c', NA, 'd']) @@ -1242,12 +1248,13 @@ def test_extract_expand_False(self): for klass in [Series, Index]: # no groups s_or_idx = klass(['A1', 'B2', 'C3']) - f = lambda: s_or_idx.str.extract('[ABC][123]', expand=False) - pytest.raises(ValueError, f) + msg = "pattern contains no capture groups" + with pytest.raises(ValueError, match=msg): + s_or_idx.str.extract('[ABC][123]', expand=False) # only non-capturing groups - f = lambda: s_or_idx.str.extract('(?:[AB]).*', expand=False) - pytest.raises(ValueError, f) + with pytest.raises(ValueError, match=msg): + s_or_idx.str.extract('(?:[AB]).*', expand=False) # single group renames series/index properly s_or_idx = klass(['A1', 'A2']) @@ -1387,12 +1394,13 @@ def test_extract_expand_True(self): for klass in [Series, Index]: # no groups s_or_idx = klass(['A1', 'B2', 'C3']) - f = lambda: s_or_idx.str.extract('[ABC][123]', expand=True) - pytest.raises(ValueError, f) + msg = "pattern contains no capture groups" + with pytest.raises(ValueError, match=msg): + s_or_idx.str.extract('[ABC][123]', expand=True) # only non-capturing groups - f = lambda: s_or_idx.str.extract('(?:[AB]).*', expand=True) - pytest.raises(ValueError, f) + with pytest.raises(ValueError, match=msg): + s_or_idx.str.extract('(?:[AB]).*', expand=True) # single group renames series/index properly s_or_idx = klass(['A1', 'A2']) @@ -3315,10 +3323,14 @@ def test_encode_decode(self): tm.assert_series_equal(result, exp) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_encode_decode_errors(self): encodeBase = Series([u('a'), u('b'), u('a\x9d')]) - pytest.raises(UnicodeEncodeError, encodeBase.str.encode, 'cp1252') + msg = (r"'charmap' codec can't encode character '\\x9d' in position 1:" + " character maps to ") + with pytest.raises(UnicodeEncodeError, match=msg): + encodeBase.str.encode('cp1252') f = lambda x: x.encode('cp1252', 'ignore') result = encodeBase.str.encode('cp1252', 'ignore') @@ -3327,7 +3339,10 @@ def test_encode_decode_errors(self): decodeBase = Series([b'a', b'b', b'a\x9d']) - pytest.raises(UnicodeDecodeError, decodeBase.str.decode, 'cp1252') + msg = ("'charmap' codec can't decode byte 0x9d in position 1:" + " character maps to ") + with pytest.raises(UnicodeDecodeError, match=msg): + decodeBase.str.decode('cp1252') f = lambda x: x.decode('cp1252', 'ignore') result = decodeBase.str.decode('cp1252', 'ignore') @@ -3418,7 +3433,8 @@ def test_method_on_bytes(self): lhs = Series(np.array(list('abc'), 'S1').astype(object)) rhs = Series(np.array(list('def'), 'S1').astype(object)) if compat.PY3: - pytest.raises(TypeError, lhs.str.cat, rhs) + with pytest.raises(TypeError, match="can't concat str to bytes"): + lhs.str.cat(rhs) else: result = lhs.str.cat(rhs) expected = Series(np.array( diff --git a/pandas/tests/test_window.py b/pandas/tests/test_window.py index e816d4c04344a..ce9d1888b8e96 100644 --- a/pandas/tests/test_window.py +++ b/pandas/tests/test_window.py @@ -89,9 +89,8 @@ def test_getitem(self): def test_select_bad_cols(self): df = DataFrame([[1, 2]], columns=['A', 'B']) g = df.rolling(window=5) - pytest.raises(KeyError, g.__getitem__, ['C']) # g[['C']] - - pytest.raises(KeyError, g.__getitem__, ['A', 'C']) # g[['A', 'C']] + with pytest.raises(KeyError, match="Columns not found: 'C'"): + g[['C']] with pytest.raises(KeyError, match='^[^A]+$'): # A should not be referenced as a bad column... # will have to rethink regex if you change message! @@ -102,7 +101,9 @@ def test_attribute_access(self): df = DataFrame([[1, 2]], columns=['A', 'B']) r = df.rolling(window=5) tm.assert_series_equal(r.A.sum(), r['A'].sum()) - pytest.raises(AttributeError, lambda: r.F) + msg = "'Rolling' object has no attribute 'F'" + with pytest.raises(AttributeError, match=msg): + r.F def tests_skip_nuisance(self): @@ -217,12 +218,11 @@ def test_agg_nested_dicts(self): df = DataFrame({'A': range(5), 'B': range(0, 10, 2)}) r = df.rolling(window=3) - def f(): + msg = r"cannot perform renaming for (r1|r2) with a nested dictionary" + with pytest.raises(SpecificationError, match=msg): r.aggregate({'r1': {'A': ['mean', 'sum']}, 'r2': {'B': ['mean', 'sum']}}) - pytest.raises(SpecificationError, f) - expected = concat([r['A'].mean(), r['A'].std(), r['B'].mean(), r['B'].std()], axis=1) expected.columns = pd.MultiIndex.from_tuples([('ra', 'mean'), ( @@ -1806,26 +1806,38 @@ def test_ewm_alpha_arg(self): def test_ewm_domain_checks(self): # GH 12492 s = Series(self.arr) - # com must satisfy: com >= 0 - pytest.raises(ValueError, s.ewm, com=-0.1) + msg = "comass must satisfy: comass >= 0" + with pytest.raises(ValueError, match=msg): + s.ewm(com=-0.1) s.ewm(com=0.0) s.ewm(com=0.1) - # span must satisfy: span >= 1 - pytest.raises(ValueError, s.ewm, span=-0.1) - pytest.raises(ValueError, s.ewm, span=0.0) - pytest.raises(ValueError, s.ewm, span=0.9) + + msg = "span must satisfy: span >= 1" + with pytest.raises(ValueError, match=msg): + s.ewm(span=-0.1) + with pytest.raises(ValueError, match=msg): + s.ewm(span=0.0) + with pytest.raises(ValueError, match=msg): + s.ewm(span=0.9) s.ewm(span=1.0) s.ewm(span=1.1) - # halflife must satisfy: halflife > 0 - pytest.raises(ValueError, s.ewm, halflife=-0.1) - pytest.raises(ValueError, s.ewm, halflife=0.0) + + msg = "halflife must satisfy: halflife > 0" + with pytest.raises(ValueError, match=msg): + s.ewm(halflife=-0.1) + with pytest.raises(ValueError, match=msg): + s.ewm(halflife=0.0) s.ewm(halflife=0.1) - # alpha must satisfy: 0 < alpha <= 1 - pytest.raises(ValueError, s.ewm, alpha=-0.1) - pytest.raises(ValueError, s.ewm, alpha=0.0) + + msg = "alpha must satisfy: 0 < alpha <= 1" + with pytest.raises(ValueError, match=msg): + s.ewm(alpha=-0.1) + with pytest.raises(ValueError, match=msg): + s.ewm(alpha=0.0) s.ewm(alpha=0.1) s.ewm(alpha=1.0) - pytest.raises(ValueError, s.ewm, alpha=1.1) + with pytest.raises(ValueError, match=msg): + s.ewm(alpha=1.1) @pytest.mark.parametrize('method', ['mean', 'vol', 'var']) def test_ew_empty_series(self, method): @@ -2598,7 +2610,10 @@ def get_result(obj, obj2=None): def test_flex_binary_moment(self): # GH3155 # don't blow the stack - pytest.raises(TypeError, rwindow._flex_binary_moment, 5, 6, None) + msg = ("arguments to moment function must be of type" + " np.ndarray/Series/DataFrame") + with pytest.raises(TypeError, match=msg): + rwindow._flex_binary_moment(5, 6, None) def test_corr_sanity(self): # GH 3155 @@ -2682,7 +2697,10 @@ def func(A, B, com, **kwargs): Series([1.]), Series([1.]), 50, min_periods=min_periods) tm.assert_series_equal(result, Series([np.NaN])) - pytest.raises(Exception, func, A, randn(50), 20, min_periods=5) + msg = "Input arrays must be of the same type!" + # exception raised is Exception + with pytest.raises(Exception, match=msg): + func(A, randn(50), 20, min_periods=5) def test_expanding_apply_args_kwargs(self, raw): @@ -3266,9 +3284,9 @@ def setup_method(self, method): def test_mutated(self): - def f(): + msg = r"group\(\) got an unexpected keyword argument 'foo'" + with pytest.raises(TypeError, match=msg): self.frame.groupby('A', foo=1) - pytest.raises(TypeError, f) g = self.frame.groupby('A') assert not g.mutated From e52f06394253b4e0bac56fe3e146fd0f20fc620c Mon Sep 17 00:00:00 2001 From: Nicholas Musolino Date: Thu, 28 Feb 2019 08:35:01 -0500 Subject: [PATCH 165/215] Fix minor error in dynamic load function (#25256) --- scripts/tests/test_validate_docstrings.py | 28 +++++++++++++++++++++++ scripts/validate_docstrings.py | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/scripts/tests/test_validate_docstrings.py b/scripts/tests/test_validate_docstrings.py index bb58449843096..09fb5a30cbc3b 100644 --- a/scripts/tests/test_validate_docstrings.py +++ b/scripts/tests/test_validate_docstrings.py @@ -4,6 +4,8 @@ import textwrap import pytest import numpy as np +import pandas as pd + import validate_docstrings validate_one = validate_docstrings.validate_one @@ -1004,6 +1006,32 @@ def test_item_subsection(self, idx, subsection): assert result[idx][3] == subsection +class TestDocstringClass(object): + @pytest.mark.parametrize('name, expected_obj', + [('pandas.isnull', pd.isnull), + ('pandas.DataFrame', pd.DataFrame), + ('pandas.Series.sum', pd.Series.sum)]) + def test_resolves_class_name(self, name, expected_obj): + d = validate_docstrings.Docstring(name) + assert d.obj is expected_obj + + @pytest.mark.parametrize('invalid_name', ['panda', 'panda.DataFrame']) + def test_raises_for_invalid_module_name(self, invalid_name): + msg = 'No module can be imported from "{}"'.format(invalid_name) + with pytest.raises(ImportError, match=msg): + validate_docstrings.Docstring(invalid_name) + + @pytest.mark.parametrize('invalid_name', + ['pandas.BadClassName', + 'pandas.Series.bad_method_name']) + def test_raises_for_invalid_attribute_name(self, invalid_name): + name_components = invalid_name.split('.') + obj_name, invalid_attr_name = name_components[-2], name_components[-1] + msg = "'{}' has no attribute '{}'".format(obj_name, invalid_attr_name) + with pytest.raises(AttributeError, match=msg): + validate_docstrings.Docstring(invalid_name) + + class TestMainFunction(object): def test_exit_status_for_validate_one(self, monkeypatch): monkeypatch.setattr( diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py index bce33f7e78daa..20f32124a2532 100755 --- a/scripts/validate_docstrings.py +++ b/scripts/validate_docstrings.py @@ -267,7 +267,7 @@ def _load_obj(name): else: continue - if 'module' not in locals(): + if 'obj' not in locals(): raise ImportError('No module can be imported ' 'from "{}"'.format(name)) From 64e5612238225d65fd28ffc72ee91c3eddcc9449 Mon Sep 17 00:00:00 2001 From: William Ayd Date: Thu, 28 Feb 2019 05:36:57 -0800 Subject: [PATCH 166/215] Cythonized GroupBy Quantile (#20405) --- asv_bench/benchmarks/groupby.py | 7 +- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/_libs/groupby.pxd | 6 ++ pandas/_libs/groupby.pyx | 101 +++++++++++++++++++++++++ pandas/core/groupby/groupby.py | 103 +++++++++++++++++++++++--- pandas/tests/groupby/test_function.py | 49 ++++++++++++ pandas/tests/groupby/test_groupby.py | 10 +-- 7 files changed, 258 insertions(+), 19 deletions(-) create mode 100644 pandas/_libs/groupby.pxd diff --git a/asv_bench/benchmarks/groupby.py b/asv_bench/benchmarks/groupby.py index 59e43ee22afde..27d279bb90a31 100644 --- a/asv_bench/benchmarks/groupby.py +++ b/asv_bench/benchmarks/groupby.py @@ -14,7 +14,7 @@ method_blacklist = { 'object': {'median', 'prod', 'sem', 'cumsum', 'sum', 'cummin', 'mean', 'max', 'skew', 'cumprod', 'cummax', 'rank', 'pct_change', 'min', - 'var', 'mad', 'describe', 'std'}, + 'var', 'mad', 'describe', 'std', 'quantile'}, 'datetime': {'median', 'prod', 'sem', 'cumsum', 'sum', 'mean', 'skew', 'cumprod', 'cummax', 'pct_change', 'var', 'mad', 'describe', 'std'} @@ -316,8 +316,9 @@ class GroupByMethods(object): ['all', 'any', 'bfill', 'count', 'cumcount', 'cummax', 'cummin', 'cumprod', 'cumsum', 'describe', 'ffill', 'first', 'head', 'last', 'mad', 'max', 'min', 'median', 'mean', 'nunique', - 'pct_change', 'prod', 'rank', 'sem', 'shift', 'size', 'skew', - 'std', 'sum', 'tail', 'unique', 'value_counts', 'var'], + 'pct_change', 'prod', 'quantile', 'rank', 'sem', 'shift', + 'size', 'skew', 'std', 'sum', 'tail', 'unique', 'value_counts', + 'var'], ['direct', 'transformation']] def setup(self, dtype, method, application): diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index d1fffbc9e2225..a591c498d00c3 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -112,6 +112,7 @@ Performance Improvements - `DataFrame.to_stata()` is now faster when outputting data with any string or non-native endian columns (:issue:`25045`) - Improved performance of :meth:`Series.searchsorted`. The speedup is especially large when the dtype is int8/int16/int32 and the searched key is within the integer bounds for the dtype (:issue:`22034`) +- Improved performance of :meth:`pandas.core.groupby.GroupBy.quantile` (:issue:`20405`) .. _whatsnew_0250.bug_fixes: diff --git a/pandas/_libs/groupby.pxd b/pandas/_libs/groupby.pxd new file mode 100644 index 0000000000000..70ad8a62871e9 --- /dev/null +++ b/pandas/_libs/groupby.pxd @@ -0,0 +1,6 @@ +cdef enum InterpolationEnumType: + INTERPOLATION_LINEAR, + INTERPOLATION_LOWER, + INTERPOLATION_HIGHER, + INTERPOLATION_NEAREST, + INTERPOLATION_MIDPOINT diff --git a/pandas/_libs/groupby.pyx b/pandas/_libs/groupby.pyx index e6b6e2c8a0055..71e25c3955a6d 100644 --- a/pandas/_libs/groupby.pyx +++ b/pandas/_libs/groupby.pyx @@ -644,5 +644,106 @@ def _group_ohlc(floating[:, :] out, group_ohlc_float32 = _group_ohlc['float'] group_ohlc_float64 = _group_ohlc['double'] + +@cython.boundscheck(False) +@cython.wraparound(False) +def group_quantile(ndarray[float64_t] out, + ndarray[int64_t] labels, + numeric[:] values, + ndarray[uint8_t] mask, + float64_t q, + object interpolation): + """ + Calculate the quantile per group. + + Parameters + ---------- + out : ndarray + Array of aggregated values that will be written to. + labels : ndarray + Array containing the unique group labels. + values : ndarray + Array containing the values to apply the function against. + q : float + The quantile value to search for. + + Notes + ----- + Rather than explicitly returning a value, this function modifies the + provided `out` parameter. + """ + cdef: + Py_ssize_t i, N=len(labels), ngroups, grp_sz, non_na_sz + Py_ssize_t grp_start=0, idx=0 + int64_t lab + uint8_t interp + float64_t q_idx, frac, val, next_val + ndarray[int64_t] counts, non_na_counts, sort_arr + + assert values.shape[0] == N + inter_methods = { + 'linear': INTERPOLATION_LINEAR, + 'lower': INTERPOLATION_LOWER, + 'higher': INTERPOLATION_HIGHER, + 'nearest': INTERPOLATION_NEAREST, + 'midpoint': INTERPOLATION_MIDPOINT, + } + interp = inter_methods[interpolation] + + counts = np.zeros_like(out, dtype=np.int64) + non_na_counts = np.zeros_like(out, dtype=np.int64) + ngroups = len(counts) + + # First figure out the size of every group + with nogil: + for i in range(N): + lab = labels[i] + counts[lab] += 1 + if not mask[i]: + non_na_counts[lab] += 1 + + # Get an index of values sorted by labels and then values + order = (values, labels) + sort_arr = np.lexsort(order).astype(np.int64, copy=False) + + with nogil: + for i in range(ngroups): + # Figure out how many group elements there are + grp_sz = counts[i] + non_na_sz = non_na_counts[i] + + if non_na_sz == 0: + out[i] = NaN + else: + # Calculate where to retrieve the desired value + # Casting to int will intentionaly truncate result + idx = grp_start + (q * (non_na_sz - 1)) + + val = values[sort_arr[idx]] + # If requested quantile falls evenly on a particular index + # then write that index's value out. Otherwise interpolate + q_idx = q * (non_na_sz - 1) + frac = q_idx % 1 + + if frac == 0.0 or interp == INTERPOLATION_LOWER: + out[i] = val + else: + next_val = values[sort_arr[idx + 1]] + if interp == INTERPOLATION_LINEAR: + out[i] = val + (next_val - val) * frac + elif interp == INTERPOLATION_HIGHER: + out[i] = next_val + elif interp == INTERPOLATION_MIDPOINT: + out[i] = (val + next_val) / 2.0 + elif interp == INTERPOLATION_NEAREST: + if frac > .5 or (frac == .5 and q > .5): # Always OK? + out[i] = next_val + else: + out[i] = val + + # Increment the index reference in sorted_arr for the next group + grp_start += grp_sz + + # generated from template include "groupby_helper.pxi" diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index c63bc5164e25b..c364f069bf53d 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -29,6 +29,8 @@ class providing the base-class of operations. ensure_float, is_extension_array_dtype, is_numeric_dtype, is_scalar) from pandas.core.dtypes.missing import isna, notna +from pandas.api.types import ( + is_datetime64_dtype, is_integer_dtype, is_object_dtype) import pandas.core.algorithms as algorithms from pandas.core.base import ( DataError, GroupByError, PandasObject, SelectionMixin, SpecificationError) @@ -1024,15 +1026,17 @@ def _bool_agg(self, val_test, skipna): """ def objs_to_bool(vals): - try: - vals = vals.astype(np.bool) - except ValueError: # for objects + # type: np.ndarray -> (np.ndarray, typing.Type) + if is_object_dtype(vals): vals = np.array([bool(x) for x in vals]) + else: + vals = vals.astype(np.bool) - return vals.view(np.uint8) + return vals.view(np.uint8), np.bool - def result_to_bool(result): - return result.astype(np.bool, copy=False) + def result_to_bool(result, inference): + # type: (np.ndarray, typing.Type) -> np.ndarray + return result.astype(inference, copy=False) return self._get_cythonized_result('group_any_all', self.grouper, aggregate=True, @@ -1688,6 +1692,75 @@ def nth(self, n, dropna=None): return result + def quantile(self, q=0.5, interpolation='linear'): + """ + Return group values at the given quantile, a la numpy.percentile. + + Parameters + ---------- + q : float or array-like, default 0.5 (50% quantile) + Value(s) between 0 and 1 providing the quantile(s) to compute. + interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'} + Method to use when the desired quantile falls between two points. + + Returns + ------- + Series or DataFrame + Return type determined by caller of GroupBy object. + + See Also + -------- + Series.quantile : Similar method for Series. + DataFrame.quantile : Similar method for DataFrame. + numpy.percentile : NumPy method to compute qth percentile. + + Examples + -------- + >>> df = pd.DataFrame([ + ... ['a', 1], ['a', 2], ['a', 3], + ... ['b', 1], ['b', 3], ['b', 5] + ... ], columns=['key', 'val']) + >>> df.groupby('key').quantile() + val + key + a 2.0 + b 3.0 + """ + + def pre_processor(vals): + # type: np.ndarray -> (np.ndarray, Optional[typing.Type]) + if is_object_dtype(vals): + raise TypeError("'quantile' cannot be performed against " + "'object' dtypes!") + + inference = None + if is_integer_dtype(vals): + inference = np.int64 + elif is_datetime64_dtype(vals): + inference = 'datetime64[ns]' + vals = vals.astype(np.float) + + return vals, inference + + def post_processor(vals, inference): + # type: (np.ndarray, Optional[typing.Type]) -> np.ndarray + if inference: + # Check for edge case + if not (is_integer_dtype(inference) and + interpolation in {'linear', 'midpoint'}): + vals = vals.astype(inference) + + return vals + + return self._get_cythonized_result('group_quantile', self.grouper, + aggregate=True, + needs_values=True, + needs_mask=True, + cython_dtype=np.float64, + pre_processing=pre_processor, + post_processing=post_processor, + q=q, interpolation=interpolation) + @Substitution(name='groupby') def ngroup(self, ascending=True): """ @@ -1924,10 +1997,16 @@ def _get_cythonized_result(self, how, grouper, aggregate=False, Whether the result of the Cython operation is an index of values to be retrieved, instead of the actual values themselves pre_processing : function, default None - Function to be applied to `values` prior to passing to Cython - Raises if `needs_values` is False + Function to be applied to `values` prior to passing to Cython. + Function should return a tuple where the first element is the + values to be passed to Cython and the second element is an optional + type which the values should be converted to after being returned + by the Cython operation. Raises if `needs_values` is False. post_processing : function, default None - Function to be applied to result of Cython function + Function to be applied to result of Cython function. Should accept + an array of values as the first argument and type inferences as its + second argument, i.e. the signature should be + (ndarray, typing.Type). **kwargs : dict Extra arguments to be passed back to Cython funcs @@ -1963,10 +2042,12 @@ def _get_cythonized_result(self, how, grouper, aggregate=False, result = np.zeros(result_sz, dtype=cython_dtype) func = partial(base_func, result, labels) + inferences = None + if needs_values: vals = obj.values if pre_processing: - vals = pre_processing(vals) + vals, inferences = pre_processing(vals) func = partial(func, vals) if needs_mask: @@ -1982,7 +2063,7 @@ def _get_cythonized_result(self, how, grouper, aggregate=False, result = algorithms.take_nd(obj.values, result) if post_processing: - result = post_processing(result) + result = post_processing(result, inferences) output[name] = result diff --git a/pandas/tests/groupby/test_function.py b/pandas/tests/groupby/test_function.py index 1788b29a11082..b5e328ef64424 100644 --- a/pandas/tests/groupby/test_function.py +++ b/pandas/tests/groupby/test_function.py @@ -1069,6 +1069,55 @@ def test_size(df): tm.assert_series_equal(df.groupby('A').size(), out) +# quantile +# -------------------------------- +@pytest.mark.parametrize("interpolation", [ + "linear", "lower", "higher", "nearest", "midpoint"]) +@pytest.mark.parametrize("a_vals,b_vals", [ + # Ints + ([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]), + ([1, 2, 3, 4], [4, 3, 2, 1]), + ([1, 2, 3, 4, 5], [4, 3, 2, 1]), + # Floats + ([1., 2., 3., 4., 5.], [5., 4., 3., 2., 1.]), + # Missing data + ([1., np.nan, 3., np.nan, 5.], [5., np.nan, 3., np.nan, 1.]), + ([np.nan, 4., np.nan, 2., np.nan], [np.nan, 4., np.nan, 2., np.nan]), + # Timestamps + ([x for x in pd.date_range('1/1/18', freq='D', periods=5)], + [x for x in pd.date_range('1/1/18', freq='D', periods=5)][::-1]), + # All NA + ([np.nan] * 5, [np.nan] * 5), +]) +@pytest.mark.parametrize('q', [0, .25, .5, .75, 1]) +def test_quantile(interpolation, a_vals, b_vals, q): + if interpolation == 'nearest' and q == 0.5 and b_vals == [4, 3, 2, 1]: + pytest.skip("Unclear numpy expectation for nearest result with " + "equidistant data") + + a_expected = pd.Series(a_vals).quantile(q, interpolation=interpolation) + b_expected = pd.Series(b_vals).quantile(q, interpolation=interpolation) + + df = DataFrame({ + 'key': ['a'] * len(a_vals) + ['b'] * len(b_vals), + 'val': a_vals + b_vals}) + + expected = DataFrame([a_expected, b_expected], columns=['val'], + index=Index(['a', 'b'], name='key')) + result = df.groupby('key').quantile(q, interpolation=interpolation) + + tm.assert_frame_equal(result, expected) + + +def test_quantile_raises(): + df = pd.DataFrame([ + ['foo', 'a'], ['foo', 'b'], ['foo', 'c']], columns=['key', 'val']) + + with pytest.raises(TypeError, match="cannot be performed against " + "'object' dtypes"): + df.groupby('key').quantile() + + # pipe # -------------------------------- diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py index 12a5d494648fc..6a11f0ae9b44a 100644 --- a/pandas/tests/groupby/test_groupby.py +++ b/pandas/tests/groupby/test_groupby.py @@ -208,7 +208,7 @@ def f(x, q=None, axis=0): trans_expected = ts_grouped.transform(g) assert_series_equal(apply_result, agg_expected) - assert_series_equal(agg_result, agg_expected, check_names=False) + assert_series_equal(agg_result, agg_expected) assert_series_equal(trans_result, trans_expected) agg_result = ts_grouped.agg(f, q=80) @@ -223,13 +223,13 @@ def f(x, q=None, axis=0): agg_result = df_grouped.agg(np.percentile, 80, axis=0) apply_result = df_grouped.apply(DataFrame.quantile, .8) expected = df_grouped.quantile(.8) - assert_frame_equal(apply_result, expected) - assert_frame_equal(agg_result, expected, check_names=False) + assert_frame_equal(apply_result, expected, check_names=False) + assert_frame_equal(agg_result, expected) agg_result = df_grouped.agg(f, q=80) apply_result = df_grouped.apply(DataFrame.quantile, q=.8) - assert_frame_equal(agg_result, expected, check_names=False) - assert_frame_equal(apply_result, expected) + assert_frame_equal(agg_result, expected) + assert_frame_equal(apply_result, expected, check_names=False) def test_len(): From 50c40ff1afa4a4a6772225e02c320294c422ed1a Mon Sep 17 00:00:00 2001 From: Flavien Lambert Date: Thu, 28 Feb 2019 21:55:33 +0800 Subject: [PATCH 167/215] BUG: Fix regression on DataFrame.replace for regex (#25266) * BUG: Fix regression on DataFrame.replace for regex The commit ensures that the replacement for regex is not confined to the beginning of the string but spans all the characters within. The behaviour is then consistent with versions prior to 0.24.0. One test has been added to account for character replacement when the character is not at the beginning of the string. --- doc/source/whatsnew/v0.24.2.rst | 1 + pandas/core/internals/managers.py | 12 ++++++------ pandas/tests/frame/test_replace.py | 7 +++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 8f4beb3f484a4..4fcde7769b362 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -23,6 +23,7 @@ Fixed Regressions - Fixed regression in :meth:`DataFrame.all` and :meth:`DataFrame.any` where ``bool_only=True`` was ignored (:issue:`25101`) - Fixed issue in ``DataFrame`` construction with passing a mixed list of mixed types could segfault. (:issue:`25075`) - Fixed regression in :meth:`DataFrame.apply` causing ``RecursionError`` when ``dict``-like classes were passed as argument. (:issue:`25196`) +- Fixed regression in :meth:`DataFrame.replace` where ``regex=True`` was only replacing patterns matching the start of the string (:issue:`25259`) - Fixed regression in :meth:`DataFrame.duplicated()`, where empty dataframe was not returning a boolean dtyped Series. (:issue:`25184`) - Fixed regression in :meth:`Series.min` and :meth:`Series.max` where ``numeric_only=True`` was ignored when the ``Series`` contained ```Categorical`` data (:issue:`25299`) diff --git a/pandas/core/internals/managers.py b/pandas/core/internals/managers.py index 38b719db1709f..407db772d73e8 100644 --- a/pandas/core/internals/managers.py +++ b/pandas/core/internals/managers.py @@ -552,9 +552,9 @@ def comp(s, regex=False): if isna(s): return isna(values) if hasattr(s, 'asm8'): - return _compare_or_regex_match(maybe_convert_objects(values), - getattr(s, 'asm8'), regex) - return _compare_or_regex_match(values, s, regex) + return _compare_or_regex_search(maybe_convert_objects(values), + getattr(s, 'asm8'), regex) + return _compare_or_regex_search(values, s, regex) masks = [comp(s, regex) for i, s in enumerate(src_list)] @@ -1897,11 +1897,11 @@ def _consolidate(blocks): return new_blocks -def _compare_or_regex_match(a, b, regex=False): +def _compare_or_regex_search(a, b, regex=False): """ Compare two array_like inputs of the same shape or two scalar values - Calls operator.eq or re.match, depending on regex argument. If regex is + Calls operator.eq or re.search, depending on regex argument. If regex is True, perform an element-wise regex matching. Parameters @@ -1917,7 +1917,7 @@ def _compare_or_regex_match(a, b, regex=False): if not regex: op = lambda x: operator.eq(x, b) else: - op = np.vectorize(lambda x: bool(re.match(b, x)) if isinstance(x, str) + op = np.vectorize(lambda x: bool(re.search(b, x)) if isinstance(x, str) else False) is_a_array = isinstance(a, np.ndarray) diff --git a/pandas/tests/frame/test_replace.py b/pandas/tests/frame/test_replace.py index 219f7a1585fc2..127a64da38ba3 100644 --- a/pandas/tests/frame/test_replace.py +++ b/pandas/tests/frame/test_replace.py @@ -466,6 +466,13 @@ def test_regex_replace_dict_nested(self): assert_frame_equal(res3, expec) assert_frame_equal(res4, expec) + def test_regex_replace_dict_nested_non_first_character(self): + # GH 25259 + df = pd.DataFrame({'first': ['abc', 'bca', 'cab']}) + expected = pd.DataFrame({'first': ['.bc', 'bc.', 'c.b']}) + result = df.replace({'a': '.'}, regex=True) + assert_frame_equal(result, expected) + def test_regex_replace_dict_nested_gh4115(self): df = pd.DataFrame({'Type': ['Q', 'T', 'Q', 'Q', 'T'], 'tmp': 2}) expected = DataFrame({'Type': [0, 1, 0, 0, 1], 'tmp': 2}) From 28abbee762972a7ecafbfcdd88d7984c0afccd50 Mon Sep 17 00:00:00 2001 From: Max van Deursen Date: Thu, 28 Feb 2019 16:17:28 +0100 Subject: [PATCH 168/215] Correct contribution guide docbuild instruction (#25479) --- doc/source/development/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/development/contributing.rst b/doc/source/development/contributing.rst index 1270bfec098e8..027f2d90bbb73 100644 --- a/doc/source/development/contributing.rst +++ b/doc/source/development/contributing.rst @@ -435,7 +435,7 @@ reducing the turn-around time for checking your changes. # compile the reference docs for a single function python make.py clean - python make.py --single DataFrame.join + python make.py --single pandas.DataFrame.join For comparison, a full documentation build may take 15 minutes, but a single section may take 15 seconds. Subsequent builds, which only process portions From f04342ae8e3afec2b30db45d4792209f9e21c1cb Mon Sep 17 00:00:00 2001 From: gfyoung Date: Thu, 28 Feb 2019 10:20:11 -0500 Subject: [PATCH 169/215] TST/REF: Add pytest idiom to test_frequencies.py (#25430) --- pandas/tests/tseries/frequencies/__init__.py | 0 .../tseries/frequencies/test_freq_code.py | 149 ++++ .../tseries/frequencies/test_inference.py | 406 +++++++++ .../tseries/frequencies/test_to_offset.py | 146 ++++ pandas/tests/tseries/test_frequencies.py | 793 ------------------ 5 files changed, 701 insertions(+), 793 deletions(-) create mode 100644 pandas/tests/tseries/frequencies/__init__.py create mode 100644 pandas/tests/tseries/frequencies/test_freq_code.py create mode 100644 pandas/tests/tseries/frequencies/test_inference.py create mode 100644 pandas/tests/tseries/frequencies/test_to_offset.py delete mode 100644 pandas/tests/tseries/test_frequencies.py diff --git a/pandas/tests/tseries/frequencies/__init__.py b/pandas/tests/tseries/frequencies/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/pandas/tests/tseries/frequencies/test_freq_code.py b/pandas/tests/tseries/frequencies/test_freq_code.py new file mode 100644 index 0000000000000..0aa29e451b1ba --- /dev/null +++ b/pandas/tests/tseries/frequencies/test_freq_code.py @@ -0,0 +1,149 @@ +import pytest + +from pandas._libs.tslibs import frequencies as libfrequencies, resolution +from pandas._libs.tslibs.frequencies import ( + FreqGroup, _period_code_map, get_freq, get_freq_code) +import pandas.compat as compat + +import pandas.tseries.offsets as offsets + + +@pytest.fixture(params=list(compat.iteritems(_period_code_map))) +def period_code_item(request): + return request.param + + +@pytest.mark.parametrize("freqstr,expected", [ + ("A", 1000), ("3A", 1000), ("-1A", 1000), + ("Y", 1000), ("3Y", 1000), ("-1Y", 1000), + ("W", 4000), ("W-MON", 4001), ("W-FRI", 4005) +]) +def test_freq_code(freqstr, expected): + assert get_freq(freqstr) == expected + + +def test_freq_code_match(period_code_item): + freqstr, code = period_code_item + assert get_freq(freqstr) == code + + +@pytest.mark.parametrize("freqstr,expected", [ + ("A", 1000), ("3A", 1000), ("-1A", 1000), ("A-JAN", 1000), + ("A-MAY", 1000), ("Y", 1000), ("3Y", 1000), ("-1Y", 1000), + ("Y-JAN", 1000), ("Y-MAY", 1000), (offsets.YearEnd(), 1000), + (offsets.YearEnd(month=1), 1000), (offsets.YearEnd(month=5), 1000), + ("W", 4000), ("W-MON", 4000), ("W-FRI", 4000), (offsets.Week(), 4000), + (offsets.Week(weekday=1), 4000), (offsets.Week(weekday=5), 4000), + ("T", FreqGroup.FR_MIN), +]) +def test_freq_group(freqstr, expected): + assert resolution.get_freq_group(freqstr) == expected + + +def test_freq_group_match(period_code_item): + freqstr, code = period_code_item + + str_group = resolution.get_freq_group(freqstr) + code_group = resolution.get_freq_group(code) + + assert str_group == code_group == code // 1000 * 1000 + + +@pytest.mark.parametrize("freqstr,exp_freqstr", [ + ("D", "D"), ("W", "D"), ("M", "D"), + ("S", "S"), ("T", "S"), ("H", "S") +]) +def test_get_to_timestamp_base(freqstr, exp_freqstr): + tsb = libfrequencies.get_to_timestamp_base + + assert tsb(get_freq_code(freqstr)[0]) == get_freq_code(exp_freqstr)[0] + + +_reso = resolution.Resolution + + +@pytest.mark.parametrize("freqstr,expected", [ + ("A", "year"), ("Q", "quarter"), ("M", "month"), + ("D", "day"), ("H", "hour"), ("T", "minute"), + ("S", "second"), ("L", "millisecond"), + ("U", "microsecond"), ("N", "nanosecond") +]) +def test_get_str_from_freq(freqstr, expected): + assert _reso.get_str_from_freq(freqstr) == expected + + +@pytest.mark.parametrize("freq", ["A", "Q", "M", "D", "H", + "T", "S", "L", "U", "N"]) +def test_get_freq_roundtrip(freq): + result = _reso.get_freq(_reso.get_str_from_freq(freq)) + assert freq == result + + +@pytest.mark.parametrize("freq", ["D", "H", "T", "S", "L", "U"]) +def test_get_freq_roundtrip2(freq): + result = _reso.get_freq(_reso.get_str(_reso.get_reso_from_freq(freq))) + assert freq == result + + +@pytest.mark.parametrize("args,expected", [ + ((1.5, "T"), (90, "S")), ((62.4, "T"), (3744, "S")), + ((1.04, "H"), (3744, "S")), ((1, "D"), (1, "D")), + ((0.342931, "H"), (1234551600, "U")), ((1.2345, "D"), (106660800, "L")) +]) +def test_resolution_bumping(args, expected): + # see gh-14378 + assert _reso.get_stride_from_decimal(*args) == expected + + +@pytest.mark.parametrize("args", [ + (0.5, "N"), + + # Too much precision in the input can prevent. + (0.3429324798798269273987982, "H") +]) +def test_cat(args): + msg = "Could not convert to integer offset at any resolution" + + with pytest.raises(ValueError, match=msg): + _reso.get_stride_from_decimal(*args) + + +@pytest.mark.parametrize("freq_input,expected", [ + # Frequency string. + ("A", (get_freq("A"), 1)), + ("3D", (get_freq("D"), 3)), + ("-2M", (get_freq("M"), -2)), + + # Tuple. + (("D", 1), (get_freq("D"), 1)), + (("A", 3), (get_freq("A"), 3)), + (("M", -2), (get_freq("M"), -2)), + ((5, "T"), (FreqGroup.FR_MIN, 5)), + + # Numeric Tuple. + ((1000, 1), (1000, 1)), + + # Offsets. + (offsets.Day(), (get_freq("D"), 1)), + (offsets.Day(3), (get_freq("D"), 3)), + (offsets.Day(-2), (get_freq("D"), -2)), + (offsets.MonthEnd(), (get_freq("M"), 1)), + (offsets.MonthEnd(3), (get_freq("M"), 3)), + (offsets.MonthEnd(-2), (get_freq("M"), -2)), + (offsets.Week(), (get_freq("W"), 1)), + (offsets.Week(3), (get_freq("W"), 3)), + (offsets.Week(-2), (get_freq("W"), -2)), + (offsets.Hour(), (FreqGroup.FR_HR, 1)), + + # Monday is weekday=0. + (offsets.Week(weekday=1), (get_freq("W-TUE"), 1)), + (offsets.Week(3, weekday=0), (get_freq("W-MON"), 3)), + (offsets.Week(-2, weekday=4), (get_freq("W-FRI"), -2)), +]) +def test_get_freq_code(freq_input, expected): + assert get_freq_code(freq_input) == expected + + +def test_get_code_invalid(): + with pytest.raises(ValueError, match="Invalid frequency"): + get_freq_code((5, "baz")) diff --git a/pandas/tests/tseries/frequencies/test_inference.py b/pandas/tests/tseries/frequencies/test_inference.py new file mode 100644 index 0000000000000..9e7ddbc45bba8 --- /dev/null +++ b/pandas/tests/tseries/frequencies/test_inference.py @@ -0,0 +1,406 @@ +from datetime import datetime, timedelta + +import numpy as np +import pytest + +from pandas._libs.tslibs.ccalendar import DAYS, MONTHS +from pandas._libs.tslibs.frequencies import INVALID_FREQ_ERR_MSG +import pandas.compat as compat +from pandas.compat import is_platform_windows, range + +from pandas import ( + DatetimeIndex, Index, Series, Timestamp, date_range, period_range) +from pandas.core.tools.datetimes import to_datetime +import pandas.util.testing as tm + +import pandas.tseries.frequencies as frequencies +import pandas.tseries.offsets as offsets + + +def _check_generated_range(start, periods, freq): + """ + Check the range generated from a given start, frequency, and period count. + + Parameters + ---------- + start : str + The start date. + periods : int + The number of periods. + freq : str + The frequency of the range. + """ + freq = freq.upper() + + gen = date_range(start, periods=periods, freq=freq) + index = DatetimeIndex(gen.values) + + if not freq.startswith("Q-"): + assert frequencies.infer_freq(index) == gen.freqstr + else: + inf_freq = frequencies.infer_freq(index) + is_dec_range = inf_freq == "Q-DEC" and gen.freqstr in ( + "Q", "Q-DEC", "Q-SEP", "Q-JUN", "Q-MAR") + is_nov_range = inf_freq == "Q-NOV" and gen.freqstr in ( + "Q-NOV", "Q-AUG", "Q-MAY", "Q-FEB") + is_oct_range = inf_freq == "Q-OCT" and gen.freqstr in ( + "Q-OCT", "Q-JUL", "Q-APR", "Q-JAN") + assert is_dec_range or is_nov_range or is_oct_range + + +@pytest.fixture(params=[(timedelta(1), "D"), + (timedelta(hours=1), "H"), + (timedelta(minutes=1), "T"), + (timedelta(seconds=1), "S"), + (np.timedelta64(1, "ns"), "N"), + (timedelta(microseconds=1), "U"), + (timedelta(microseconds=1000), "L")]) +def base_delta_code_pair(request): + return request.param + + +@pytest.fixture(params=[1, 2, 3, 4]) +def count(request): + return request.param + + +@pytest.fixture(params=DAYS) +def day(request): + return request.param + + +@pytest.fixture(params=MONTHS) +def month(request): + return request.param + + +@pytest.fixture(params=[5, 7]) +def periods(request): + return request.param + + +def test_raise_if_period_index(): + index = period_range(start="1/1/1990", periods=20, freq="M") + msg = "Check the `freq` attribute instead of using infer_freq" + + with pytest.raises(TypeError, match=msg): + frequencies.infer_freq(index) + + +def test_raise_if_too_few(): + index = DatetimeIndex(["12/31/1998", "1/3/1999"]) + msg = "Need at least 3 dates to infer frequency" + + with pytest.raises(ValueError, match=msg): + frequencies.infer_freq(index) + + +def test_business_daily(): + index = DatetimeIndex(["01/01/1999", "1/4/1999", "1/5/1999"]) + assert frequencies.infer_freq(index) == "B" + + +def test_business_daily_look_alike(): + # see gh-16624 + # + # Do not infer "B when "weekend" (2-day gap) in wrong place. + index = DatetimeIndex(["12/31/1998", "1/3/1999", "1/4/1999"]) + assert frequencies.infer_freq(index) is None + + +def test_day_corner(): + index = DatetimeIndex(["1/1/2000", "1/2/2000", "1/3/2000"]) + assert frequencies.infer_freq(index) == "D" + + +def test_non_datetime_index(): + dates = to_datetime(["1/1/2000", "1/2/2000", "1/3/2000"]) + assert frequencies.infer_freq(dates) == "D" + + +def test_fifth_week_of_month_infer(): + # see gh-9425 + # + # Only attempt to infer up to WOM-4. + index = DatetimeIndex(["2014-03-31", "2014-06-30", "2015-03-30"]) + assert frequencies.infer_freq(index) is None + + +def test_week_of_month_fake(): + # All of these dates are on same day + # of week and are 4 or 5 weeks apart. + index = DatetimeIndex(["2013-08-27", "2013-10-01", + "2013-10-29", "2013-11-26"]) + assert frequencies.infer_freq(index) != "WOM-4TUE" + + +def test_fifth_week_of_month(): + # see gh-9425 + # + # Only supports freq up to WOM-4. + msg = ("Of the four parameters: start, end, periods, " + "and freq, exactly three must be specified") + + with pytest.raises(ValueError, match=msg): + date_range("2014-01-01", freq="WOM-5MON") + + +def test_monthly_ambiguous(): + rng = DatetimeIndex(["1/31/2000", "2/29/2000", "3/31/2000"]) + assert rng.inferred_freq == "M" + + +def test_annual_ambiguous(): + rng = DatetimeIndex(["1/31/2000", "1/31/2001", "1/31/2002"]) + assert rng.inferred_freq == "A-JAN" + + +def test_infer_freq_delta(base_delta_code_pair, count): + b = Timestamp(datetime.now()) + base_delta, code = base_delta_code_pair + + inc = base_delta * count + index = DatetimeIndex([b + inc * j for j in range(3)]) + + exp_freq = "%d%s" % (count, code) if count > 1 else code + assert frequencies.infer_freq(index) == exp_freq + + +@pytest.mark.parametrize("constructor", [ + lambda now, delta: DatetimeIndex([now + delta * 7] + + [now + delta * j for j in range(3)]), + lambda now, delta: DatetimeIndex([now + delta * j for j in range(3)] + + [now + delta * 7]) +]) +def test_infer_freq_custom(base_delta_code_pair, constructor): + b = Timestamp(datetime.now()) + base_delta, _ = base_delta_code_pair + + index = constructor(b, base_delta) + assert frequencies.infer_freq(index) is None + + +def test_weekly_infer(periods, day): + _check_generated_range("1/1/2000", periods, "W-{day}".format(day=day)) + + +def test_week_of_month_infer(periods, day, count): + _check_generated_range("1/1/2000", periods, + "WOM-{count}{day}".format(count=count, day=day)) + + +@pytest.mark.parametrize("freq", ["M", "BM", "BMS"]) +def test_monthly_infer(periods, freq): + _check_generated_range("1/1/2000", periods, "M") + + +def test_quarterly_infer(month, periods): + _check_generated_range("1/1/2000", periods, + "Q-{month}".format(month=month)) + + +@pytest.mark.parametrize("annual", ["A", "BA"]) +def test_annually_infer(month, periods, annual): + _check_generated_range("1/1/2000", periods, + "{annual}-{month}".format(annual=annual, + month=month)) + + +@pytest.mark.parametrize("freq,expected", [ + ("Q", "Q-DEC"), ("Q-NOV", "Q-NOV"), ("Q-OCT", "Q-OCT") +]) +def test_infer_freq_index(freq, expected): + rng = period_range("1959Q2", "2009Q3", freq=freq) + rng = Index(rng.to_timestamp("D", how="e").astype(object)) + + assert rng.inferred_freq == expected + + +@pytest.mark.parametrize( + "expected,dates", + list(compat.iteritems( + {"AS-JAN": ["2009-01-01", "2010-01-01", "2011-01-01", "2012-01-01"], + "Q-OCT": ["2009-01-31", "2009-04-30", "2009-07-31", "2009-10-31"], + "M": ["2010-11-30", "2010-12-31", "2011-01-31", "2011-02-28"], + "W-SAT": ["2010-12-25", "2011-01-01", "2011-01-08", "2011-01-15"], + "D": ["2011-01-01", "2011-01-02", "2011-01-03", "2011-01-04"], + "H": ["2011-12-31 22:00", "2011-12-31 23:00", + "2012-01-01 00:00", "2012-01-01 01:00"]})) +) +def test_infer_freq_tz(tz_naive_fixture, expected, dates): + # see gh-7310 + tz = tz_naive_fixture + idx = DatetimeIndex(dates, tz=tz) + assert idx.inferred_freq == expected + + +@pytest.mark.parametrize("date_pair", [ + ["2013-11-02", "2013-11-5"], # Fall DST + ["2014-03-08", "2014-03-11"], # Spring DST + ["2014-01-01", "2014-01-03"] # Regular Time +]) +@pytest.mark.parametrize("freq", [ + "3H", "10T", "3601S", "3600001L", "3600000001U", "3600000000001N" +]) +def test_infer_freq_tz_transition(tz_naive_fixture, date_pair, freq): + # see gh-8772 + tz = tz_naive_fixture + idx = date_range(date_pair[0], date_pair[1], freq=freq, tz=tz) + assert idx.inferred_freq == freq + + +def test_infer_freq_tz_transition_custom(): + index = date_range("2013-11-03", periods=5, + freq="3H").tz_localize("America/Chicago") + assert index.inferred_freq is None + + +@pytest.mark.parametrize("data,expected", [ + # Hourly freq in a day must result in "H" + (["2014-07-01 09:00", "2014-07-01 10:00", "2014-07-01 11:00", + "2014-07-01 12:00", "2014-07-01 13:00", "2014-07-01 14:00"], "H"), + + (["2014-07-01 09:00", "2014-07-01 10:00", "2014-07-01 11:00", + "2014-07-01 12:00", "2014-07-01 13:00", "2014-07-01 14:00", + "2014-07-01 15:00", "2014-07-01 16:00", "2014-07-02 09:00", + "2014-07-02 10:00", "2014-07-02 11:00"], "BH"), + (["2014-07-04 09:00", "2014-07-04 10:00", "2014-07-04 11:00", + "2014-07-04 12:00", "2014-07-04 13:00", "2014-07-04 14:00", + "2014-07-04 15:00", "2014-07-04 16:00", "2014-07-07 09:00", + "2014-07-07 10:00", "2014-07-07 11:00"], "BH"), + (["2014-07-04 09:00", "2014-07-04 10:00", "2014-07-04 11:00", + "2014-07-04 12:00", "2014-07-04 13:00", "2014-07-04 14:00", + "2014-07-04 15:00", "2014-07-04 16:00", "2014-07-07 09:00", + "2014-07-07 10:00", "2014-07-07 11:00", "2014-07-07 12:00", + "2014-07-07 13:00", "2014-07-07 14:00", "2014-07-07 15:00", + "2014-07-07 16:00", "2014-07-08 09:00", "2014-07-08 10:00", + "2014-07-08 11:00", "2014-07-08 12:00", "2014-07-08 13:00", + "2014-07-08 14:00", "2014-07-08 15:00", "2014-07-08 16:00"], "BH"), +]) +def test_infer_freq_business_hour(data, expected): + # see gh-7905 + idx = DatetimeIndex(data) + assert idx.inferred_freq == expected + + +def test_not_monotonic(): + rng = DatetimeIndex(["1/31/2000", "1/31/2001", "1/31/2002"]) + rng = rng[::-1] + + assert rng.inferred_freq == "-1A-JAN" + + +def test_non_datetime_index2(): + rng = DatetimeIndex(["1/31/2000", "1/31/2001", "1/31/2002"]) + vals = rng.to_pydatetime() + + result = frequencies.infer_freq(vals) + assert result == rng.inferred_freq + + +@pytest.mark.parametrize("idx", [ + tm.makeIntIndex(10), tm.makeFloatIndex(10), tm.makePeriodIndex(10) +]) +def test_invalid_index_types(idx): + msg = ("(cannot infer freq from a non-convertible)|" + "(Check the `freq` attribute instead of using infer_freq)") + + with pytest.raises(TypeError, match=msg): + frequencies.infer_freq(idx) + + +@pytest.mark.skipif(is_platform_windows(), + reason="see gh-10822: Windows issue") +@pytest.mark.parametrize("idx", [tm.makeStringIndex(10), + tm.makeUnicodeIndex(10)]) +def test_invalid_index_types_unicode(idx): + # see gh-10822 + # + # Odd error message on conversions to datetime for unicode. + msg = "Unknown string format" + + with pytest.raises(ValueError, match=msg): + frequencies.infer_freq(idx) + + +def test_string_datetime_like_compat(): + # see gh-6463 + data = ["2004-01", "2004-02", "2004-03", "2004-04"] + + expected = frequencies.infer_freq(data) + result = frequencies.infer_freq(Index(data)) + + assert result == expected + + +def test_series(): + # see gh-6407 + s = Series(date_range("20130101", "20130110")) + inferred = frequencies.infer_freq(s) + assert inferred == "D" + + +@pytest.mark.parametrize("end", [10, 10.]) +def test_series_invalid_type(end): + # see gh-6407 + msg = "cannot infer freq from a non-convertible dtype on a Series" + s = Series(np.arange(end)) + + with pytest.raises(TypeError, match=msg): + frequencies.infer_freq(s) + + +def test_series_inconvertible_string(): + # see gh-6407 + msg = "Unknown string format" + + with pytest.raises(ValueError, match=msg): + frequencies.infer_freq(Series(["foo", "bar"])) + + +@pytest.mark.parametrize("freq", [None, "L"]) +def test_series_period_index(freq): + # see gh-6407 + # + # Cannot infer on PeriodIndex + msg = "cannot infer freq from a non-convertible dtype on a Series" + s = Series(period_range("2013", periods=10, freq=freq)) + + with pytest.raises(TypeError, match=msg): + frequencies.infer_freq(s) + + +@pytest.mark.parametrize("freq", ["M", "L", "S"]) +def test_series_datetime_index(freq): + s = Series(date_range("20130101", periods=10, freq=freq)) + inferred = frequencies.infer_freq(s) + assert inferred == freq + + +@pytest.mark.parametrize("offset_func", [ + frequencies.get_offset, + lambda freq: date_range("2011-01-01", periods=5, freq=freq) +]) +@pytest.mark.parametrize("freq", [ + "WEEKDAY", "EOM", "W@MON", "W@TUE", "W@WED", "W@THU", + "W@FRI", "W@SAT", "W@SUN", "Q@JAN", "Q@FEB", "Q@MAR", + "A@JAN", "A@FEB", "A@MAR", "A@APR", "A@MAY", "A@JUN", + "A@JUL", "A@AUG", "A@SEP", "A@OCT", "A@NOV", "A@DEC", + "Y@JAN", "WOM@1MON", "WOM@2MON", "WOM@3MON", + "WOM@4MON", "WOM@1TUE", "WOM@2TUE", "WOM@3TUE", + "WOM@4TUE", "WOM@1WED", "WOM@2WED", "WOM@3WED", + "WOM@4WED", "WOM@1THU", "WOM@2THU", "WOM@3THU", + "WOM@4THU", "WOM@1FRI", "WOM@2FRI", "WOM@3FRI", + "WOM@4FRI" +]) +def test_legacy_offset_warnings(offset_func, freq): + with pytest.raises(ValueError, match=INVALID_FREQ_ERR_MSG): + offset_func(freq) + + +def test_ms_vs_capital_ms(): + left = frequencies.get_offset("ms") + right = frequencies.get_offset("MS") + + assert left == offsets.Milli() + assert right == offsets.MonthBegin() diff --git a/pandas/tests/tseries/frequencies/test_to_offset.py b/pandas/tests/tseries/frequencies/test_to_offset.py new file mode 100644 index 0000000000000..c9c35b47f3475 --- /dev/null +++ b/pandas/tests/tseries/frequencies/test_to_offset.py @@ -0,0 +1,146 @@ +import re + +import pytest + +from pandas import Timedelta + +import pandas.tseries.frequencies as frequencies +import pandas.tseries.offsets as offsets + + +@pytest.mark.parametrize("freq_input,expected", [ + (frequencies.to_offset("10us"), offsets.Micro(10)), + (offsets.Hour(), offsets.Hour()), + ((5, "T"), offsets.Minute(5)), + ("2h30min", offsets.Minute(150)), + ("2h 30min", offsets.Minute(150)), + ("2h30min15s", offsets.Second(150 * 60 + 15)), + ("2h 60min", offsets.Hour(3)), + ("2h 20.5min", offsets.Second(8430)), + ("1.5min", offsets.Second(90)), + ("0.5S", offsets.Milli(500)), + ("15l500u", offsets.Micro(15500)), + ("10s75L", offsets.Milli(10075)), + ("1s0.25ms", offsets.Micro(1000250)), + ("1s0.25L", offsets.Micro(1000250)), + ("2800N", offsets.Nano(2800)), + ("2SM", offsets.SemiMonthEnd(2)), + ("2SM-16", offsets.SemiMonthEnd(2, day_of_month=16)), + ("2SMS-14", offsets.SemiMonthBegin(2, day_of_month=14)), + ("2SMS-15", offsets.SemiMonthBegin(2)), +]) +def test_to_offset(freq_input, expected): + result = frequencies.to_offset(freq_input) + assert result == expected + + +@pytest.mark.parametrize("freqstr,expected", [ + ("-1S", -1), + ("-2SM", -2), + ("-1SMS", -1), + ("-5min10s", -310), +]) +def test_to_offset_negative(freqstr, expected): + result = frequencies.to_offset(freqstr) + assert result.n == expected + + +@pytest.mark.parametrize("freqstr", [ + "2h20m", "U1", "-U", "3U1", "-2-3U", "-2D:3H", + "1.5.0S", "2SMS-15-15", "2SMS-15D", "100foo", + + # Invalid leading +/- signs. + "+-1d", "-+1h", "+1", "-7", "+d", "-m", + + # Invalid shortcut anchors. + "SM-0", "SM-28", "SM-29", "SM-FOO", "BSM", "SM--1", "SMS-1", + "SMS-28", "SMS-30", "SMS-BAR", "SMS-BYR", "BSMS", "SMS--2" +]) +def test_to_offset_invalid(freqstr): + # see gh-13930 + + # We escape string because some of our + # inputs contain regex special characters. + msg = re.escape("Invalid frequency: {freqstr}".format(freqstr=freqstr)) + with pytest.raises(ValueError, match=msg): + frequencies.to_offset(freqstr) + + +def test_to_offset_no_evaluate(): + with pytest.raises(ValueError, match="Could not evaluate"): + frequencies.to_offset(("", "")) + + +@pytest.mark.parametrize("freqstr,expected", [ + ("2D 3H", offsets.Hour(51)), + ("2 D3 H", offsets.Hour(51)), + ("2 D 3 H", offsets.Hour(51)), + (" 2 D 3 H ", offsets.Hour(51)), + (" H ", offsets.Hour()), + (" 3 H ", offsets.Hour(3)), +]) +def test_to_offset_whitespace(freqstr, expected): + result = frequencies.to_offset(freqstr) + assert result == expected + + +@pytest.mark.parametrize("freqstr,expected", [ + ("00H 00T 01S", 1), + ("-00H 03T 14S", -194), +]) +def test_to_offset_leading_zero(freqstr, expected): + result = frequencies.to_offset(freqstr) + assert result.n == expected + + +@pytest.mark.parametrize("freqstr,expected", [ + ("+1d", 1), + ("+2h30min", 150), +]) +def test_to_offset_leading_plus(freqstr, expected): + result = frequencies.to_offset(freqstr) + assert result.n == expected + + +@pytest.mark.parametrize("kwargs,expected", [ + (dict(days=1, seconds=1), offsets.Second(86401)), + (dict(days=-1, seconds=1), offsets.Second(-86399)), + (dict(hours=1, minutes=10), offsets.Minute(70)), + (dict(hours=1, minutes=-10), offsets.Minute(50)), + (dict(weeks=1), offsets.Day(7)), + (dict(hours=1), offsets.Hour(1)), + (dict(hours=1), frequencies.to_offset("60min")), + (dict(microseconds=1), offsets.Micro(1)) +]) +def test_to_offset_pd_timedelta(kwargs, expected): + # see gh-9064 + td = Timedelta(**kwargs) + result = frequencies.to_offset(td) + assert result == expected + + +def test_to_offset_pd_timedelta_invalid(): + # see gh-9064 + msg = "Invalid frequency: 0 days 00:00:00" + td = Timedelta(microseconds=0) + + with pytest.raises(ValueError, match=msg): + frequencies.to_offset(td) + + +@pytest.mark.parametrize("shortcut,expected", [ + ("W", offsets.Week(weekday=6)), + ("W-SUN", offsets.Week(weekday=6)), + ("Q", offsets.QuarterEnd(startingMonth=12)), + ("Q-DEC", offsets.QuarterEnd(startingMonth=12)), + ("Q-MAY", offsets.QuarterEnd(startingMonth=5)), + ("SM", offsets.SemiMonthEnd(day_of_month=15)), + ("SM-15", offsets.SemiMonthEnd(day_of_month=15)), + ("SM-1", offsets.SemiMonthEnd(day_of_month=1)), + ("SM-27", offsets.SemiMonthEnd(day_of_month=27)), + ("SMS-2", offsets.SemiMonthBegin(day_of_month=2)), + ("SMS-27", offsets.SemiMonthBegin(day_of_month=27)), +]) +def test_anchored_shortcuts(shortcut, expected): + result = frequencies.to_offset(shortcut) + assert result == expected diff --git a/pandas/tests/tseries/test_frequencies.py b/pandas/tests/tseries/test_frequencies.py deleted file mode 100644 index eb4e63654b47b..0000000000000 --- a/pandas/tests/tseries/test_frequencies.py +++ /dev/null @@ -1,793 +0,0 @@ -from datetime import datetime, timedelta - -import numpy as np -import pytest - -from pandas._libs.tslibs import frequencies as libfrequencies, resolution -from pandas._libs.tslibs.ccalendar import MONTHS -from pandas._libs.tslibs.frequencies import ( - INVALID_FREQ_ERR_MSG, FreqGroup, _period_code_map, get_freq, get_freq_code) -import pandas.compat as compat -from pandas.compat import is_platform_windows, range - -from pandas import ( - DatetimeIndex, Index, Series, Timedelta, Timestamp, date_range, - period_range) -from pandas.core.tools.datetimes import to_datetime -import pandas.util.testing as tm - -import pandas.tseries.frequencies as frequencies -import pandas.tseries.offsets as offsets - - -class TestToOffset(object): - - def test_to_offset_multiple(self): - freqstr = '2h30min' - freqstr2 = '2h 30min' - - result = frequencies.to_offset(freqstr) - assert (result == frequencies.to_offset(freqstr2)) - expected = offsets.Minute(150) - assert (result == expected) - - freqstr = '2h30min15s' - result = frequencies.to_offset(freqstr) - expected = offsets.Second(150 * 60 + 15) - assert (result == expected) - - freqstr = '2h 60min' - result = frequencies.to_offset(freqstr) - expected = offsets.Hour(3) - assert (result == expected) - - freqstr = '2h 20.5min' - result = frequencies.to_offset(freqstr) - expected = offsets.Second(8430) - assert (result == expected) - - freqstr = '1.5min' - result = frequencies.to_offset(freqstr) - expected = offsets.Second(90) - assert (result == expected) - - freqstr = '0.5S' - result = frequencies.to_offset(freqstr) - expected = offsets.Milli(500) - assert (result == expected) - - freqstr = '15l500u' - result = frequencies.to_offset(freqstr) - expected = offsets.Micro(15500) - assert (result == expected) - - freqstr = '10s75L' - result = frequencies.to_offset(freqstr) - expected = offsets.Milli(10075) - assert (result == expected) - - freqstr = '1s0.25ms' - result = frequencies.to_offset(freqstr) - expected = offsets.Micro(1000250) - assert (result == expected) - - freqstr = '1s0.25L' - result = frequencies.to_offset(freqstr) - expected = offsets.Micro(1000250) - assert (result == expected) - - freqstr = '2800N' - result = frequencies.to_offset(freqstr) - expected = offsets.Nano(2800) - assert (result == expected) - - freqstr = '2SM' - result = frequencies.to_offset(freqstr) - expected = offsets.SemiMonthEnd(2) - assert (result == expected) - - freqstr = '2SM-16' - result = frequencies.to_offset(freqstr) - expected = offsets.SemiMonthEnd(2, day_of_month=16) - assert (result == expected) - - freqstr = '2SMS-14' - result = frequencies.to_offset(freqstr) - expected = offsets.SemiMonthBegin(2, day_of_month=14) - assert (result == expected) - - freqstr = '2SMS-15' - result = frequencies.to_offset(freqstr) - expected = offsets.SemiMonthBegin(2) - assert (result == expected) - - # malformed - with pytest.raises(ValueError, match='Invalid frequency: 2h20m'): - frequencies.to_offset('2h20m') - - def test_to_offset_negative(self): - freqstr = '-1S' - result = frequencies.to_offset(freqstr) - assert (result.n == -1) - - freqstr = '-5min10s' - result = frequencies.to_offset(freqstr) - assert (result.n == -310) - - freqstr = '-2SM' - result = frequencies.to_offset(freqstr) - assert (result.n == -2) - - freqstr = '-1SMS' - result = frequencies.to_offset(freqstr) - assert (result.n == -1) - - def test_to_offset_invalid(self): - # GH 13930 - with pytest.raises(ValueError, match='Invalid frequency: U1'): - frequencies.to_offset('U1') - with pytest.raises(ValueError, match='Invalid frequency: -U'): - frequencies.to_offset('-U') - with pytest.raises(ValueError, match='Invalid frequency: 3U1'): - frequencies.to_offset('3U1') - with pytest.raises(ValueError, match='Invalid frequency: -2-3U'): - frequencies.to_offset('-2-3U') - with pytest.raises(ValueError, match='Invalid frequency: -2D:3H'): - frequencies.to_offset('-2D:3H') - with pytest.raises(ValueError, match='Invalid frequency: 1.5.0S'): - frequencies.to_offset('1.5.0S') - - # split offsets with spaces are valid - assert frequencies.to_offset('2D 3H') == offsets.Hour(51) - assert frequencies.to_offset('2 D3 H') == offsets.Hour(51) - assert frequencies.to_offset('2 D 3 H') == offsets.Hour(51) - assert frequencies.to_offset(' 2 D 3 H ') == offsets.Hour(51) - assert frequencies.to_offset(' H ') == offsets.Hour() - assert frequencies.to_offset(' 3 H ') == offsets.Hour(3) - - # special cases - assert frequencies.to_offset('2SMS-15') == offsets.SemiMonthBegin(2) - with pytest.raises(ValueError, match='Invalid frequency: 2SMS-15-15'): - frequencies.to_offset('2SMS-15-15') - with pytest.raises(ValueError, match='Invalid frequency: 2SMS-15D'): - frequencies.to_offset('2SMS-15D') - - def test_to_offset_leading_zero(self): - freqstr = '00H 00T 01S' - result = frequencies.to_offset(freqstr) - assert (result.n == 1) - - freqstr = '-00H 03T 14S' - result = frequencies.to_offset(freqstr) - assert (result.n == -194) - - def test_to_offset_leading_plus(self): - freqstr = '+1d' - result = frequencies.to_offset(freqstr) - assert (result.n == 1) - - freqstr = '+2h30min' - result = frequencies.to_offset(freqstr) - assert (result.n == 150) - - for bad_freq in ['+-1d', '-+1h', '+1', '-7', '+d', '-m']: - with pytest.raises(ValueError, match='Invalid frequency:'): - frequencies.to_offset(bad_freq) - - def test_to_offset_pd_timedelta(self): - # Tests for #9064 - td = Timedelta(days=1, seconds=1) - result = frequencies.to_offset(td) - expected = offsets.Second(86401) - assert (expected == result) - - td = Timedelta(days=-1, seconds=1) - result = frequencies.to_offset(td) - expected = offsets.Second(-86399) - assert (expected == result) - - td = Timedelta(hours=1, minutes=10) - result = frequencies.to_offset(td) - expected = offsets.Minute(70) - assert (expected == result) - - td = Timedelta(hours=1, minutes=-10) - result = frequencies.to_offset(td) - expected = offsets.Minute(50) - assert (expected == result) - - td = Timedelta(weeks=1) - result = frequencies.to_offset(td) - expected = offsets.Day(7) - assert (expected == result) - - td1 = Timedelta(hours=1) - result1 = frequencies.to_offset(td1) - result2 = frequencies.to_offset('60min') - assert (result1 == result2) - - td = Timedelta(microseconds=1) - result = frequencies.to_offset(td) - expected = offsets.Micro(1) - assert (expected == result) - - td = Timedelta(microseconds=0) - pytest.raises(ValueError, lambda: frequencies.to_offset(td)) - - def test_anchored_shortcuts(self): - result = frequencies.to_offset('W') - expected = frequencies.to_offset('W-SUN') - assert (result == expected) - - result1 = frequencies.to_offset('Q') - result2 = frequencies.to_offset('Q-DEC') - expected = offsets.QuarterEnd(startingMonth=12) - assert (result1 == expected) - assert (result2 == expected) - - result1 = frequencies.to_offset('Q-MAY') - expected = offsets.QuarterEnd(startingMonth=5) - assert (result1 == expected) - - result1 = frequencies.to_offset('SM') - result2 = frequencies.to_offset('SM-15') - expected = offsets.SemiMonthEnd(day_of_month=15) - assert (result1 == expected) - assert (result2 == expected) - - result = frequencies.to_offset('SM-1') - expected = offsets.SemiMonthEnd(day_of_month=1) - assert (result == expected) - - result = frequencies.to_offset('SM-27') - expected = offsets.SemiMonthEnd(day_of_month=27) - assert (result == expected) - - result = frequencies.to_offset('SMS-2') - expected = offsets.SemiMonthBegin(day_of_month=2) - assert (result == expected) - - result = frequencies.to_offset('SMS-27') - expected = offsets.SemiMonthBegin(day_of_month=27) - assert (result == expected) - - # ensure invalid cases fail as expected - invalid_anchors = ['SM-0', 'SM-28', 'SM-29', - 'SM-FOO', 'BSM', 'SM--1', - 'SMS-1', 'SMS-28', 'SMS-30', - 'SMS-BAR', 'SMS-BYR' 'BSMS', - 'SMS--2'] - for invalid_anchor in invalid_anchors: - with pytest.raises(ValueError, match='Invalid frequency: '): - frequencies.to_offset(invalid_anchor) - - -def test_ms_vs_MS(): - left = frequencies.get_offset('ms') - right = frequencies.get_offset('MS') - assert left == offsets.Milli() - assert right == offsets.MonthBegin() - - -def test_rule_aliases(): - rule = frequencies.to_offset('10us') - assert rule == offsets.Micro(10) - - -class TestFrequencyCode(object): - - def test_freq_code(self): - assert get_freq('A') == 1000 - assert get_freq('3A') == 1000 - assert get_freq('-1A') == 1000 - - assert get_freq('Y') == 1000 - assert get_freq('3Y') == 1000 - assert get_freq('-1Y') == 1000 - - assert get_freq('W') == 4000 - assert get_freq('W-MON') == 4001 - assert get_freq('W-FRI') == 4005 - - for freqstr, code in compat.iteritems(_period_code_map): - result = get_freq(freqstr) - assert result == code - - result = resolution.get_freq_group(freqstr) - assert result == code // 1000 * 1000 - - result = resolution.get_freq_group(code) - assert result == code // 1000 * 1000 - - def test_freq_group(self): - assert resolution.get_freq_group('A') == 1000 - assert resolution.get_freq_group('3A') == 1000 - assert resolution.get_freq_group('-1A') == 1000 - assert resolution.get_freq_group('A-JAN') == 1000 - assert resolution.get_freq_group('A-MAY') == 1000 - - assert resolution.get_freq_group('Y') == 1000 - assert resolution.get_freq_group('3Y') == 1000 - assert resolution.get_freq_group('-1Y') == 1000 - assert resolution.get_freq_group('Y-JAN') == 1000 - assert resolution.get_freq_group('Y-MAY') == 1000 - - assert resolution.get_freq_group(offsets.YearEnd()) == 1000 - assert resolution.get_freq_group(offsets.YearEnd(month=1)) == 1000 - assert resolution.get_freq_group(offsets.YearEnd(month=5)) == 1000 - - assert resolution.get_freq_group('W') == 4000 - assert resolution.get_freq_group('W-MON') == 4000 - assert resolution.get_freq_group('W-FRI') == 4000 - assert resolution.get_freq_group(offsets.Week()) == 4000 - assert resolution.get_freq_group(offsets.Week(weekday=1)) == 4000 - assert resolution.get_freq_group(offsets.Week(weekday=5)) == 4000 - - def test_get_to_timestamp_base(self): - tsb = libfrequencies.get_to_timestamp_base - - assert (tsb(get_freq_code('D')[0]) == - get_freq_code('D')[0]) - assert (tsb(get_freq_code('W')[0]) == - get_freq_code('D')[0]) - assert (tsb(get_freq_code('M')[0]) == - get_freq_code('D')[0]) - - assert (tsb(get_freq_code('S')[0]) == - get_freq_code('S')[0]) - assert (tsb(get_freq_code('T')[0]) == - get_freq_code('S')[0]) - assert (tsb(get_freq_code('H')[0]) == - get_freq_code('S')[0]) - - def test_freq_to_reso(self): - Reso = resolution.Resolution - - assert Reso.get_str_from_freq('A') == 'year' - assert Reso.get_str_from_freq('Q') == 'quarter' - assert Reso.get_str_from_freq('M') == 'month' - assert Reso.get_str_from_freq('D') == 'day' - assert Reso.get_str_from_freq('H') == 'hour' - assert Reso.get_str_from_freq('T') == 'minute' - assert Reso.get_str_from_freq('S') == 'second' - assert Reso.get_str_from_freq('L') == 'millisecond' - assert Reso.get_str_from_freq('U') == 'microsecond' - assert Reso.get_str_from_freq('N') == 'nanosecond' - - for freq in ['A', 'Q', 'M', 'D', 'H', 'T', 'S', 'L', 'U', 'N']: - # check roundtrip - result = Reso.get_freq(Reso.get_str_from_freq(freq)) - assert freq == result - - for freq in ['D', 'H', 'T', 'S', 'L', 'U']: - result = Reso.get_freq(Reso.get_str(Reso.get_reso_from_freq(freq))) - assert freq == result - - def test_resolution_bumping(self): - # see gh-14378 - Reso = resolution.Resolution - - assert Reso.get_stride_from_decimal(1.5, 'T') == (90, 'S') - assert Reso.get_stride_from_decimal(62.4, 'T') == (3744, 'S') - assert Reso.get_stride_from_decimal(1.04, 'H') == (3744, 'S') - assert Reso.get_stride_from_decimal(1, 'D') == (1, 'D') - assert (Reso.get_stride_from_decimal(0.342931, 'H') == - (1234551600, 'U')) - assert Reso.get_stride_from_decimal(1.2345, 'D') == (106660800, 'L') - - with pytest.raises(ValueError): - Reso.get_stride_from_decimal(0.5, 'N') - - # too much precision in the input can prevent - with pytest.raises(ValueError): - Reso.get_stride_from_decimal(0.3429324798798269273987982, 'H') - - def test_get_freq_code(self): - # frequency str - assert (get_freq_code('A') == - (get_freq('A'), 1)) - assert (get_freq_code('3D') == - (get_freq('D'), 3)) - assert (get_freq_code('-2M') == - (get_freq('M'), -2)) - - # tuple - assert (get_freq_code(('D', 1)) == - (get_freq('D'), 1)) - assert (get_freq_code(('A', 3)) == - (get_freq('A'), 3)) - assert (get_freq_code(('M', -2)) == - (get_freq('M'), -2)) - - # numeric tuple - assert get_freq_code((1000, 1)) == (1000, 1) - - # offsets - assert (get_freq_code(offsets.Day()) == - (get_freq('D'), 1)) - assert (get_freq_code(offsets.Day(3)) == - (get_freq('D'), 3)) - assert (get_freq_code(offsets.Day(-2)) == - (get_freq('D'), -2)) - - assert (get_freq_code(offsets.MonthEnd()) == - (get_freq('M'), 1)) - assert (get_freq_code(offsets.MonthEnd(3)) == - (get_freq('M'), 3)) - assert (get_freq_code(offsets.MonthEnd(-2)) == - (get_freq('M'), -2)) - - assert (get_freq_code(offsets.Week()) == - (get_freq('W'), 1)) - assert (get_freq_code(offsets.Week(3)) == - (get_freq('W'), 3)) - assert (get_freq_code(offsets.Week(-2)) == - (get_freq('W'), -2)) - - # Monday is weekday=0 - assert (get_freq_code(offsets.Week(weekday=1)) == - (get_freq('W-TUE'), 1)) - assert (get_freq_code(offsets.Week(3, weekday=0)) == - (get_freq('W-MON'), 3)) - assert (get_freq_code(offsets.Week(-2, weekday=4)) == - (get_freq('W-FRI'), -2)) - - def test_frequency_misc(self): - assert (resolution.get_freq_group('T') == - FreqGroup.FR_MIN) - - code, stride = get_freq_code(offsets.Hour()) - assert code == FreqGroup.FR_HR - - code, stride = get_freq_code((5, 'T')) - assert code == FreqGroup.FR_MIN - assert stride == 5 - - offset = offsets.Hour() - result = frequencies.to_offset(offset) - assert result == offset - - result = frequencies.to_offset((5, 'T')) - expected = offsets.Minute(5) - assert result == expected - - with pytest.raises(ValueError, match='Invalid frequency'): - get_freq_code((5, 'baz')) - - with pytest.raises(ValueError, match='Invalid frequency'): - frequencies.to_offset('100foo') - - with pytest.raises(ValueError, match='Could not evaluate'): - frequencies.to_offset(('', '')) - - -_dti = DatetimeIndex - - -class TestFrequencyInference(object): - - def test_raise_if_period_index(self): - index = period_range(start="1/1/1990", periods=20, freq="M") - pytest.raises(TypeError, frequencies.infer_freq, index) - - def test_raise_if_too_few(self): - index = _dti(['12/31/1998', '1/3/1999']) - pytest.raises(ValueError, frequencies.infer_freq, index) - - def test_business_daily(self): - index = _dti(['01/01/1999', '1/4/1999', '1/5/1999']) - assert frequencies.infer_freq(index) == 'B' - - def test_business_daily_look_alike(self): - # GH 16624, do not infer 'B' when 'weekend' (2-day gap) in wrong place - index = _dti(['12/31/1998', '1/3/1999', '1/4/1999']) - assert frequencies.infer_freq(index) is None - - def test_day(self): - self._check_tick(timedelta(1), 'D') - - def test_day_corner(self): - index = _dti(['1/1/2000', '1/2/2000', '1/3/2000']) - assert frequencies.infer_freq(index) == 'D' - - def test_non_datetimeindex(self): - dates = to_datetime(['1/1/2000', '1/2/2000', '1/3/2000']) - assert frequencies.infer_freq(dates) == 'D' - - def test_hour(self): - self._check_tick(timedelta(hours=1), 'H') - - def test_minute(self): - self._check_tick(timedelta(minutes=1), 'T') - - def test_second(self): - self._check_tick(timedelta(seconds=1), 'S') - - def test_millisecond(self): - self._check_tick(timedelta(microseconds=1000), 'L') - - def test_microsecond(self): - self._check_tick(timedelta(microseconds=1), 'U') - - def test_nanosecond(self): - self._check_tick(np.timedelta64(1, 'ns'), 'N') - - def _check_tick(self, base_delta, code): - b = Timestamp(datetime.now()) - for i in range(1, 5): - inc = base_delta * i - index = _dti([b + inc * j for j in range(3)]) - if i > 1: - exp_freq = '%d%s' % (i, code) - else: - exp_freq = code - assert frequencies.infer_freq(index) == exp_freq - - index = _dti([b + base_delta * 7] + [b + base_delta * j for j in range( - 3)]) - assert frequencies.infer_freq(index) is None - - index = _dti([b + base_delta * j for j in range(3)] + [b + base_delta * - 7]) - - assert frequencies.infer_freq(index) is None - - def test_weekly(self): - days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] - - for day in days: - self._check_generated_range('1/1/2000', 'W-%s' % day) - - def test_week_of_month(self): - days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] - - for day in days: - for i in range(1, 5): - self._check_generated_range('1/1/2000', 'WOM-%d%s' % (i, day)) - - def test_fifth_week_of_month(self): - # Only supports freq up to WOM-4. See #9425 - func = lambda: date_range('2014-01-01', freq='WOM-5MON') - pytest.raises(ValueError, func) - - def test_fifth_week_of_month_infer(self): - # Only attempts to infer up to WOM-4. See #9425 - index = DatetimeIndex(["2014-03-31", "2014-06-30", "2015-03-30"]) - assert frequencies.infer_freq(index) is None - - def test_week_of_month_fake(self): - # All of these dates are on same day of week and are 4 or 5 weeks apart - index = DatetimeIndex(["2013-08-27", "2013-10-01", "2013-10-29", - "2013-11-26"]) - assert frequencies.infer_freq(index) != 'WOM-4TUE' - - def test_monthly(self): - self._check_generated_range('1/1/2000', 'M') - - def test_monthly_ambiguous(self): - rng = _dti(['1/31/2000', '2/29/2000', '3/31/2000']) - assert rng.inferred_freq == 'M' - - def test_business_monthly(self): - self._check_generated_range('1/1/2000', 'BM') - - def test_business_start_monthly(self): - self._check_generated_range('1/1/2000', 'BMS') - - def test_quarterly(self): - for month in ['JAN', 'FEB', 'MAR']: - self._check_generated_range('1/1/2000', 'Q-%s' % month) - - def test_annual(self): - for month in MONTHS: - self._check_generated_range('1/1/2000', 'A-%s' % month) - - def test_business_annual(self): - for month in MONTHS: - self._check_generated_range('1/1/2000', 'BA-%s' % month) - - def test_annual_ambiguous(self): - rng = _dti(['1/31/2000', '1/31/2001', '1/31/2002']) - assert rng.inferred_freq == 'A-JAN' - - def _check_generated_range(self, start, freq): - freq = freq.upper() - - gen = date_range(start, periods=7, freq=freq) - index = _dti(gen.values) - if not freq.startswith('Q-'): - assert frequencies.infer_freq(index) == gen.freqstr - else: - inf_freq = frequencies.infer_freq(index) - is_dec_range = inf_freq == 'Q-DEC' and gen.freqstr in ( - 'Q', 'Q-DEC', 'Q-SEP', 'Q-JUN', 'Q-MAR') - is_nov_range = inf_freq == 'Q-NOV' and gen.freqstr in ( - 'Q-NOV', 'Q-AUG', 'Q-MAY', 'Q-FEB') - is_oct_range = inf_freq == 'Q-OCT' and gen.freqstr in ( - 'Q-OCT', 'Q-JUL', 'Q-APR', 'Q-JAN') - assert is_dec_range or is_nov_range or is_oct_range - - gen = date_range(start, periods=5, freq=freq) - index = _dti(gen.values) - - if not freq.startswith('Q-'): - assert frequencies.infer_freq(index) == gen.freqstr - else: - inf_freq = frequencies.infer_freq(index) - is_dec_range = inf_freq == 'Q-DEC' and gen.freqstr in ( - 'Q', 'Q-DEC', 'Q-SEP', 'Q-JUN', 'Q-MAR') - is_nov_range = inf_freq == 'Q-NOV' and gen.freqstr in ( - 'Q-NOV', 'Q-AUG', 'Q-MAY', 'Q-FEB') - is_oct_range = inf_freq == 'Q-OCT' and gen.freqstr in ( - 'Q-OCT', 'Q-JUL', 'Q-APR', 'Q-JAN') - - assert is_dec_range or is_nov_range or is_oct_range - - def test_infer_freq(self): - rng = period_range('1959Q2', '2009Q3', freq='Q') - rng = Index(rng.to_timestamp('D', how='e').astype(object)) - assert rng.inferred_freq == 'Q-DEC' - - rng = period_range('1959Q2', '2009Q3', freq='Q-NOV') - rng = Index(rng.to_timestamp('D', how='e').astype(object)) - assert rng.inferred_freq == 'Q-NOV' - - rng = period_range('1959Q2', '2009Q3', freq='Q-OCT') - rng = Index(rng.to_timestamp('D', how='e').astype(object)) - assert rng.inferred_freq == 'Q-OCT' - - def test_infer_freq_tz(self): - - freqs = {'AS-JAN': - ['2009-01-01', '2010-01-01', '2011-01-01', '2012-01-01'], - 'Q-OCT': - ['2009-01-31', '2009-04-30', '2009-07-31', '2009-10-31'], - 'M': ['2010-11-30', '2010-12-31', '2011-01-31', '2011-02-28'], - 'W-SAT': - ['2010-12-25', '2011-01-01', '2011-01-08', '2011-01-15'], - 'D': ['2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04'], - 'H': ['2011-12-31 22:00', '2011-12-31 23:00', - '2012-01-01 00:00', '2012-01-01 01:00']} - - # GH 7310 - for tz in [None, 'Australia/Sydney', 'Asia/Tokyo', 'Europe/Paris', - 'US/Pacific', 'US/Eastern']: - for expected, dates in compat.iteritems(freqs): - idx = DatetimeIndex(dates, tz=tz) - assert idx.inferred_freq == expected - - def test_infer_freq_tz_transition(self): - # Tests for #8772 - date_pairs = [['2013-11-02', '2013-11-5'], # Fall DST - ['2014-03-08', '2014-03-11'], # Spring DST - ['2014-01-01', '2014-01-03']] # Regular Time - freqs = ['3H', '10T', '3601S', '3600001L', '3600000001U', - '3600000000001N'] - - for tz in [None, 'Australia/Sydney', 'Asia/Tokyo', 'Europe/Paris', - 'US/Pacific', 'US/Eastern']: - for date_pair in date_pairs: - for freq in freqs: - idx = date_range(date_pair[0], date_pair[ - 1], freq=freq, tz=tz) - assert idx.inferred_freq == freq - - index = date_range("2013-11-03", periods=5, - freq="3H").tz_localize("America/Chicago") - assert index.inferred_freq is None - - def test_infer_freq_businesshour(self): - # GH 7905 - idx = DatetimeIndex( - ['2014-07-01 09:00', '2014-07-01 10:00', '2014-07-01 11:00', - '2014-07-01 12:00', '2014-07-01 13:00', '2014-07-01 14:00']) - # hourly freq in a day must result in 'H' - assert idx.inferred_freq == 'H' - - idx = DatetimeIndex( - ['2014-07-01 09:00', '2014-07-01 10:00', '2014-07-01 11:00', - '2014-07-01 12:00', '2014-07-01 13:00', '2014-07-01 14:00', - '2014-07-01 15:00', '2014-07-01 16:00', '2014-07-02 09:00', - '2014-07-02 10:00', '2014-07-02 11:00']) - assert idx.inferred_freq == 'BH' - - idx = DatetimeIndex( - ['2014-07-04 09:00', '2014-07-04 10:00', '2014-07-04 11:00', - '2014-07-04 12:00', '2014-07-04 13:00', '2014-07-04 14:00', - '2014-07-04 15:00', '2014-07-04 16:00', '2014-07-07 09:00', - '2014-07-07 10:00', '2014-07-07 11:00']) - assert idx.inferred_freq == 'BH' - - idx = DatetimeIndex( - ['2014-07-04 09:00', '2014-07-04 10:00', '2014-07-04 11:00', - '2014-07-04 12:00', '2014-07-04 13:00', '2014-07-04 14:00', - '2014-07-04 15:00', '2014-07-04 16:00', '2014-07-07 09:00', - '2014-07-07 10:00', '2014-07-07 11:00', '2014-07-07 12:00', - '2014-07-07 13:00', '2014-07-07 14:00', '2014-07-07 15:00', - '2014-07-07 16:00', '2014-07-08 09:00', '2014-07-08 10:00', - '2014-07-08 11:00', '2014-07-08 12:00', '2014-07-08 13:00', - '2014-07-08 14:00', '2014-07-08 15:00', '2014-07-08 16:00']) - assert idx.inferred_freq == 'BH' - - def test_not_monotonic(self): - rng = _dti(['1/31/2000', '1/31/2001', '1/31/2002']) - rng = rng[::-1] - assert rng.inferred_freq == '-1A-JAN' - - def test_non_datetimeindex2(self): - rng = _dti(['1/31/2000', '1/31/2001', '1/31/2002']) - - vals = rng.to_pydatetime() - - result = frequencies.infer_freq(vals) - assert result == rng.inferred_freq - - def test_invalid_index_types(self): - - # test all index types - for i in [tm.makeIntIndex(10), tm.makeFloatIndex(10), - tm.makePeriodIndex(10)]: - pytest.raises(TypeError, lambda: frequencies.infer_freq(i)) - - # GH 10822 - # odd error message on conversions to datetime for unicode - if not is_platform_windows(): - for i in [tm.makeStringIndex(10), tm.makeUnicodeIndex(10)]: - pytest.raises(ValueError, lambda: frequencies.infer_freq(i)) - - def test_string_datetimelike_compat(self): - - # GH 6463 - expected = frequencies.infer_freq(['2004-01', '2004-02', '2004-03', - '2004-04']) - result = frequencies.infer_freq(Index(['2004-01', '2004-02', '2004-03', - '2004-04'])) - assert result == expected - - def test_series(self): - - # GH6407 - # inferring series - - # invalid type of Series - for s in [Series(np.arange(10)), Series(np.arange(10.))]: - pytest.raises(TypeError, lambda: frequencies.infer_freq(s)) - - # a non-convertible string - pytest.raises(ValueError, lambda: frequencies.infer_freq( - Series(['foo', 'bar']))) - - # cannot infer on PeriodIndex - for freq in [None, 'L']: - s = Series(period_range('2013', periods=10, freq=freq)) - pytest.raises(TypeError, lambda: frequencies.infer_freq(s)) - - # DateTimeIndex - for freq in ['M', 'L', 'S']: - s = Series(date_range('20130101', periods=10, freq=freq)) - inferred = frequencies.infer_freq(s) - assert inferred == freq - - s = Series(date_range('20130101', '20130110')) - inferred = frequencies.infer_freq(s) - assert inferred == 'D' - - def test_legacy_offset_warnings(self): - freqs = ['WEEKDAY', 'EOM', 'W@MON', 'W@TUE', 'W@WED', 'W@THU', - 'W@FRI', 'W@SAT', 'W@SUN', 'Q@JAN', 'Q@FEB', 'Q@MAR', - 'A@JAN', 'A@FEB', 'A@MAR', 'A@APR', 'A@MAY', 'A@JUN', - 'A@JUL', 'A@AUG', 'A@SEP', 'A@OCT', 'A@NOV', 'A@DEC', - 'Y@JAN', 'WOM@1MON', 'WOM@2MON', 'WOM@3MON', - 'WOM@4MON', 'WOM@1TUE', 'WOM@2TUE', 'WOM@3TUE', - 'WOM@4TUE', 'WOM@1WED', 'WOM@2WED', 'WOM@3WED', - 'WOM@4WED', 'WOM@1THU', 'WOM@2THU', 'WOM@3THU', - 'WOM@4THU', 'WOM@1FRI', 'WOM@2FRI', 'WOM@3FRI', - 'WOM@4FRI'] - - msg = INVALID_FREQ_ERR_MSG - for freq in freqs: - with pytest.raises(ValueError, match=msg): - frequencies.get_offset(freq) - - with pytest.raises(ValueError, match=msg): - date_range('2011-01-01', periods=5, freq=freq) From e9de5f3e159296a46bc62aa42a6225b67d8a4f10 Mon Sep 17 00:00:00 2001 From: Albert Villanova del Moral Date: Thu, 28 Feb 2019 16:53:29 +0100 Subject: [PATCH 170/215] BUG: Fix index type casting in read_json with orient='table' and float index (#25433) (#25434) --- doc/source/whatsnew/v0.25.0.rst | 2 ++ pandas/io/json/json.py | 28 +++++++++++++------ .../tests/io/json/test_json_table_schema.py | 11 ++------ pandas/tests/io/json/test_pandas.py | 23 +++++++++++++-- 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index a591c498d00c3..a6f7395f5177e 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -210,6 +210,8 @@ I/O - Fixed bug in missing text when using :meth:`to_clipboard` if copying utf-16 characters in Python 3 on Windows (:issue:`25040`) - Bug in :func:`read_json` for ``orient='table'`` when it tries to infer dtypes by default, which is not applicable as dtypes are already defined in the JSON schema (:issue:`21345`) +- Bug in :func:`read_json` for ``orient='table'`` and float index, as it infers index dtype by default, which is not applicable because index dtype is already defined in the JSON schema (:issue:`25433`) +- Bug in :func:`read_json` for ``orient='table'`` and string of float column names, as it makes a column name type conversion to Timestamp, which is not applicable because column names are already defined in the JSON schema (:issue:`25435`) - - - diff --git a/pandas/io/json/json.py b/pandas/io/json/json.py index 725e2d28ffd67..4bae067ee5196 100644 --- a/pandas/io/json/json.py +++ b/pandas/io/json/json.py @@ -227,7 +227,7 @@ def _write(self, obj, orient, double_precision, ensure_ascii, def read_json(path_or_buf=None, orient=None, typ='frame', dtype=None, - convert_axes=True, convert_dates=True, keep_default_dates=True, + convert_axes=None, convert_dates=True, keep_default_dates=True, numpy=False, precise_float=False, date_unit=None, encoding=None, lines=False, chunksize=None, compression='infer'): """ @@ -277,18 +277,25 @@ def read_json(path_or_buf=None, orient=None, typ='frame', dtype=None, 'table' as an allowed value for the ``orient`` argument typ : type of object to recover (series or frame), default 'frame' - dtype : boolean or dict, default True + dtype : boolean or dict, default None If True, infer dtypes; if a dict of column to dtype, then use those; if False, then don't infer dtypes at all, applies only to the data. - Not applicable with ``orient='table'``. + For all ``orient`` values except ``'table'``, default is True. - .. versionchanged:: 0.25 + .. versionchanged:: 0.25.0 - Not applicable with ``orient='table'``. + Not applicable for ``orient='table'``. - convert_axes : boolean, default True + convert_axes : boolean, default None Try to convert the axes to the proper dtypes. + + For all ``orient`` values except ``'table'``, default is True. + + .. versionchanged:: 0.25.0 + + Not applicable for ``orient='table'``. + convert_dates : boolean, default True List of columns to parse for dates; If True, then try to parse datelike columns default is True; a column label is datelike if @@ -417,8 +424,13 @@ def read_json(path_or_buf=None, orient=None, typ='frame', dtype=None, if orient == 'table' and dtype: raise ValueError("cannot pass both dtype and orient='table'") + if orient == 'table' and convert_axes: + raise ValueError("cannot pass both convert_axes and orient='table'") - dtype = orient != 'table' if dtype is None else dtype + if dtype is None and orient != 'table': + dtype = True + if convert_axes is None and orient != 'table': + convert_axes = True compression = _infer_compression(path_or_buf, compression) filepath_or_buffer, _, compression, should_close = get_filepath_or_buffer( @@ -692,7 +704,7 @@ def _try_convert_data(self, name, data, use_dtypes=True, # don't try to coerce, unless a force conversion if use_dtypes: - if self.dtype is False: + if not self.dtype: return data, False elif self.dtype is True: pass diff --git a/pandas/tests/io/json/test_json_table_schema.py b/pandas/tests/io/json/test_json_table_schema.py index 3002d1dfb5f8a..351b495e5d8fc 100644 --- a/pandas/tests/io/json/test_json_table_schema.py +++ b/pandas/tests/io/json/test_json_table_schema.py @@ -564,17 +564,10 @@ def test_multiindex(self, index_names): result = pd.read_json(out, orient="table") tm.assert_frame_equal(df, result) - @pytest.mark.parametrize("strict_check", [ - pytest.param(True, marks=pytest.mark.xfail), - False - ]) - def test_empty_frame_roundtrip(self, strict_check): + def test_empty_frame_roundtrip(self): # GH 21287 df = pd.DataFrame([], columns=['a', 'b', 'c']) expected = df.copy() out = df.to_json(orient='table') result = pd.read_json(out, orient='table') - # TODO: When DF coercion issue (#21345) is resolved tighten type checks - tm.assert_frame_equal(expected, result, - check_dtype=strict_check, - check_index_type=strict_check) + tm.assert_frame_equal(expected, result) diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py index fecd0f0572757..ed598b730d960 100644 --- a/pandas/tests/io/json/test_pandas.py +++ b/pandas/tests/io/json/test_pandas.py @@ -194,7 +194,7 @@ def _check_orient(df, orient, dtype=None, numpy=False, else: unser = unser.sort_index() - if dtype is False: + if not dtype: check_dtype = False if not convert_axes and df.index.dtype.type == np.datetime64: @@ -1202,6 +1202,16 @@ def test_data_frame_size_after_to_json(self): assert size_before == size_after + @pytest.mark.parametrize('index', [None, [1, 2], [1., 2.], ['a', 'b'], + ['1', '2'], ['1.', '2.']]) + @pytest.mark.parametrize('columns', [['a', 'b'], ['1', '2'], ['1.', '2.']]) + def test_from_json_to_json_table_index_and_columns(self, index, columns): + # GH25433 GH25435 + expected = DataFrame([[1, 2], [3, 4]], index=index, columns=columns) + dfjson = expected.to_json(orient='table') + result = pd.read_json(dfjson, orient='table') + assert_frame_equal(result, expected) + def test_from_json_to_json_table_dtypes(self): # GH21345 expected = pd.DataFrame({'a': [1, 2], 'b': [3., 4.], 'c': ['5', '6']}) @@ -1214,9 +1224,18 @@ def test_read_json_table_dtype_raises(self, dtype): # GH21345 df = pd.DataFrame({'a': [1, 2], 'b': [3., 4.], 'c': ['5', '6']}) dfjson = df.to_json(orient='table') - with pytest.raises(ValueError): + msg = "cannot pass both dtype and orient='table'" + with pytest.raises(ValueError, match=msg): pd.read_json(dfjson, orient='table', dtype=dtype) + def test_read_json_table_convert_axes_raises(self): + # GH25433 GH25435 + df = DataFrame([[1, 2], [3, 4]], index=[1., 2.], columns=['1.', '2.']) + dfjson = df.to_json(orient='table') + msg = "cannot pass both convert_axes and orient='table'" + with pytest.raises(ValueError, match=msg): + pd.read_json(dfjson, orient='table', convert_axes=True) + @pytest.mark.parametrize('data, expected', [ (DataFrame([[1, 2], [4, 5]], columns=['a', 'b']), {'columns': ['a', 'b'], 'data': [[1, 2], [4, 5]]}), From 169a56a33fb7f4fad447e70869599a99832eeee5 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Thu, 28 Feb 2019 09:38:47 -0800 Subject: [PATCH 171/215] BUG: Groupby.agg with reduction function with tz aware data (#25308) * BUG: Groupby.agg cannot reduce with tz aware data * Handle output always as UTC * Add whatsnew * isort and add another fixed groupby.first/last issue * bring condition at a higher level * Add try for _try_cast * Add comments * Don't pass the utc_dtype explicitly * Remove unused import * Use string dtype instead --- doc/source/whatsnew/v0.25.0.rst | 4 ++-- pandas/_libs/reduction.pyx | 4 +++- pandas/core/groupby/groupby.py | 19 +++++++++++++++++-- pandas/tests/groupby/aggregate/test_other.py | 15 +++++++++++++++ pandas/tests/groupby/test_nth.py | 20 ++++++++++++++++++++ 5 files changed, 57 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index a6f7395f5177e..f847c1d827186 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -229,8 +229,8 @@ Groupby/Resample/Rolling - Bug in :meth:`pandas.core.resample.Resampler.agg` with a timezone aware index where ``OverflowError`` would raise when passing a list of functions (:issue:`22660`) - Bug in :meth:`pandas.core.groupby.DataFrameGroupBy.nunique` in which the names of column levels were lost (:issue:`23222`) -- -- +- Bug in :func:`pandas.core.groupby.GroupBy.agg` when applying a aggregation function to timezone aware data (:issue:`23683`) +- Bug in :func:`pandas.core.groupby.GroupBy.first` and :func:`pandas.core.groupby.GroupBy.last` where timezone information would be dropped (:issue:`21603`) Reshaping diff --git a/pandas/_libs/reduction.pyx b/pandas/_libs/reduction.pyx index 507567cf480d7..517d59c399179 100644 --- a/pandas/_libs/reduction.pyx +++ b/pandas/_libs/reduction.pyx @@ -342,7 +342,9 @@ cdef class SeriesGrouper: index = None else: values = dummy.values - if dummy.dtype != self.arr.dtype: + # GH 23683: datetimetz types are equivalent to datetime types here + if (dummy.dtype != self.arr.dtype + and values.dtype != self.arr.dtype): raise ValueError('Dummy array must be same dtype') if not values.flags.contiguous: values = values.copy() diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py index c364f069bf53d..926da40deaff2 100644 --- a/pandas/core/groupby/groupby.py +++ b/pandas/core/groupby/groupby.py @@ -26,7 +26,8 @@ class providing the base-class of operations. from pandas.core.dtypes.cast import maybe_downcast_to_dtype from pandas.core.dtypes.common import ( - ensure_float, is_extension_array_dtype, is_numeric_dtype, is_scalar) + ensure_float, is_datetime64tz_dtype, is_extension_array_dtype, + is_numeric_dtype, is_scalar) from pandas.core.dtypes.missing import isna, notna from pandas.api.types import ( @@ -766,7 +767,21 @@ def _try_cast(self, result, obj, numeric_only=False): dtype = obj.dtype if not is_scalar(result): - if is_extension_array_dtype(dtype): + if is_datetime64tz_dtype(dtype): + # GH 23683 + # Prior results _may_ have been generated in UTC. + # Ensure we localize to UTC first before converting + # to the target timezone + try: + result = obj._values._from_sequence( + result, dtype='datetime64[ns, UTC]' + ) + result = result.astype(dtype) + except TypeError: + # _try_cast was called at a point where the result + # was already tz-aware + pass + elif is_extension_array_dtype(dtype): # The function can return something of any type, so check # if the type is compatible with the calling EA. try: diff --git a/pandas/tests/groupby/aggregate/test_other.py b/pandas/tests/groupby/aggregate/test_other.py index b5214b11bddcc..cacfdb7694de1 100644 --- a/pandas/tests/groupby/aggregate/test_other.py +++ b/pandas/tests/groupby/aggregate/test_other.py @@ -512,3 +512,18 @@ def test_agg_list_like_func(): expected = pd.DataFrame({'A': [str(x) for x in range(3)], 'B': [[str(x)] for x in range(3)]}) tm.assert_frame_equal(result, expected) + + +def test_agg_lambda_with_timezone(): + # GH 23683 + df = pd.DataFrame({ + 'tag': [1, 1], + 'date': [ + pd.Timestamp('2018-01-01', tz='UTC'), + pd.Timestamp('2018-01-02', tz='UTC')] + }) + result = df.groupby('tag').agg({'date': lambda e: e.head(1)}) + expected = pd.DataFrame([pd.Timestamp('2018-01-01', tz='UTC')], + index=pd.Index([1], name='tag'), + columns=['date']) + tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/groupby/test_nth.py b/pandas/tests/groupby/test_nth.py index 255d9a8acf2d0..7a3d189d3020e 100644 --- a/pandas/tests/groupby/test_nth.py +++ b/pandas/tests/groupby/test_nth.py @@ -278,6 +278,26 @@ def test_first_last_tz(data, expected_first, expected_last): assert_frame_equal(result, expected[['id', 'time']]) +@pytest.mark.parametrize('method, ts, alpha', [ + ['first', Timestamp('2013-01-01', tz='US/Eastern'), 'a'], + ['last', Timestamp('2013-01-02', tz='US/Eastern'), 'b'] +]) +def test_first_last_tz_multi_column(method, ts, alpha): + # GH 21603 + df = pd.DataFrame({'group': [1, 1, 2], + 'category_string': pd.Series(list('abc')).astype( + 'category'), + 'datetimetz': pd.date_range('20130101', periods=3, + tz='US/Eastern')}) + result = getattr(df.groupby('group'), method)() + expepcted = pd.DataFrame({'category_string': [alpha, 'c'], + 'datetimetz': [ts, + Timestamp('2013-01-03', + tz='US/Eastern')]}, + index=pd.Index([1, 2], name='group')) + assert_frame_equal(result, expepcted) + + def test_nth_multi_index_as_expected(): # PR 9090, related to issue 8979 # test nth on MultiIndex From ae1ab8993c5152ee14a1c4bb3577d268fb3b90e3 Mon Sep 17 00:00:00 2001 From: Gordon Blackadder Date: Thu, 28 Feb 2019 20:00:30 +0000 Subject: [PATCH 172/215] DOC: Fix docstring for read_sql_table (#25465) --- pandas/io/sql.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/pandas/io/sql.py b/pandas/io/sql.py index aaface5415384..02fba52eac7f7 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -182,26 +182,29 @@ def execute(sql, con, cur=None, params=None): def read_sql_table(table_name, con, schema=None, index_col=None, coerce_float=True, parse_dates=None, columns=None, chunksize=None): - """Read SQL database table into a DataFrame. + """ + Read SQL database table into a DataFrame. Given a table name and a SQLAlchemy connectable, returns a DataFrame. This function does not support DBAPI connections. Parameters ---------- - table_name : string + table_name : str Name of SQL table in database. - con : SQLAlchemy connectable (or database string URI) + con : SQLAlchemy connectable or str + A database URI could be provided as as str. SQLite DBAPI connection mode not supported. - schema : string, default None + schema : str, default None Name of SQL schema in database to query (if database flavor supports this). Uses default schema if None (default). - index_col : string or list of strings, optional, default: None + index_col : str or list of str, optional, default: None Column(s) to set as index(MultiIndex). - coerce_float : boolean, default True + coerce_float : bool, default True Attempts to convert values of non-string, non-numeric objects (like decimal.Decimal) to floating point. Can result in loss of Precision. - parse_dates : list or dict, default: None + parse_dates : list or dict, default None + The behavior is as follows: - List of column names to parse as dates. - Dict of ``{column_name: format string}`` where format string is strftime compatible in case of parsing string times or is one of @@ -210,8 +213,8 @@ def read_sql_table(table_name, con, schema=None, index_col=None, to the keyword arguments of :func:`pandas.to_datetime` Especially useful with databases without native Datetime support, such as SQLite. - columns : list, default: None - List of column names to select from SQL table + columns : list, default None + List of column names to select from SQL table. chunksize : int, default None If specified, returns an iterator where `chunksize` is the number of rows to include in each chunk. @@ -219,15 +222,21 @@ def read_sql_table(table_name, con, schema=None, index_col=None, Returns ------- DataFrame + A SQL table is returned as two-dimensional data structure with labeled + axes. See Also -------- read_sql_query : Read SQL query into a DataFrame. - read_sql + read_sql : Read SQL query or database table into a DataFrame. Notes ----- Any datetime values with time zone information will be converted to UTC. + + Examples + -------- + >>> pd.read_sql_table('table_name', 'postgres:///db_name') # doctest:+SKIP """ con = _engine_builder(con) From db978c716369064421c5ca71bd26002e0021e0d1 Mon Sep 17 00:00:00 2001 From: Kaiqi Dong Date: Thu, 28 Feb 2019 22:21:39 +0100 Subject: [PATCH 173/215] ENH: Add Series.str.casefold (#25419) --- doc/source/reference/series.rst | 1 + doc/source/user_guide/text.rst | 1 + doc/source/whatsnew/v0.25.0.rst | 1 + pandas/core/strings.py | 19 +++++++++++++------ pandas/tests/test_strings.py | 11 ++++++++++- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/doc/source/reference/series.rst b/doc/source/reference/series.rst index a6ac40b5203bf..b406893e3414a 100644 --- a/doc/source/reference/series.rst +++ b/doc/source/reference/series.rst @@ -409,6 +409,7 @@ strings and apply several methods to it. These can be accessed like :template: autosummary/accessor_method.rst Series.str.capitalize + Series.str.casefold Series.str.cat Series.str.center Series.str.contains diff --git a/doc/source/user_guide/text.rst b/doc/source/user_guide/text.rst index e4f60a761750d..6f21a7d9beb36 100644 --- a/doc/source/user_guide/text.rst +++ b/doc/source/user_guide/text.rst @@ -600,6 +600,7 @@ Method Summary :meth:`~Series.str.partition`;Equivalent to ``str.partition`` :meth:`~Series.str.rpartition`;Equivalent to ``str.rpartition`` :meth:`~Series.str.lower`;Equivalent to ``str.lower`` + :meth:`~Series.str.casefold`;Equivalent to ``str.casefold`` :meth:`~Series.str.upper`;Equivalent to ``str.upper`` :meth:`~Series.str.find`;Equivalent to ``str.find`` :meth:`~Series.str.rfind`;Equivalent to ``str.rfind`` diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index f847c1d827186..d1f1ea862110e 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -22,6 +22,7 @@ Other Enhancements - Indexing of ``DataFrame`` and ``Series`` now accepts zerodim ``np.ndarray`` (:issue:`24919`) - :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`) - :meth:`DataFrame.at_time` and :meth:`Series.at_time` now support :meth:`datetime.time` objects with timezones (:issue:`24043`) +- ``Series.str`` has gained :meth:`Series.str.casefold` method to removes all case distinctions present in a string (:issue:`25405`) - :meth:`DataFrame.set_index` now works for instances of ``abc.Iterator``, provided their output is of the same length as the calling frame (:issue:`22484`, :issue:`24984`) - :meth:`DatetimeIndex.union` now supports the ``sort`` argument. The behaviour of the sort parameter matches that of :meth:`Index.union` (:issue:`24994`) - diff --git a/pandas/core/strings.py b/pandas/core/strings.py index cc7a4db515c42..9577b07360f65 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -2926,7 +2926,7 @@ def rindex(self, sub, start=0, end=None): _shared_docs['casemethods'] = (""" Convert strings in the Series/Index to %(type)s. - + %(version)s Equivalent to :meth:`str.%(method)s`. Returns @@ -2943,6 +2943,7 @@ def rindex(self, sub, start=0, end=None): remaining to lowercase. Series.str.swapcase : Converts uppercase to lowercase and lowercase to uppercase. + Series.str.casefold: Removes all case distinctions in the string. Examples -------- @@ -2989,12 +2990,15 @@ def rindex(self, sub, start=0, end=None): 3 sWaPcAsE dtype: object """) - _shared_docs['lower'] = dict(type='lowercase', method='lower') - _shared_docs['upper'] = dict(type='uppercase', method='upper') - _shared_docs['title'] = dict(type='titlecase', method='title') + _shared_docs['lower'] = dict(type='lowercase', method='lower', version='') + _shared_docs['upper'] = dict(type='uppercase', method='upper', version='') + _shared_docs['title'] = dict(type='titlecase', method='title', version='') _shared_docs['capitalize'] = dict(type='be capitalized', - method='capitalize') - _shared_docs['swapcase'] = dict(type='be swapcased', method='swapcase') + method='capitalize', version='') + _shared_docs['swapcase'] = dict(type='be swapcased', method='swapcase', + version='') + _shared_docs['casefold'] = dict(type='be casefolded', method='casefold', + version='\n .. versionadded:: 0.25.0\n') lower = _noarg_wrapper(lambda x: x.lower(), docstring=_shared_docs['casemethods'] % _shared_docs['lower']) @@ -3010,6 +3014,9 @@ def rindex(self, sub, start=0, end=None): swapcase = _noarg_wrapper(lambda x: x.swapcase(), docstring=_shared_docs['casemethods'] % _shared_docs['swapcase']) + casefold = _noarg_wrapper(lambda x: x.casefold(), + docstring=_shared_docs['casemethods'] % + _shared_docs['casefold']) _shared_docs['ismethods'] = (""" Check whether all characters in each string are %(type)s. diff --git a/pandas/tests/test_strings.py b/pandas/tests/test_strings.py index bbcdc24f58f9b..40a83f90c8dfd 100644 --- a/pandas/tests/test_strings.py +++ b/pandas/tests/test_strings.py @@ -76,7 +76,7 @@ def assert_series_or_index_equal(left, right): 'len', 'lower', 'lstrip', 'partition', 'rpartition', 'rsplit', 'rstrip', 'slice', 'slice_replace', 'split', - 'strip', 'swapcase', 'title', 'upper' + 'strip', 'swapcase', 'title', 'upper', 'casefold' ], [()] * 100, [{}] * 100)) ids, _, _ = zip(*_any_string_method) # use method name as fixture-id @@ -3440,3 +3440,12 @@ def test_method_on_bytes(self): expected = Series(np.array( ['ad', 'be', 'cf'], 'S2').astype(object)) tm.assert_series_equal(result, expected) + + @pytest.mark.skipif(compat.PY2, reason='not in python2') + def test_casefold(self): + # GH25405 + expected = Series(['ss', NA, 'case', 'ssd']) + s = Series(['ß', NA, 'case', 'ßd']) + result = s.str.casefold() + + tm.assert_series_equal(result, expected) From 0a61ecdf6b4ea61a67afb4e3862df79adc07053a Mon Sep 17 00:00:00 2001 From: Thein Oo Date: Thu, 28 Feb 2019 20:46:43 -0500 Subject: [PATCH 174/215] Fix PR10 error and Clean up docstrings from functions related to RT05 errors (#25132) --- ci/code_checks.sh | 4 +- pandas/core/algorithms.py | 13 +++-- pandas/core/arrays/categorical.py | 14 ++--- pandas/core/arrays/datetimelike.py | 6 +-- pandas/core/frame.py | 7 +-- pandas/core/generic.py | 32 +++++++---- pandas/core/indexes/base.py | 23 ++++---- pandas/core/indexes/multi.py | 6 +-- pandas/core/series.py | 2 + pandas/io/excel/_base.py | 2 +- pandas/plotting/_core.py | 86 +++++++++++++++--------------- pandas/plotting/_misc.py | 12 ++--- pandas/tseries/frequencies.py | 6 +-- 13 files changed, 118 insertions(+), 95 deletions(-) diff --git a/ci/code_checks.sh b/ci/code_checks.sh index ac6aade106ce6..c4840f1e836c4 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -241,8 +241,8 @@ fi ### DOCSTRINGS ### if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then - MSG='Validate docstrings (GL06, GL07, GL09, SS04, PR03, PR05, PR10, EX04, RT04, RT05, SS05, SA05)' ; echo $MSG - $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,PR03,PR04,PR05,EX04,RT04,RT05,SS05,SA05 + MSG='Validate docstrings (GL06, GL07, GL09, SS04, SS05, PR03, PR04, PR05, PR10, EX04, RT04, RT05, SA05)' ; echo $MSG + $BASE_DIR/scripts/validate_docstrings.py --format=azure --errors=GL06,GL07,GL09,SS04,SS05,PR03,PR04,PR05,PR10,EX04,RT04,RT05,SA05 RET=$(($RET + $?)) ; echo $MSG "DONE" fi diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index b056a357d0a51..4a71951e2435e 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -288,10 +288,15 @@ def unique(values): Returns ------- - unique values. - If the input is an Index, the return is an Index - If the input is a Categorical dtype, the return is a Categorical - If the input is a Series/ndarray, the return will be an ndarray. + numpy.ndarray or ExtensionArray + + The return can be: + + * Index : when the input is an Index + * Categorical : when the input is a Categorical dtype + * ndarray : when the input is a Series/ndarray + + Return numpy.ndarray or ExtensionArray. See Also -------- diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 79e565df94eae..37a24a54be8b1 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1289,7 +1289,7 @@ def __array__(self, dtype=None): Returns ------- - values : numpy array + numpy.array A numpy array of either the specified dtype or, if dtype==None (default), the same dtype as categorical.categories.dtype. @@ -1499,9 +1499,9 @@ def get_values(self): Returns ------- - values : numpy array + numpy.array A numpy array of the same dtype as categorical.categories.dtype or - Index if datetime / periods + Index if datetime / periods. """ # if we are a datetime and period index, return Index to keep metadata if is_datetimelike(self.categories): @@ -1540,7 +1540,7 @@ def argsort(self, *args, **kwargs): Returns ------- - argsorted : numpy array + numpy.array See Also -------- @@ -1593,7 +1593,7 @@ def sort_values(self, inplace=False, ascending=True, na_position='last'): Returns ------- - y : Categorical or None + Categorical or None See Also -------- @@ -1667,7 +1667,7 @@ def _values_for_rank(self): Returns ------- - numpy array + numpy.array """ from pandas import Series @@ -1695,7 +1695,7 @@ def ravel(self, order='C'): Returns ------- - raveled : numpy array + numpy.array """ return np.array(self) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 84536ac72a455..94668c74c1693 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -144,7 +144,7 @@ def strftime(self, date_format): Return an Index of formatted strings specified by date_format, which supports the same string format as the python standard library. Details of the string format can be found in `python string format - doc <%(URL)s>`__ + doc <%(URL)s>`__. Parameters ---------- @@ -748,7 +748,7 @@ def _maybe_mask_results(self, result, fill_value=iNaT, convert=None): mask the result if needed, convert to the provided dtype if its not None - This is an internal routine + This is an internal routine. """ if self._hasnans: @@ -1047,7 +1047,7 @@ def _sub_period_array(self, other): Returns ------- result : np.ndarray[object] - Array of DateOffset objects; nulls represented by NaT + Array of DateOffset objects; nulls represented by NaT. """ if not is_period_dtype(self): raise TypeError("cannot subtract {dtype}-dtype from {cls}" diff --git a/pandas/core/frame.py b/pandas/core/frame.py index a40733b7076b0..6b4d95055d06d 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2696,7 +2696,7 @@ def get_value(self, index, col, takeable=False): Returns ------- - scalar value + scalar """ warnings.warn("get_value is deprecated and will be removed " @@ -2736,7 +2736,7 @@ def set_value(self, index, col, value, takeable=False): ---------- index : row label col : column label - value : scalar value + value : scalar takeable : interpret the index/col as indexers, default False Returns @@ -6896,7 +6896,7 @@ def round(self, decimals=0, *args, **kwargs): Returns ------- - DataFrame : + DataFrame A DataFrame with the affected columns rounded to the specified number of decimal places. @@ -7000,6 +7000,7 @@ def corr(self, method='pearson', min_periods=1): * spearman : Spearman rank correlation * callable: callable with input two 1d ndarrays and returning a float + .. versionadded:: 0.24.0 min_periods : int, optional diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 523543ada235c..eb427a42a249b 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -2807,14 +2807,17 @@ def to_latex(self, buf=None, columns=None, col_space=None, header=True, defaults to 'ascii' on Python 2 and 'utf-8' on Python 3. decimal : str, default '.' Character recognized as decimal separator, e.g. ',' in Europe. + .. versionadded:: 0.18.0 multicolumn : bool, default True Use \multicolumn to enhance MultiIndex columns. The default will be read from the config module. + .. versionadded:: 0.20.0 multicolumn_format : str, default 'l' The alignment for multicolumns, similar to `column_format` The default will be read from the config module. + .. versionadded:: 0.20.0 multirow : bool, default False Use \multirow to enhance MultiIndex rows. Requires adding a @@ -2822,6 +2825,7 @@ def to_latex(self, buf=None, columns=None, col_space=None, header=True, centered labels (instead of top-aligned) across the contained rows, separating groups via clines. The default will be read from the pandas config module. + .. versionadded:: 0.20.0 Returns @@ -4948,11 +4952,15 @@ def pipe(self, func, *args, **kwargs): Returns ------- - DataFrame, Series or scalar - If DataFrame.agg is called with a single function, returns a Series - If DataFrame.agg is called with several functions, returns a DataFrame - If Series.agg is called with single function, returns a scalar - If Series.agg is called with several functions, returns a Series. + scalar, Series or DataFrame + + The return can be: + + * scalar : when Series.agg is called with single function + * Series : when DataFrame.agg is called with a single function + * DataFrame : when DataFrame.agg is called with several functions + + Return scalar, Series or DataFrame. %(see_also)s @@ -6879,11 +6887,15 @@ def asof(self, where, subset=None): ------- scalar, Series, or DataFrame - Scalar : when `self` is a Series and `where` is a scalar. - Series: when `self` is a Series and `where` is an array-like, - or when `self` is a DataFrame and `where` is a scalar. - DataFrame : when `self` is a DataFrame and `where` is an - array-like. + The return can be: + + * scalar : when `self` is a Series and `where` is a scalar + * Series: when `self` is a Series and `where` is an array-like, + or when `self` is a DataFrame and `where` is a scalar + * DataFrame : when `self` is a DataFrame and `where` is an + array-like + + Return scalar, Series, or DataFrame. See Also -------- diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 1cdacc908b663..dee181fc1c569 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1443,7 +1443,7 @@ def sortlevel(self, level=None, ascending=True, sort_remaining=None): Returns ------- - sorted_index : Index + Index """ return self.sort_values(return_indexer=True, ascending=ascending) @@ -1461,7 +1461,7 @@ def _get_level_values(self, level): Returns ------- - values : Index + Index Calling object, as there is only one level in the Index. See Also @@ -1506,7 +1506,7 @@ def droplevel(self, level=0): Returns ------- - index : Index or MultiIndex + Index or MultiIndex """ if not isinstance(level, (tuple, list)): level = [level] @@ -1558,11 +1558,11 @@ def droplevel(self, level=0): Returns ------- grouper : Index - Index of values to group on + Index of values to group on. labels : ndarray of int or None - Array of locations in level_index + Array of locations in level_index. uniques : Index or None - Index of unique values for level + Index of unique values for level. """ @Appender(_index_shared_docs['_get_grouper_for_level']) @@ -2972,9 +2972,10 @@ def _convert_listlike_indexer(self, keyarr, kind=None): Returns ------- - tuple (indexer, keyarr) - indexer is an ndarray or None if cannot convert - keyarr are tuple-safe keys + indexer : numpy.ndarray or None + Return an ndarray or None if cannot convert. + keyarr : numpy.ndarray + Return tuple-safe keys. """ if isinstance(keyarr, Index): keyarr = self._convert_index_indexer(keyarr) @@ -3158,9 +3159,9 @@ def _reindex_non_unique(self, target): Returns ------- new_index : pd.Index - Resulting index + Resulting index. indexer : np.ndarray or None - Indices of output values in original index + Indices of output values in original index. """ diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 492d28476e1f0..616c17cd16f9a 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -61,7 +61,7 @@ def _codes_to_ints(self, codes): Returns ------ int_keys : scalar or 1-dimensional array, of dtype uint64 - Integer(s) representing one combination (each) + Integer(s) representing one combination (each). """ # Shift the representation of each level by the pre-calculated number # of bits: @@ -101,7 +101,7 @@ def _codes_to_ints(self, codes): Returns ------ int_keys : int, or 1-dimensional array of dtype object - Integer(s) representing one combination (each) + Integer(s) representing one combination (each). """ # Shift the representation of each level by the pre-calculated number @@ -2195,7 +2195,7 @@ def reindex(self, target, method=None, level=None, limit=None, new_index : pd.MultiIndex Resulting index indexer : np.ndarray or None - Indices of output values in original index + Indices of output values in original index. """ # GH6552: preserve names when reindexing to non-named target diff --git a/pandas/core/series.py b/pandas/core/series.py index ad7c6af21f637..cada6663ce651 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1669,6 +1669,8 @@ def unique(self): * Sparse * IntegerNA + See Examples section. + Examples -------- >>> pd.Series([2, 1, 3, 3], name='A').unique() diff --git a/pandas/io/excel/_base.py b/pandas/io/excel/_base.py index 8f7bf8e0466f9..c6d390692c789 100644 --- a/pandas/io/excel/_base.py +++ b/pandas/io/excel/_base.py @@ -510,7 +510,7 @@ class ExcelWriter(object): mode : {'w' or 'a'}, default 'w' File mode to use (write or append). - .. versionadded:: 0.24.0 + .. versionadded:: 0.24.0 Attributes ---------- diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index 2c672f235f1e1..48d870bfc2e03 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -1413,7 +1413,7 @@ def orientation(self): Returns ------- - axes : matplotlib.axes.Axes or numpy.ndarray of them + matplotlib.axes.Axes or numpy.ndarray of them See Also -------- @@ -1809,26 +1809,26 @@ def _plot(data, x=None, y=None, subplots=False, Allows plotting of one column versus another""" series_coord = "" -df_unique = """stacked : boolean, default False in line and +df_unique = """stacked : bool, default False in line and bar plots, and True in area plot. If True, create stacked plot. - sort_columns : boolean, default False + sort_columns : bool, default False Sort column names to determine plot ordering - secondary_y : boolean or sequence, default False + secondary_y : bool or sequence, default False Whether to plot on the secondary y-axis If a list/tuple, which columns to plot on secondary y-axis""" series_unique = """label : label argument to provide to plot - secondary_y : boolean or sequence of ints, default False + secondary_y : bool or sequence of ints, default False If True then y-axis will be on the right""" df_ax = """ax : matplotlib axes object, default None - subplots : boolean, default False + subplots : bool, default False Make separate subplots for each column - sharex : boolean, default True if ax is None else False + sharex : bool, default True if ax is None else False In case subplots=True, share x axis and set some x axis labels to invisible; defaults to True if ax is None otherwise False if an ax is passed in; Be aware, that passing in both an ax and sharex=True will alter all x axis labels for all axis in a figure! - sharey : boolean, default False + sharey : bool, default False In case subplots=True, share y axis and set some y axis labels to invisible layout : tuple (optional) @@ -1882,23 +1882,23 @@ def _plot(data, x=None, y=None, subplots=False, %(klass_kind)s %(klass_ax)s figsize : a tuple (width, height) in inches - use_index : boolean, default True + use_index : bool, default True Use index as ticks for x axis title : string or list Title to use for the plot. If a string is passed, print the string at the top of the figure. If a list is passed and `subplots` is True, print each item in the list above the corresponding subplot. - grid : boolean, default None (matlab style default) + grid : bool, default None (matlab style default) Axis grid lines legend : False/True/'reverse' Place legend on axis subplots style : list or dict matplotlib line style per column - logx : boolean, default False + logx : bool, default False Use log scaling on x axis - logy : boolean, default False + logy : bool, default False Use log scaling on y axis - loglog : boolean, default False + loglog : bool, default False Use log scaling on both x and y axes xticks : sequence Values to use for the xticks @@ -1913,12 +1913,12 @@ def _plot(data, x=None, y=None, subplots=False, colormap : str or matplotlib colormap object, default None Colormap to select colors from. If string, load colormap with that name from matplotlib. - colorbar : boolean, optional + colorbar : bool, optional If True, plot colorbar (only relevant for 'scatter' and 'hexbin' plots) position : float Specify relative alignments for bar plot layout. From 0 (left/bottom-end) to 1 (right/top-end). Default is 0.5 (center) - table : boolean, Series or DataFrame, default False + table : bool, Series or DataFrame, default False If True, draw a table using the data in the DataFrame and the data will be transposed to meet matplotlib's default layout. If a Series or DataFrame is passed, use passed data to draw a table. @@ -1927,7 +1927,7 @@ def _plot(data, x=None, y=None, subplots=False, detail. xerr : same types as yerr. %(klass_unique)s - mark_right : boolean, default True + mark_right : bool, default True When using a secondary_y axis, automatically mark the column labels with "(right)" in the legend `**kwds` : keywords @@ -1935,7 +1935,7 @@ def _plot(data, x=None, y=None, subplots=False, Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them Notes ----- @@ -2025,7 +2025,7 @@ def plot_series(data, kind='line', ax=None, # Series unique rot : int or float, default 0 The rotation angle of labels (in degrees) with respect to the screen coordinate system. - grid : boolean, default True + grid : bool, default True Setting this to True will show the grid. figsize : A tuple (width, height) in inches The size of the figure to create in matplotlib. @@ -2070,6 +2070,7 @@ def plot_series(data, kind='line', ax=None, # Series unique * :class:`~pandas.Series` * :class:`~numpy.array` (for ``return_type = None``) + Return Series or numpy.array. Use ``return_type='dict'`` when you want to tweak the appearance of the lines after plotting. In this case a dict containing the Lines @@ -2272,7 +2273,7 @@ def scatter_plot(data, x, y, by=None, ax=None, figsize=None, grid=False, Returns ------- - fig : matplotlib.Figure + matplotlib.Figure """ import matplotlib.pyplot as plt @@ -2321,7 +2322,7 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, If passed, will be used to limit data to a subset of columns. by : object, optional If passed, then used to form histograms for separate groups. - grid : boolean, default True + grid : bool, default True Whether to show axis grid lines. xlabelsize : int, default None If specified changes the x-axis label size. @@ -2335,13 +2336,13 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, y labels rotated 90 degrees clockwise. ax : Matplotlib axes object, default None The axes to plot the histogram on. - sharex : boolean, default True if ax is None else False + sharex : bool, default True if ax is None else False In case subplots=True, share x axis and set some x axis labels to invisible; defaults to True if ax is None otherwise False if an ax is passed in. Note that passing in both an ax and sharex=True will alter all x axis labels for all subplots in a figure. - sharey : boolean, default False + sharey : bool, default False In case subplots=True, share y axis and set some y axis labels to invisible. figsize : tuple @@ -2360,7 +2361,7 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, Returns ------- - axes : matplotlib.AxesSubplot or numpy.ndarray of them + matplotlib.AxesSubplot or numpy.ndarray of them See Also -------- @@ -2428,7 +2429,7 @@ def hist_series(self, by=None, ax=None, grid=True, xlabelsize=None, If passed, then used to form histograms for separate groups ax : matplotlib axis object If not passed, uses gca() - grid : boolean, default True + grid : bool, default True Whether to show axis grid lines xlabelsize : int, default None If specified changes the x-axis label size @@ -2510,15 +2511,15 @@ def grouped_hist(data, column=None, by=None, ax=None, bins=50, figsize=None, bins : int, default 50 figsize : tuple, optional layout : optional - sharex : boolean, default False - sharey : boolean, default False + sharex : bool, default False + sharey : bool, default False rot : int, default 90 grid : bool, default True kwargs : dict, keyword arguments passed to matplotlib.Axes.hist Returns ------- - axes : collection of Matplotlib Axes + collection of Matplotlib Axes """ _raise_if_no_mpl() _converter._WARN = False @@ -2752,7 +2753,7 @@ def line(self, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them Examples -------- @@ -2777,7 +2778,7 @@ def bar(self, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them """ return self(kind='bar', **kwds) @@ -2793,7 +2794,7 @@ def barh(self, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them """ return self(kind='barh', **kwds) @@ -2809,7 +2810,7 @@ def box(self, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them """ return self(kind='box', **kwds) @@ -2827,7 +2828,7 @@ def hist(self, bins=10, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them """ return self(kind='hist', bins=bins, **kwds) @@ -2886,7 +2887,7 @@ def area(self, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them """ return self(kind='area', **kwds) @@ -2902,7 +2903,7 @@ def pie(self, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them """ return self(kind='pie', **kwds) @@ -2962,8 +2963,8 @@ def line(self, x=None, y=None, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or :class:`numpy.ndarray` - Returns an ndarray when ``subplots=True``. + :class:`matplotlib.axes.Axes` or :class:`numpy.ndarray` + Return an ndarray when ``subplots=True``. See Also -------- @@ -3027,7 +3028,7 @@ def bar(self, x=None, y=None, **kwds): Returns ------- - axes : matplotlib.axes.Axes or np.ndarray of them + matplotlib.axes.Axes or np.ndarray of them An ndarray is returned with one :class:`matplotlib.axes.Axes` per column when ``subplots=True``. @@ -3109,7 +3110,7 @@ def barh(self, x=None, y=None, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them. + :class:`matplotlib.axes.Axes` or numpy.ndarray of them See Also -------- @@ -3196,7 +3197,7 @@ def box(self, by=None, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them See Also -------- @@ -3239,7 +3240,8 @@ def hist(self, by=None, bins=10, **kwds): Returns ------- - axes : matplotlib.AxesSubplot histogram. + class:`matplotlib.AxesSubplot` + Return a histogram plot. See Also -------- @@ -3403,7 +3405,7 @@ def pie(self, y=None, **kwds): Returns ------- - axes : matplotlib.axes.Axes or np.ndarray of them. + matplotlib.axes.Axes or np.ndarray of them A NumPy array is returned when `subplots` is True. See Also @@ -3479,7 +3481,7 @@ def scatter(self, x, y, s=None, c=None, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` or numpy.ndarray of them + :class:`matplotlib.axes.Axes` or numpy.ndarray of them See Also -------- diff --git a/pandas/plotting/_misc.py b/pandas/plotting/_misc.py index 21592a5b4a0a1..5171ea68fd497 100644 --- a/pandas/plotting/_misc.py +++ b/pandas/plotting/_misc.py @@ -178,7 +178,7 @@ def radviz(frame, class_column, ax=None, color=None, colormap=None, **kwds): Returns ------- - axes : :class:`matplotlib.axes.Axes` + class:`matplotlib.axes.Axes` See Also -------- @@ -302,7 +302,7 @@ def andrews_curves(frame, class_column, ax=None, samples=200, color=None, Returns ------- - ax : Matplotlib axis object + class:`matplotlip.axis.Axes` """ from math import sqrt, pi @@ -389,7 +389,7 @@ def bootstrap_plot(series, fig=None, size=50, samples=500, **kwds): Returns ------- - fig : matplotlib.figure.Figure + matplotlib.figure.Figure Matplotlib figure. See Also @@ -490,7 +490,7 @@ def parallel_coordinates(frame, class_column, cols=None, ax=None, color=None, Returns ------- - ax: matplotlib axis object + class:`matplotlib.axis.Axes` Examples -------- @@ -579,7 +579,7 @@ def lag_plot(series, lag=1, ax=None, **kwds): Returns ------- - ax: Matplotlib axis object + class:`matplotlib.axis.Axes` """ import matplotlib.pyplot as plt @@ -610,7 +610,7 @@ def autocorrelation_plot(series, ax=None, **kwds): Returns: ----------- - ax: Matplotlib axis object + class:`matplotlib.axis.Axes` """ import matplotlib.pyplot as plt n = len(series) diff --git a/pandas/tseries/frequencies.py b/pandas/tseries/frequencies.py index 4802447cbc99d..1b782b430a1a7 100644 --- a/pandas/tseries/frequencies.py +++ b/pandas/tseries/frequencies.py @@ -67,7 +67,7 @@ def to_offset(freq): Returns ------- - delta : DateOffset + DateOffset None if freq is None. Raises @@ -214,7 +214,7 @@ def infer_freq(index, warn=True): Returns ------- - freq : string or None + str or None None if no discernible frequency TypeError if the index is not datetime-like ValueError if there are less than three values. @@ -300,7 +300,7 @@ def get_freq(self): Returns ------- - freqstr : str or None + str or None """ if not self.is_monotonic or not self.index._is_unique: return None From 9bb98b8832d3eb90ae592cf8f1275c35edea778e Mon Sep 17 00:00:00 2001 From: Matthew Roeschke Date: Fri, 1 Mar 2019 04:47:30 -0800 Subject: [PATCH 175/215] Fix unreliable test (#25496) --- pandas/tests/indexes/datetimes/test_datetime.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_datetime.py b/pandas/tests/indexes/datetimes/test_datetime.py index a3ee5fe39769f..c7147e6fe7063 100644 --- a/pandas/tests/indexes/datetimes/test_datetime.py +++ b/pandas/tests/indexes/datetimes/test_datetime.py @@ -100,8 +100,7 @@ def test_hash_error(self): def test_stringified_slice_with_tz(self): # GH#2658 - import datetime - start = datetime.datetime.now() + start = '2013-01-07' idx = date_range(start=start, freq="1d", periods=10, tz='US/Eastern') df = DataFrame(lrange(10), index=idx) df["2013-01-14 23:44:34.437768-05:00":] # no exception here From 011f0a6ebacb13619bf8225b3bc2370872b85769 Mon Sep 17 00:00:00 2001 From: Max van Deursen Date: Fri, 1 Mar 2019 13:57:49 +0100 Subject: [PATCH 176/215] DOC: Clarifying doc/make.py --single parameter (#25482) --- doc/make.py | 8 +++++--- doc/source/development/contributing.rst | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/make.py b/doc/make.py index 438c4a04a3f08..8b2a77987e663 100755 --- a/doc/make.py +++ b/doc/make.py @@ -294,14 +294,16 @@ def main(): help='number of jobs used by sphinx-build') argparser.add_argument('--no-api', default=False, - help='ommit api and autosummary', + help='omit api and autosummary', action='store_true') argparser.add_argument('--single', metavar='FILENAME', type=str, default=None, - help=('filename of section or method name to ' - 'compile, e.g. "indexing", "DataFrame.join"')) + help=('filename (relative to the "source" folder)' + ' of section or method name to compile, e.g. ' + '"development/contributing.rst",' + ' "ecosystem.rst", "pandas.DataFrame.join"')) argparser.add_argument('--python-path', type=str, default=os.path.dirname(DOC_PATH), diff --git a/doc/source/development/contributing.rst b/doc/source/development/contributing.rst index 027f2d90bbb73..a87a66cd08ad1 100644 --- a/doc/source/development/contributing.rst +++ b/doc/source/development/contributing.rst @@ -428,10 +428,10 @@ reducing the turn-around time for checking your changes. python make.py clean python make.py --no-api - # compile the docs with only a single - # section, that which is in indexing.rst + # compile the docs with only a single section, relative to the "source" folder. + # For example, compiling only this guide (docs/source/development/contributing.rst) python make.py clean - python make.py --single indexing + python make.py --single development/contributing.rst # compile the reference docs for a single function python make.py clean From 1f8d7e07d7188e217145b45a8746dcc624c5f06c Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Fri, 1 Mar 2019 16:52:07 +0000 Subject: [PATCH 177/215] fix MacPython / pandas-wheels ci failures (#25505) --- pandas/tests/test_algos.py | 2 +- pandas/tests/test_sorting.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/tests/test_algos.py b/pandas/tests/test_algos.py index c56bf944699e2..3f75c508d22f9 100644 --- a/pandas/tests/test_algos.py +++ b/pandas/tests/test_algos.py @@ -229,7 +229,7 @@ def test_complex_sorting(self): # gh 12666 - check no segfault x17 = np.array([complex(i) for i in range(17)], dtype=object) - msg = ("'<' not supported between instances of 'complex' and" + msg = (r"'(<|>)' not supported between instances of 'complex' and" r" 'complex'|" r"unorderable types: complex\(\) > complex\(\)") with pytest.raises(TypeError, match=msg): diff --git a/pandas/tests/test_sorting.py b/pandas/tests/test_sorting.py index e83bdb1af9121..2a64947042979 100644 --- a/pandas/tests/test_sorting.py +++ b/pandas/tests/test_sorting.py @@ -409,9 +409,9 @@ def test_mixed_integer_from_list(self): def test_unsortable(self): # GH 13714 arr = np.array([1, 2, datetime.now(), 0, 3], dtype=object) - msg = ("'<' not supported between instances of 'datetime.datetime'" - r" and 'int'|" - r"unorderable types: int\(\) > datetime.datetime\(\)") + msg = (r"'(<|>)' not supported between instances of" + r" 'datetime\.datetime' and 'int'|" + r"unorderable types: int\(\) > datetime\.datetime\(\)") if compat.PY2: # RuntimeWarning: tp_compare didn't return -1 or -2 for exception with warnings.catch_warnings(): From 1d3b4a57fd931520f53185aa120db9bcbf6f0c79 Mon Sep 17 00:00:00 2001 From: Nicholas Musolino Date: Fri, 1 Mar 2019 12:21:02 -0500 Subject: [PATCH 178/215] DOC: Reword Series.interpolate docstring for clarity (#25491) --- pandas/core/generic.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index eb427a42a249b..ee8f9cba951b3 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -6618,10 +6618,10 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, * 'pad': Fill in NaNs using existing values. * 'nearest', 'zero', 'slinear', 'quadratic', 'cubic', 'spline', 'barycentric', 'polynomial': Passed to - `scipy.interpolate.interp1d`. Both 'polynomial' and 'spline' - require that you also specify an `order` (int), - e.g. ``df.interpolate(method='polynomial', order=5)``. - These use the numerical values of the index. + `scipy.interpolate.interp1d`. These methods use the numerical + values of the index. Both 'polynomial' and 'spline' require that + you also specify an `order` (int), e.g. + ``df.interpolate(method='polynomial', order=5)``. * 'krogh', 'piecewise_polynomial', 'spline', 'pchip', 'akima': Wrappers around the SciPy interpolation methods of similar names. See `Notes`. From ae4db8665484a0082949cef0acf16559c07c922f Mon Sep 17 00:00:00 2001 From: William Ayd Date: Fri, 1 Mar 2019 09:57:38 -0800 Subject: [PATCH 179/215] Changed insertion order to sys.path (#25486) --- doc/make.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/make.py b/doc/make.py index 8b2a77987e663..6ffbd3ef86e68 100755 --- a/doc/make.py +++ b/doc/make.py @@ -325,7 +325,7 @@ def main(): # the import of `python_path` correctly. The latter is used to resolve # the import within the module, injecting it into the global namespace os.environ['PYTHONPATH'] = args.python_path - sys.path.append(args.python_path) + sys.path.insert(0, args.python_path) globals()['pandas'] = importlib.import_module('pandas') # Set the matplotlib backend to the non-interactive Agg backend for all From 3e3c9019a11fbdf9e61fc36ca216fd128c25ae04 Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Sat, 2 Mar 2019 16:08:08 -0500 Subject: [PATCH 180/215] TST: xfail non-writeable pytables tests with numpy 1.16x (#25517) --- pandas/compat/numpy/__init__.py | 4 ++- pandas/tests/indexes/multi/test_analytics.py | 4 +-- pandas/tests/io/test_pytables.py | 31 ++++++++++++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/pandas/compat/numpy/__init__.py b/pandas/compat/numpy/__init__.py index bc9af01a97467..6e9f768d8bd68 100644 --- a/pandas/compat/numpy/__init__.py +++ b/pandas/compat/numpy/__init__.py @@ -13,6 +13,7 @@ _np_version_under1p14 = _nlv < LooseVersion('1.14') _np_version_under1p15 = _nlv < LooseVersion('1.15') _np_version_under1p16 = _nlv < LooseVersion('1.16') +_np_version_under1p17 = _nlv < LooseVersion('1.17') if _nlv < '1.12': @@ -66,5 +67,6 @@ def np_array_datetime64_compat(arr, *args, **kwargs): '_np_version_under1p13', '_np_version_under1p14', '_np_version_under1p15', - '_np_version_under1p16' + '_np_version_under1p16', + '_np_version_under1p17' ] diff --git a/pandas/tests/indexes/multi/test_analytics.py b/pandas/tests/indexes/multi/test_analytics.py index 27a5ba9e5434a..d5a6e9acaa5f3 100644 --- a/pandas/tests/indexes/multi/test_analytics.py +++ b/pandas/tests/indexes/multi/test_analytics.py @@ -4,7 +4,7 @@ import pytest from pandas.compat import PY2, lrange -from pandas.compat.numpy import _np_version_under1p16 +from pandas.compat.numpy import _np_version_under1p17 import pandas as pd from pandas import Index, MultiIndex, date_range, period_range @@ -287,7 +287,7 @@ def test_numpy_ufuncs(idx, func): # test ufuncs of numpy. see: # http://docs.scipy.org/doc/numpy/reference/ufuncs.html - if _np_version_under1p16: + if _np_version_under1p17: expected_exception = AttributeError msg = "'tuple' object has no attribute '{}'".format(func.__name__) else: diff --git a/pandas/tests/io/test_pytables.py b/pandas/tests/io/test_pytables.py index b464903d8b4e0..69ff32d1b728b 100644 --- a/pandas/tests/io/test_pytables.py +++ b/pandas/tests/io/test_pytables.py @@ -34,6 +34,15 @@ tables = pytest.importorskip('tables') +# TODO: +# remove when gh-24839 is fixed; this affects numpy 1.16 +# and pytables 3.4.4 +xfail_non_writeable = pytest.mark.xfail( + LooseVersion(np.__version__) >= LooseVersion('1.16'), + reason=('gh-25511, gh-24839. pytables needs a ' + 'release beyong 3.4.4 to support numpy 1.16x')) + + _default_compressor = ('blosc' if LooseVersion(tables.__version__) >= LooseVersion('2.2') else 'zlib') @@ -862,6 +871,7 @@ def test_put_integer(self): df = DataFrame(np.random.randn(50, 100)) self._check_roundtrip(df, tm.assert_frame_equal) + @xfail_non_writeable def test_put_mixed_type(self): df = tm.makeTimeDataFrame() df['obj1'] = 'foo' @@ -1438,7 +1448,10 @@ def test_to_hdf_with_min_itemsize(self): tm.assert_series_equal(pd.read_hdf(path, 'ss4'), pd.concat([df['B'], df2['B']])) - @pytest.mark.parametrize("format", ['fixed', 'table']) + @pytest.mark.parametrize( + "format", + [pytest.param('fixed', marks=xfail_non_writeable), + 'table']) def test_to_hdf_errors(self, format): data = ['\ud800foo'] @@ -1815,6 +1828,7 @@ def test_pass_spec_to_storer(self): pytest.raises(TypeError, store.select, 'df', where=[('columns=A')]) + @xfail_non_writeable def test_append_misc(self): with ensure_clean_store(self.path) as store: @@ -2006,6 +2020,7 @@ def test_unimplemented_dtypes_table_columns(self): # this fails because we have a date in the object block...... pytest.raises(TypeError, store.append, 'df_unimplemented', df) + @xfail_non_writeable @pytest.mark.skipif( LooseVersion(np.__version__) == LooseVersion('1.15.0'), reason=("Skipping pytables test when numpy version is " @@ -2245,6 +2260,7 @@ def test_float_index(self): s = Series(np.random.randn(10), index=index) self._check_roundtrip(s, tm.assert_series_equal) + @xfail_non_writeable def test_tuple_index(self): # GH #492 @@ -2257,6 +2273,7 @@ def test_tuple_index(self): simplefilter("ignore", pd.errors.PerformanceWarning) self._check_roundtrip(DF, tm.assert_frame_equal) + @xfail_non_writeable @pytest.mark.filterwarnings("ignore::pandas.errors.PerformanceWarning") def test_index_types(self): @@ -2320,6 +2337,7 @@ def test_timeseries_preepoch(self): except OverflowError: pytest.skip('known failer on some windows platforms') + @xfail_non_writeable @pytest.mark.parametrize("compression", [ False, pytest.param(True, marks=td.skip_if_windows_python_3) ]) @@ -2350,6 +2368,7 @@ def test_frame(self, compression): # empty self._check_roundtrip(df[:0], tm.assert_frame_equal) + @xfail_non_writeable def test_empty_series_frame(self): s0 = Series() s1 = Series(name='myseries') @@ -2363,8 +2382,10 @@ def test_empty_series_frame(self): self._check_roundtrip(df1, tm.assert_frame_equal) self._check_roundtrip(df2, tm.assert_frame_equal) - def test_empty_series(self): - for dtype in [np.int64, np.float64, np.object, 'm8[ns]', 'M8[ns]']: + @xfail_non_writeable + @pytest.mark.parametrize( + 'dtype', [np.int64, np.float64, np.object, 'm8[ns]', 'M8[ns]']) + def test_empty_series(self, dtype): s = Series(dtype=dtype) self._check_roundtrip(s, tm.assert_series_equal) @@ -2445,6 +2466,7 @@ def test_store_series_name(self): recons = store['series'] tm.assert_series_equal(recons, series) + @xfail_non_writeable @pytest.mark.parametrize("compression", [ False, pytest.param(True, marks=td.skip_if_windows_python_3) ]) @@ -3954,6 +3976,7 @@ def test_pytables_native2_read(self, datapath): d1 = store['detector'] assert isinstance(d1, DataFrame) + @xfail_non_writeable def test_legacy_table_fixed_format_read_py2(self, datapath): # GH 24510 # legacy table with fixed format written in Python 2 @@ -4117,6 +4140,7 @@ def test_unicode_longer_encoded(self): result = store.get('df') tm.assert_frame_equal(result, df) + @xfail_non_writeable def test_store_datetime_mixed(self): df = DataFrame( @@ -4677,6 +4701,7 @@ def test_complex_table(self): reread = read_hdf(path, 'df') assert_frame_equal(df, reread) + @xfail_non_writeable def test_complex_mixed_fixed(self): complex64 = np.array([1.0 + 1.0j, 1.0 + 1.0j, 1.0 + 1.0j, 1.0 + 1.0j], dtype=np.complex64) From d7084616472ee39faa2758117dc7d6707b3a8bb3 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Sun, 3 Mar 2019 01:41:53 +0000 Subject: [PATCH 181/215] =?UTF-8?q?STY:=20use=20pytest.raises=20context=20?= =?UTF-8?q?manager=20(arithmetic,=20arrays,=20computati=E2=80=A6=20(#25504?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pandas/tests/arithmetic/test_timedelta64.py | 66 ++++++++++---- .../arrays/categorical/test_analytics.py | 29 +++--- .../arrays/categorical/test_operators.py | 88 +++++++++++++------ pandas/tests/arrays/sparse/test_libsparse.py | 10 ++- pandas/tests/computation/test_eval.py | 74 ++++++++++------ pandas/tests/dtypes/test_common.py | 15 ++-- pandas/tests/dtypes/test_dtypes.py | 18 ++-- 7 files changed, 205 insertions(+), 95 deletions(-) diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index c31d7acad3111..0faed74d4a021 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -205,10 +205,20 @@ def test_subtraction_ops(self): td = Timedelta('1 days') dt = Timestamp('20130101') - pytest.raises(TypeError, lambda: tdi - dt) - pytest.raises(TypeError, lambda: tdi - dti) - pytest.raises(TypeError, lambda: td - dt) - pytest.raises(TypeError, lambda: td - dti) + msg = "cannot subtract a datelike from a TimedeltaArray" + with pytest.raises(TypeError, match=msg): + tdi - dt + with pytest.raises(TypeError, match=msg): + tdi - dti + + msg = (r"descriptor '__sub__' requires a 'datetime\.datetime' object" + " but received a 'Timedelta'") + with pytest.raises(TypeError, match=msg): + td - dt + + msg = "bad operand type for unary -: 'DatetimeArray'" + with pytest.raises(TypeError, match=msg): + td - dti result = dt - dti expected = TimedeltaIndex(['0 days', '-1 days', '-2 days'], name='bar') @@ -265,19 +275,38 @@ def _check(result, expected): _check(result, expected) # tz mismatches - pytest.raises(TypeError, lambda: dt_tz - ts) - pytest.raises(TypeError, lambda: dt_tz - dt) - pytest.raises(TypeError, lambda: dt_tz - ts_tz2) - pytest.raises(TypeError, lambda: dt - dt_tz) - pytest.raises(TypeError, lambda: ts - dt_tz) - pytest.raises(TypeError, lambda: ts_tz2 - ts) - pytest.raises(TypeError, lambda: ts_tz2 - dt) - pytest.raises(TypeError, lambda: ts_tz - ts_tz2) + msg = ("Timestamp subtraction must have the same timezones or no" + " timezones") + with pytest.raises(TypeError, match=msg): + dt_tz - ts + msg = "can't subtract offset-naive and offset-aware datetimes" + with pytest.raises(TypeError, match=msg): + dt_tz - dt + msg = ("Timestamp subtraction must have the same timezones or no" + " timezones") + with pytest.raises(TypeError, match=msg): + dt_tz - ts_tz2 + msg = "can't subtract offset-naive and offset-aware datetimes" + with pytest.raises(TypeError, match=msg): + dt - dt_tz + msg = ("Timestamp subtraction must have the same timezones or no" + " timezones") + with pytest.raises(TypeError, match=msg): + ts - dt_tz + with pytest.raises(TypeError, match=msg): + ts_tz2 - ts + with pytest.raises(TypeError, match=msg): + ts_tz2 - dt + with pytest.raises(TypeError, match=msg): + ts_tz - ts_tz2 # with dti - pytest.raises(TypeError, lambda: dti - ts_tz) - pytest.raises(TypeError, lambda: dti_tz - ts) - pytest.raises(TypeError, lambda: dti_tz - ts_tz2) + with pytest.raises(TypeError, match=msg): + dti - ts_tz + with pytest.raises(TypeError, match=msg): + dti_tz - ts + with pytest.raises(TypeError, match=msg): + dti_tz - ts_tz2 result = dti_tz - dt_tz expected = TimedeltaIndex(['0 days', '1 days', '2 days']) @@ -349,8 +378,11 @@ def test_addition_ops(self): tm.assert_index_equal(result, expected) # unequal length - pytest.raises(ValueError, lambda: tdi + dti[0:1]) - pytest.raises(ValueError, lambda: tdi[0:1] + dti) + msg = "cannot add indices of unequal length" + with pytest.raises(ValueError, match=msg): + tdi + dti[0:1] + with pytest.raises(ValueError, match=msg): + tdi[0:1] + dti # random indexes with pytest.raises(NullFrequencyError): diff --git a/pandas/tests/arrays/categorical/test_analytics.py b/pandas/tests/arrays/categorical/test_analytics.py index 5efcd527de8d8..7ce82d5bcdded 100644 --- a/pandas/tests/arrays/categorical/test_analytics.py +++ b/pandas/tests/arrays/categorical/test_analytics.py @@ -18,8 +18,11 @@ def test_min_max(self): # unordered cats have no min/max cat = Categorical(["a", "b", "c", "d"], ordered=False) - pytest.raises(TypeError, lambda: cat.min()) - pytest.raises(TypeError, lambda: cat.max()) + msg = "Categorical is not ordered for operation {}" + with pytest.raises(TypeError, match=msg.format('min')): + cat.min() + with pytest.raises(TypeError, match=msg.format('max')): + cat.max() cat = Categorical(["a", "b", "c", "d"], ordered=True) _min = cat.min() @@ -108,18 +111,24 @@ def test_searchsorted(self): tm.assert_numpy_array_equal(res_ser, exp) # Searching for a single value that is not from the Categorical - pytest.raises(KeyError, lambda: c1.searchsorted('cucumber')) - pytest.raises(KeyError, lambda: s1.searchsorted('cucumber')) + msg = r"Value\(s\) to be inserted must be in categories" + with pytest.raises(KeyError, match=msg): + c1.searchsorted('cucumber') + with pytest.raises(KeyError, match=msg): + s1.searchsorted('cucumber') # Searching for multiple values one of each is not from the Categorical - pytest.raises(KeyError, - lambda: c1.searchsorted(['bread', 'cucumber'])) - pytest.raises(KeyError, - lambda: s1.searchsorted(['bread', 'cucumber'])) + with pytest.raises(KeyError, match=msg): + c1.searchsorted(['bread', 'cucumber']) + with pytest.raises(KeyError, match=msg): + s1.searchsorted(['bread', 'cucumber']) # searchsorted call for unordered Categorical - pytest.raises(ValueError, lambda: c2.searchsorted('apple')) - pytest.raises(ValueError, lambda: s2.searchsorted('apple')) + msg = "Categorical not ordered" + with pytest.raises(ValueError, match=msg): + c2.searchsorted('apple') + with pytest.raises(ValueError, match=msg): + s2.searchsorted('apple') def test_unique(self): # categories are reordered based on value when ordered=False diff --git a/pandas/tests/arrays/categorical/test_operators.py b/pandas/tests/arrays/categorical/test_operators.py index b2965bbcc456a..e1264722aedcd 100644 --- a/pandas/tests/arrays/categorical/test_operators.py +++ b/pandas/tests/arrays/categorical/test_operators.py @@ -4,6 +4,8 @@ import numpy as np import pytest +from pandas.compat import PY2 + import pandas as pd from pandas import Categorical, DataFrame, Series, date_range from pandas.tests.arrays.categorical.common import TestCategorical @@ -17,6 +19,7 @@ def test_categories_none_comparisons(self): 'a', 'c', 'c', 'c'], ordered=True) tm.assert_categorical_equal(factor, self.factor) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_comparisons(self): result = self.factor[self.factor == 'a'] @@ -95,16 +98,24 @@ def test_comparisons(self): # comparison (in both directions) with Series will raise s = Series(["b", "b", "b"]) - pytest.raises(TypeError, lambda: cat > s) - pytest.raises(TypeError, lambda: cat_rev > s) - pytest.raises(TypeError, lambda: s < cat) - pytest.raises(TypeError, lambda: s < cat_rev) + msg = ("Cannot compare a Categorical for op __gt__ with type" + r" ") + with pytest.raises(TypeError, match=msg): + cat > s + with pytest.raises(TypeError, match=msg): + cat_rev > s + with pytest.raises(TypeError, match=msg): + s < cat + with pytest.raises(TypeError, match=msg): + s < cat_rev # comparison with numpy.array will raise in both direction, but only on # newer numpy versions a = np.array(["b", "b", "b"]) - pytest.raises(TypeError, lambda: cat > a) - pytest.raises(TypeError, lambda: cat_rev > a) + with pytest.raises(TypeError, match=msg): + cat > a + with pytest.raises(TypeError, match=msg): + cat_rev > a # Make sure that unequal comparison take the categories order in # account @@ -163,16 +174,23 @@ def test_comparison_with_unknown_scalars(self): # for unequal comps, but not for equal/not equal cat = Categorical([1, 2, 3], ordered=True) - pytest.raises(TypeError, lambda: cat < 4) - pytest.raises(TypeError, lambda: cat > 4) - pytest.raises(TypeError, lambda: 4 < cat) - pytest.raises(TypeError, lambda: 4 > cat) + msg = ("Cannot compare a Categorical for op __{}__ with a scalar," + " which is not a category") + with pytest.raises(TypeError, match=msg.format('lt')): + cat < 4 + with pytest.raises(TypeError, match=msg.format('gt')): + cat > 4 + with pytest.raises(TypeError, match=msg.format('gt')): + 4 < cat + with pytest.raises(TypeError, match=msg.format('lt')): + 4 > cat tm.assert_numpy_array_equal(cat == 4, np.array([False, False, False])) tm.assert_numpy_array_equal(cat != 4, np.array([True, True, True])) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") @pytest.mark.parametrize('data,reverse,base', [ (list("abc"), list("cba"), list("bbb")), ([1, 2, 3], [3, 2, 1], [2, 2, 2])] @@ -219,16 +237,26 @@ def test_comparisons(self, data, reverse, base): # categorical cannot be compared to Series or numpy array, and also # not the other way around - pytest.raises(TypeError, lambda: cat > s) - pytest.raises(TypeError, lambda: cat_rev > s) - pytest.raises(TypeError, lambda: cat > a) - pytest.raises(TypeError, lambda: cat_rev > a) + msg = ("Cannot compare a Categorical for op __gt__ with type" + r" ") + with pytest.raises(TypeError, match=msg): + cat > s + with pytest.raises(TypeError, match=msg): + cat_rev > s + with pytest.raises(TypeError, match=msg): + cat > a + with pytest.raises(TypeError, match=msg): + cat_rev > a - pytest.raises(TypeError, lambda: s < cat) - pytest.raises(TypeError, lambda: s < cat_rev) + with pytest.raises(TypeError, match=msg): + s < cat + with pytest.raises(TypeError, match=msg): + s < cat_rev - pytest.raises(TypeError, lambda: a < cat) - pytest.raises(TypeError, lambda: a < cat_rev) + with pytest.raises(TypeError, match=msg): + a < cat + with pytest.raises(TypeError, match=msg): + a < cat_rev @pytest.mark.parametrize('ctor', [ lambda *args, **kwargs: Categorical(*args, **kwargs), @@ -287,16 +315,21 @@ def test_numeric_like_ops(self): right=False, labels=cat_labels) # numeric ops should not succeed - for op in ['__add__', '__sub__', '__mul__', '__truediv__']: - pytest.raises(TypeError, - lambda: getattr(df, op)(df)) + for op, str_rep in [('__add__', r'\+'), + ('__sub__', '-'), + ('__mul__', r'\*'), + ('__truediv__', '/')]: + msg = r"Series cannot perform the operation {}".format(str_rep) + with pytest.raises(TypeError, match=msg): + getattr(df, op)(df) # reduction ops should not succeed (unless specifically defined, e.g. # min/max) s = df['value_group'] for op in ['kurt', 'skew', 'var', 'std', 'mean', 'sum', 'median']: - pytest.raises(TypeError, - lambda: getattr(s, op)(numeric_only=False)) + msg = "Categorical cannot perform the operation {}".format(op) + with pytest.raises(TypeError, match=msg): + getattr(s, op)(numeric_only=False) # mad technically works because it takes always the numeric data @@ -306,8 +339,13 @@ def test_numeric_like_ops(self): np.sum(s) # numeric ops on a Series - for op in ['__add__', '__sub__', '__mul__', '__truediv__']: - pytest.raises(TypeError, lambda: getattr(s, op)(2)) + for op, str_rep in [('__add__', r'\+'), + ('__sub__', '-'), + ('__mul__', r'\*'), + ('__truediv__', '/')]: + msg = r"Series cannot perform the operation {}".format(str_rep) + with pytest.raises(TypeError, match=msg): + getattr(s, op)(2) # invalid ufunc with pytest.raises(TypeError): diff --git a/pandas/tests/arrays/sparse/test_libsparse.py b/pandas/tests/arrays/sparse/test_libsparse.py index 6e9d790bf85f3..2cbe7d9ea084c 100644 --- a/pandas/tests/arrays/sparse/test_libsparse.py +++ b/pandas/tests/arrays/sparse/test_libsparse.py @@ -449,11 +449,13 @@ def test_check_integrity(self): # also OK even though empty index = BlockIndex(1, locs, lengths) # noqa - # block extend beyond end - pytest.raises(Exception, BlockIndex, 10, [5], [10]) + msg = "Block 0 extends beyond end" + with pytest.raises(ValueError, match=msg): + BlockIndex(10, [5], [10]) - # block overlap - pytest.raises(Exception, BlockIndex, 10, [2, 5], [5, 3]) + msg = "Block 0 overlaps" + with pytest.raises(ValueError, match=msg): + BlockIndex(10, [2, 5], [5, 3]) def test_to_int_index(self): locs = [0, 10] diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index c1ba15f428eb7..a14d8e4471c23 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -285,10 +285,14 @@ def check_operands(left, right, cmp_op): def check_simple_cmp_op(self, lhs, cmp1, rhs): ex = 'lhs {0} rhs'.format(cmp1) + msg = (r"only list-like( or dict-like)? objects are allowed to be" + r" passed to (DataFrame\.)?isin\(\), you passed a" + r" (\[|')bool(\]|')|" + "argument of type 'bool' is not iterable") if cmp1 in ('in', 'not in') and not is_list_like(rhs): - pytest.raises(TypeError, pd.eval, ex, engine=self.engine, - parser=self.parser, local_dict={'lhs': lhs, - 'rhs': rhs}) + with pytest.raises(TypeError, match=msg): + pd.eval(ex, engine=self.engine, parser=self.parser, + local_dict={'lhs': lhs, 'rhs': rhs}) else: expected = _eval_single_bin(lhs, cmp1, rhs, self.engine) result = pd.eval(ex, engine=self.engine, parser=self.parser) @@ -341,9 +345,11 @@ def check_floor_division(self, lhs, arith1, rhs): expected = lhs // rhs self.check_equal(res, expected) else: - pytest.raises(TypeError, pd.eval, ex, - local_dict={'lhs': lhs, 'rhs': rhs}, - engine=self.engine, parser=self.parser) + msg = (r"unsupported operand type\(s\) for //: 'VariableNode' and" + " 'VariableNode'") + with pytest.raises(TypeError, match=msg): + pd.eval(ex, local_dict={'lhs': lhs, 'rhs': rhs}, + engine=self.engine, parser=self.parser) def get_expected_pow_result(self, lhs, rhs): try: @@ -396,10 +402,14 @@ def check_compound_invert_op(self, lhs, cmp1, rhs): skip_these = 'in', 'not in' ex = '~(lhs {0} rhs)'.format(cmp1) + msg = (r"only list-like( or dict-like)? objects are allowed to be" + r" passed to (DataFrame\.)?isin\(\), you passed a" + r" (\[|')float(\]|')|" + "argument of type 'float' is not iterable") if is_scalar(rhs) and cmp1 in skip_these: - pytest.raises(TypeError, pd.eval, ex, engine=self.engine, - parser=self.parser, local_dict={'lhs': lhs, - 'rhs': rhs}) + with pytest.raises(TypeError, match=msg): + pd.eval(ex, engine=self.engine, parser=self.parser, + local_dict={'lhs': lhs, 'rhs': rhs}) else: # compound if is_scalar(lhs) and is_scalar(rhs): @@ -1101,8 +1111,9 @@ def test_simple_arith_ops(self): ex3 = '1 {0} (x + 1)'.format(op) if op in ('in', 'not in'): - pytest.raises(TypeError, pd.eval, ex, - engine=self.engine, parser=self.parser) + msg = "argument of type 'int' is not iterable" + with pytest.raises(TypeError, match=msg): + pd.eval(ex, engine=self.engine, parser=self.parser) else: expec = _eval_single_bin(1, op, 1, self.engine) x = self.eval(ex, engine=self.engine, parser=self.parser) @@ -1236,19 +1247,25 @@ def test_assignment_fails(self): df = DataFrame(np.random.randn(5, 3), columns=list('abc')) df2 = DataFrame(np.random.randn(5, 3)) expr1 = 'df = df2' - pytest.raises(ValueError, self.eval, expr1, - local_dict={'df': df, 'df2': df2}) + msg = "cannot assign without a target object" + with pytest.raises(ValueError, match=msg): + self.eval(expr1, local_dict={'df': df, 'df2': df2}) def test_assignment_column(self): df = DataFrame(np.random.randn(5, 2), columns=list('ab')) orig_df = df.copy() # multiple assignees - pytest.raises(SyntaxError, df.eval, 'd c = a + b') + with pytest.raises(SyntaxError, match="invalid syntax"): + df.eval('d c = a + b') # invalid assignees - pytest.raises(SyntaxError, df.eval, 'd,c = a + b') - pytest.raises(SyntaxError, df.eval, 'Timestamp("20131001") = a + b') + msg = "left hand side of an assignment must be a single name" + with pytest.raises(SyntaxError, match=msg): + df.eval('d,c = a + b') + msg = "can't assign to function call" + with pytest.raises(SyntaxError, match=msg): + df.eval('Timestamp("20131001") = a + b') # single assignment - existing variable expected = orig_df.copy() @@ -1291,7 +1308,9 @@ def f(): # multiple assignment df = orig_df.copy() df.eval('c = a + b', inplace=True) - pytest.raises(SyntaxError, df.eval, 'c = a = b') + msg = "can only assign a single expression" + with pytest.raises(SyntaxError, match=msg): + df.eval('c = a = b') # explicit targets df = orig_df.copy() @@ -1545,21 +1564,24 @@ def test_check_many_exprs(self): def test_fails_and(self): df = DataFrame(np.random.randn(5, 3)) - pytest.raises(NotImplementedError, pd.eval, 'df > 2 and df > 3', - local_dict={'df': df}, parser=self.parser, - engine=self.engine) + msg = "'BoolOp' nodes are not implemented" + with pytest.raises(NotImplementedError, match=msg): + pd.eval('df > 2 and df > 3', local_dict={'df': df}, + parser=self.parser, engine=self.engine) def test_fails_or(self): df = DataFrame(np.random.randn(5, 3)) - pytest.raises(NotImplementedError, pd.eval, 'df > 2 or df > 3', - local_dict={'df': df}, parser=self.parser, - engine=self.engine) + msg = "'BoolOp' nodes are not implemented" + with pytest.raises(NotImplementedError, match=msg): + pd.eval('df > 2 or df > 3', local_dict={'df': df}, + parser=self.parser, engine=self.engine) def test_fails_not(self): df = DataFrame(np.random.randn(5, 3)) - pytest.raises(NotImplementedError, pd.eval, 'not df > 2', - local_dict={'df': df}, parser=self.parser, - engine=self.engine) + msg = "'Not' nodes are not implemented" + with pytest.raises(NotImplementedError, match=msg): + pd.eval('not df > 2', local_dict={'df': df}, parser=self.parser, + engine=self.engine) def test_fails_ampersand(self): df = DataFrame(np.random.randn(5, 3)) # noqa diff --git a/pandas/tests/dtypes/test_common.py b/pandas/tests/dtypes/test_common.py index 62e96fd39a759..5c1f6ff405b3b 100644 --- a/pandas/tests/dtypes/test_common.py +++ b/pandas/tests/dtypes/test_common.py @@ -607,13 +607,16 @@ def test__get_dtype(input_param, result): assert com._get_dtype(input_param) == result -@pytest.mark.parametrize('input_param', [None, - 1, 1.2, - 'random string', - pd.DataFrame([1, 2])]) -def test__get_dtype_fails(input_param): +@pytest.mark.parametrize('input_param,expected_error_message', [ + (None, "Cannot deduce dtype from null object"), + (1, "data type not understood"), + (1.2, "data type not understood"), + ('random string', "data type 'random string' not understood"), + (pd.DataFrame([1, 2]), "data type not understood")]) +def test__get_dtype_fails(input_param, expected_error_message): # python objects - pytest.raises(TypeError, com._get_dtype, input_param) + with pytest.raises(TypeError, match=expected_error_message): + com._get_dtype(input_param) @pytest.mark.parametrize('input_param,result', [ diff --git a/pandas/tests/dtypes/test_dtypes.py b/pandas/tests/dtypes/test_dtypes.py index 1c1442d6f2f23..4366f610871ff 100644 --- a/pandas/tests/dtypes/test_dtypes.py +++ b/pandas/tests/dtypes/test_dtypes.py @@ -38,7 +38,8 @@ def test_equality_invalid(self): assert not is_dtype_equal(self.dtype, np.int64) def test_numpy_informed(self): - pytest.raises(TypeError, np.dtype, self.dtype) + with pytest.raises(TypeError, match="data type not understood"): + np.dtype(self.dtype) assert not self.dtype == np.str_ assert not np.str_ == self.dtype @@ -87,8 +88,9 @@ def test_equality(self): def test_construction_from_string(self): result = CategoricalDtype.construct_from_string('category') assert is_dtype_equal(self.dtype, result) - pytest.raises( - TypeError, lambda: CategoricalDtype.construct_from_string('foo')) + msg = "cannot construct a CategoricalDtype" + with pytest.raises(TypeError, match=msg): + CategoricalDtype.construct_from_string('foo') def test_constructor_invalid(self): msg = "Parameter 'categories' must be list-like" @@ -202,8 +204,9 @@ def test_hash_vs_equality(self): assert hash(dtype2) != hash(dtype4) def test_construction(self): - pytest.raises(ValueError, - lambda: DatetimeTZDtype('ms', 'US/Eastern')) + msg = "DatetimeTZDtype only supports ns units" + with pytest.raises(ValueError, match=msg): + DatetimeTZDtype('ms', 'US/Eastern') def test_subclass(self): a = DatetimeTZDtype.construct_from_string('datetime64[ns, US/Eastern]') @@ -226,8 +229,9 @@ def test_construction_from_string(self): result = DatetimeTZDtype.construct_from_string( 'datetime64[ns, US/Eastern]') assert is_dtype_equal(self.dtype, result) - pytest.raises(TypeError, - lambda: DatetimeTZDtype.construct_from_string('foo')) + msg = "Could not construct DatetimeTZDtype from 'foo'" + with pytest.raises(TypeError, match=msg): + DatetimeTZDtype.construct_from_string('foo') def test_construct_from_string_raises(self): with pytest.raises(TypeError, match="notatz"): From cc5b73e9807e5d4527fdd36187ebd11d744217de Mon Sep 17 00:00:00 2001 From: Jeremy Schendel Date: Sat, 2 Mar 2019 18:44:22 -0700 Subject: [PATCH 182/215] BUG: Fix RecursionError during IntervalTree construction (#25498) --- doc/source/whatsnew/v0.24.2.rst | 1 + pandas/_libs/intervaltree.pxi.in | 2 +- pandas/tests/indexes/interval/test_interval_tree.py | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 4fcde7769b362..926239e7e5dc5 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -98,6 +98,7 @@ Bug Fixes - Bug in :meth:`Series.is_unique` where single occurrences of ``NaN`` were not considered unique (:issue:`25180`) - Bug in :func:`merge` when merging an empty ``DataFrame`` with an ``Int64`` column or a non-empty ``DataFrame`` with an ``Int64`` column that is all ``NaN`` (:issue:`25183`) +- Bug in ``IntervalTree`` where a ``RecursionError`` occurs upon construction due to an overflow when adding endpoints, which also causes :class:`IntervalIndex` to crash during indexing operations (:issue:`25485`) - .. _whatsnew_0.242.contributors: diff --git a/pandas/_libs/intervaltree.pxi.in b/pandas/_libs/intervaltree.pxi.in index fb6f30c030f11..196841f35ed8d 100644 --- a/pandas/_libs/intervaltree.pxi.in +++ b/pandas/_libs/intervaltree.pxi.in @@ -284,7 +284,7 @@ cdef class {{dtype_title}}Closed{{closed_title}}IntervalNode: else: # calculate a pivot so we can create child nodes self.is_leaf_node = False - self.pivot = np.median(left + right) / 2 + self.pivot = np.median(left / 2 + right / 2) left_set, right_set, center_set = self.classify_intervals( left, right) diff --git a/pandas/tests/indexes/interval/test_interval_tree.py b/pandas/tests/indexes/interval/test_interval_tree.py index 90722e66d8d8c..46b2d12015a22 100644 --- a/pandas/tests/indexes/interval/test_interval_tree.py +++ b/pandas/tests/indexes/interval/test_interval_tree.py @@ -171,3 +171,13 @@ def test_is_overlapping_trivial(self, closed, left, right): # GH 23309 tree = IntervalTree(left, right, closed=closed) assert tree.is_overlapping is False + + def test_construction_overflow(self): + # GH 25485 + left, right = np.arange(101), [np.iinfo(np.int64).max] * 101 + tree = IntervalTree(left, right) + + # pivot should be average of left/right medians + result = tree.root.pivot + expected = (50 + np.iinfo(np.int64).max) / 2 + assert result == expected From c66028cab32b32f3e0fbb592eba5d05cce5eb443 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Sun, 3 Mar 2019 01:46:07 +0000 Subject: [PATCH 183/215] STY: use pytest.raises context manager (plotting, reductions, scalar...) (#25483) * STY: use pytest.raises context manager (plotting, reductions, scalar...) * revert removed testing in test_timedelta.py * remove TODO from test_frame.py * skip py2 ci failure --- pandas/tests/plotting/test_boxplot_method.py | 21 ++++++--- pandas/tests/plotting/test_datetimelike.py | 13 +++-- pandas/tests/plotting/test_hist_method.py | 17 ++++--- pandas/tests/plotting/test_misc.py | 14 ++++-- pandas/tests/reductions/test_reductions.py | 4 +- pandas/tests/scalar/period/test_period.py | 21 +++++---- .../tests/scalar/timedelta/test_timedelta.py | 45 +++++++++++++----- .../tests/scalar/timestamp/test_timestamp.py | 8 +++- pandas/tests/sparse/frame/test_frame.py | 47 +++++++++++++------ pandas/tests/sparse/series/test_series.py | 34 +++++++++----- pandas/tests/tseries/offsets/test_offsets.py | 17 +++++-- .../tests/tseries/offsets/test_yqm_offsets.py | 20 +++++--- 12 files changed, 180 insertions(+), 81 deletions(-) diff --git a/pandas/tests/plotting/test_boxplot_method.py b/pandas/tests/plotting/test_boxplot_method.py index 7d721c7de3398..e6b9795aebe7c 100644 --- a/pandas/tests/plotting/test_boxplot_method.py +++ b/pandas/tests/plotting/test_boxplot_method.py @@ -267,13 +267,20 @@ def test_grouped_box_return_type(self): def test_grouped_box_layout(self): df = self.hist_df - pytest.raises(ValueError, df.boxplot, column=['weight', 'height'], - by=df.gender, layout=(1, 1)) - pytest.raises(ValueError, df.boxplot, - column=['height', 'weight', 'category'], - layout=(2, 1), return_type='dict') - pytest.raises(ValueError, df.boxplot, column=['weight', 'height'], - by=df.gender, layout=(-1, -1)) + msg = "Layout of 1x1 must be larger than required size 2" + with pytest.raises(ValueError, match=msg): + df.boxplot(column=['weight', 'height'], by=df.gender, + layout=(1, 1)) + + msg = "The 'layout' keyword is not supported when 'by' is None" + with pytest.raises(ValueError, match=msg): + df.boxplot(column=['height', 'weight', 'category'], + layout=(2, 1), return_type='dict') + + msg = "At least one dimension of layout must be positive" + with pytest.raises(ValueError, match=msg): + df.boxplot(column=['weight', 'height'], by=df.gender, + layout=(-1, -1)) # _check_plot_works adds an ax so catch warning. see GH #13188 with tm.assert_produces_warning(UserWarning): diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py index ad79cc97f8b77..6702ad6cfb761 100644 --- a/pandas/tests/plotting/test_datetimelike.py +++ b/pandas/tests/plotting/test_datetimelike.py @@ -97,7 +97,9 @@ def test_nonnumeric_exclude(self): assert len(ax.get_lines()) == 1 # B was plotted self.plt.close(fig) - pytest.raises(TypeError, df['A'].plot) + msg = "Empty 'DataFrame': no numeric data to plot" + with pytest.raises(TypeError, match=msg): + df['A'].plot() def test_tsplot_deprecated(self): from pandas.tseries.plotting import tsplot @@ -140,10 +142,15 @@ def f(*args, **kwds): def test_both_style_and_color(self): ts = tm.makeTimeSeries() - pytest.raises(ValueError, ts.plot, style='b-', color='#000099') + msg = ("Cannot pass 'style' string with a color symbol and 'color' " + "keyword argument. Please use one or the other or pass 'style'" + " without a color symbol") + with pytest.raises(ValueError, match=msg): + ts.plot(style='b-', color='#000099') s = ts.reset_index(drop=True) - pytest.raises(ValueError, s.plot, style='b-', color='#000099') + with pytest.raises(ValueError, match=msg): + s.plot(style='b-', color='#000099') @pytest.mark.slow def test_high_freq(self): diff --git a/pandas/tests/plotting/test_hist_method.py b/pandas/tests/plotting/test_hist_method.py index 7bdbdac54f7a6..4f0bef52b5e15 100644 --- a/pandas/tests/plotting/test_hist_method.py +++ b/pandas/tests/plotting/test_hist_method.py @@ -332,12 +332,17 @@ def test_grouped_hist_legacy2(self): @pytest.mark.slow def test_grouped_hist_layout(self): df = self.hist_df - pytest.raises(ValueError, df.hist, column='weight', by=df.gender, - layout=(1, 1)) - pytest.raises(ValueError, df.hist, column='height', by=df.category, - layout=(1, 3)) - pytest.raises(ValueError, df.hist, column='height', by=df.category, - layout=(-1, -1)) + msg = "Layout of 1x1 must be larger than required size 2" + with pytest.raises(ValueError, match=msg): + df.hist(column='weight', by=df.gender, layout=(1, 1)) + + msg = "Layout of 1x3 must be larger than required size 4" + with pytest.raises(ValueError, match=msg): + df.hist(column='height', by=df.category, layout=(1, 3)) + + msg = "At least one dimension of layout must be positive" + with pytest.raises(ValueError, match=msg): + df.hist(column='height', by=df.category, layout=(-1, -1)) with tm.assert_produces_warning(UserWarning): axes = _check_plot_works(df.hist, column='height', by=df.gender, diff --git a/pandas/tests/plotting/test_misc.py b/pandas/tests/plotting/test_misc.py index 44b95f7d1b00b..98248586f3d27 100644 --- a/pandas/tests/plotting/test_misc.py +++ b/pandas/tests/plotting/test_misc.py @@ -278,14 +278,20 @@ def test_subplot_titles(self, iris): assert [p.get_title() for p in plot] == title # Case len(title) > len(df) - pytest.raises(ValueError, df.plot, subplots=True, - title=title + ["kittens > puppies"]) + msg = ("The length of `title` must equal the number of columns if" + " using `title` of type `list` and `subplots=True`") + with pytest.raises(ValueError, match=msg): + df.plot(subplots=True, title=title + ["kittens > puppies"]) # Case len(title) < len(df) - pytest.raises(ValueError, df.plot, subplots=True, title=title[:2]) + with pytest.raises(ValueError, match=msg): + df.plot(subplots=True, title=title[:2]) # Case subplots=False and title is of type list - pytest.raises(ValueError, df.plot, subplots=False, title=title) + msg = ("Using `title` of type `list` is not supported unless" + " `subplots=True` is passed") + with pytest.raises(ValueError, match=msg): + df.plot(subplots=False, title=title) # Case df with 3 numeric columns but layout of (2,2) plot = df.drop('SepalWidth', axis=1).plot(subplots=True, layout=(2, 2), diff --git a/pandas/tests/reductions/test_reductions.py b/pandas/tests/reductions/test_reductions.py index 8520855d14918..fbf7f610688ba 100644 --- a/pandas/tests/reductions/test_reductions.py +++ b/pandas/tests/reductions/test_reductions.py @@ -276,7 +276,9 @@ def test_timedelta_ops(self): # invalid ops for op in ['skew', 'kurt', 'sem', 'prod']: - pytest.raises(TypeError, getattr(td, op)) + msg = "reduction operation '{}' not allowed for this dtype" + with pytest.raises(TypeError, match=msg.format(op)): + getattr(td, op)() # GH#10040 # make sure NaT is properly handled by median() diff --git a/pandas/tests/scalar/period/test_period.py b/pandas/tests/scalar/period/test_period.py index d0f87618ad3af..8ca19745055a3 100644 --- a/pandas/tests/scalar/period/test_period.py +++ b/pandas/tests/scalar/period/test_period.py @@ -8,6 +8,7 @@ from pandas._libs.tslibs.ccalendar import DAYS, MONTHS from pandas._libs.tslibs.frequencies import INVALID_FREQ_ERR_MSG from pandas._libs.tslibs.parsing import DateParseError +from pandas._libs.tslibs.period import IncompatibleFrequency from pandas._libs.tslibs.timezones import dateutil_gettz, maybe_get_tz from pandas.compat import iteritems, text_type from pandas.compat.numpy import np_datetime64_compat @@ -35,7 +36,9 @@ def test_construction(self): i4 = Period('2005', freq='M') i5 = Period('2005', freq='m') - pytest.raises(ValueError, i1.__ne__, i4) + msg = r"Input has different freq=M from Period\(freq=A-DEC\)" + with pytest.raises(IncompatibleFrequency, match=msg): + i1 != i4 assert i4 == i5 i1 = Period.now('Q') @@ -74,9 +77,12 @@ def test_construction(self): freq='U') assert i1 == expected - pytest.raises(ValueError, Period, ordinal=200701) + msg = "Must supply freq for ordinal value" + with pytest.raises(ValueError, match=msg): + Period(ordinal=200701) - pytest.raises(ValueError, Period, '2007-1-1', freq='X') + with pytest.raises(ValueError, match="Invalid frequency: X"): + Period('2007-1-1', freq='X') def test_construction_bday(self): @@ -233,10 +239,6 @@ def test_period_constructor_offsets(self): freq='U') assert i1 == expected - pytest.raises(ValueError, Period, ordinal=200701) - - pytest.raises(ValueError, Period, '2007-1-1', freq='X') - def test_invalid_arguments(self): with pytest.raises(ValueError): Period(datetime.now()) @@ -925,8 +927,9 @@ def test_properties_secondly(self): class TestPeriodField(object): def test_get_period_field_array_raises_on_out_of_range(self): - pytest.raises(ValueError, libperiod.get_period_field_arr, -1, - np.empty(1), 0) + msg = "Buffer dtype mismatch, expected 'int64_t' but got 'double'" + with pytest.raises(ValueError, match=msg): + libperiod.get_period_field_arr(-1, np.empty(1), 0) class TestComparisons(object): diff --git a/pandas/tests/scalar/timedelta/test_timedelta.py b/pandas/tests/scalar/timedelta/test_timedelta.py index bf71c37aa9c3d..ee2c2e9e1959c 100644 --- a/pandas/tests/scalar/timedelta/test_timedelta.py +++ b/pandas/tests/scalar/timedelta/test_timedelta.py @@ -250,9 +250,13 @@ def check(value): assert rng.microseconds == 0 assert rng.nanoseconds == 0 - pytest.raises(AttributeError, lambda: rng.hours) - pytest.raises(AttributeError, lambda: rng.minutes) - pytest.raises(AttributeError, lambda: rng.milliseconds) + msg = "'Timedelta' object has no attribute '{}'" + with pytest.raises(AttributeError, match=msg.format('hours')): + rng.hours + with pytest.raises(AttributeError, match=msg.format('minutes')): + rng.minutes + with pytest.raises(AttributeError, match=msg.format('milliseconds')): + rng.milliseconds # GH 10050 check(rng.days) @@ -272,9 +276,13 @@ def check(value): assert rng.seconds == 10 * 3600 + 11 * 60 + 12 assert rng.microseconds == 100 * 1000 + 123 assert rng.nanoseconds == 456 - pytest.raises(AttributeError, lambda: rng.hours) - pytest.raises(AttributeError, lambda: rng.minutes) - pytest.raises(AttributeError, lambda: rng.milliseconds) + msg = "'Timedelta' object has no attribute '{}'" + with pytest.raises(AttributeError, match=msg.format('hours')): + rng.hours + with pytest.raises(AttributeError, match=msg.format('minutes')): + rng.minutes + with pytest.raises(AttributeError, match=msg.format('milliseconds')): + rng.milliseconds # components tup = pd.to_timedelta(-1, 'us').components @@ -449,8 +457,12 @@ def test_round(self): assert r2 == s2 # invalid - for freq in ['Y', 'M', 'foobar']: - pytest.raises(ValueError, lambda: t1.round(freq)) + for freq, msg in [ + ('Y', ' is a non-fixed frequency'), + ('M', ' is a non-fixed frequency'), + ('foobar', 'Invalid frequency: foobar')]: + with pytest.raises(ValueError, match=msg): + t1.round(freq) t1 = timedelta_range('1 days', periods=3, freq='1 min 2 s 3 us') t2 = -1 * t1 @@ -495,11 +507,15 @@ def test_round(self): r1 = t1.round(freq) tm.assert_index_equal(r1, s1) r2 = t2.round(freq) - tm.assert_index_equal(r2, s2) + tm.assert_index_equal(r2, s2) # invalid - for freq in ['Y', 'M', 'foobar']: - pytest.raises(ValueError, lambda: t1.round(freq)) + for freq, msg in [ + ('Y', ' is a non-fixed frequency'), + ('M', ' is a non-fixed frequency'), + ('foobar', 'Invalid frequency: foobar')]: + with pytest.raises(ValueError, match=msg): + t1.round(freq) def test_contains(self): # Checking for any NaT-like objects @@ -609,9 +625,12 @@ def test_overflow(self): assert np.allclose(result.value / 1000, expected.value / 1000) # sum - pytest.raises(ValueError, lambda: (s - s.min()).sum()) + msg = "overflow in timedelta operation" + with pytest.raises(ValueError, match=msg): + (s - s.min()).sum() s1 = s[0:10000] - pytest.raises(ValueError, lambda: (s1 - s1.min()).sum()) + with pytest.raises(ValueError, match=msg): + (s1 - s1.min()).sum() s2 = s[0:1000] result = (s2 - s2.min()).sum() diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 7d81d905eac4f..b55d00b44fd67 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -60,7 +60,9 @@ def check(value, equal): check(ts.hour, 9) check(ts.minute, 6) check(ts.second, 3) - pytest.raises(AttributeError, lambda: ts.millisecond) + msg = "'Timestamp' object has no attribute 'millisecond'" + with pytest.raises(AttributeError, match=msg): + ts.millisecond check(ts.microsecond, 100) check(ts.nanosecond, 1) check(ts.dayofweek, 6) @@ -78,7 +80,9 @@ def check(value, equal): check(ts.hour, 23) check(ts.minute, 59) check(ts.second, 0) - pytest.raises(AttributeError, lambda: ts.millisecond) + msg = "'Timestamp' object has no attribute 'millisecond'" + with pytest.raises(AttributeError, match=msg): + ts.millisecond check(ts.microsecond, 0) check(ts.nanosecond, 0) check(ts.dayofweek, 2) diff --git a/pandas/tests/sparse/frame/test_frame.py b/pandas/tests/sparse/frame/test_frame.py index bfb5103c97adc..b31738794c854 100644 --- a/pandas/tests/sparse/frame/test_frame.py +++ b/pandas/tests/sparse/frame/test_frame.py @@ -7,7 +7,7 @@ import pytest from pandas._libs.sparse import BlockIndex, IntIndex -from pandas.compat import lrange +from pandas.compat import PY2, lrange from pandas.errors import PerformanceWarning import pandas as pd @@ -145,8 +145,9 @@ def test_constructor_ndarray(self, float_frame): tm.assert_sp_frame_equal(sp, float_frame.reindex(columns=['A'])) # raise on level argument - pytest.raises(TypeError, float_frame.reindex, columns=['A'], - level=1) + msg = "Reindex by level not supported for sparse" + with pytest.raises(TypeError, match=msg): + float_frame.reindex(columns=['A'], level=1) # wrong length index / columns with pytest.raises(ValueError, match="^Index length"): @@ -433,7 +434,8 @@ def test_getitem(self): exp = sdf.reindex(columns=['a', 'b']) tm.assert_sp_frame_equal(result, exp) - pytest.raises(Exception, sdf.__getitem__, ['a', 'd']) + with pytest.raises(KeyError, match=r"\['d'\] not in index"): + sdf[['a', 'd']] def test_iloc(self, float_frame): @@ -504,7 +506,9 @@ def test_getitem_overload(self, float_frame): subframe = float_frame[indexer] tm.assert_index_equal(subindex, subframe.index) - pytest.raises(Exception, float_frame.__getitem__, indexer[:-1]) + msg = "Item wrong length 9 instead of 10" + with pytest.raises(ValueError, match=msg): + float_frame[indexer[:-1]] def test_setitem(self, float_frame, float_frame_int_kind, float_frame_dense, @@ -551,8 +555,9 @@ def _check_frame(frame, orig): assert len(frame['I'].sp_values) == N // 2 # insert ndarray wrong size - pytest.raises(Exception, frame.__setitem__, 'foo', - np.random.randn(N - 1)) + msg = "Length of values does not match length of index" + with pytest.raises(AssertionError, match=msg): + frame['foo'] = np.random.randn(N - 1) # scalar value frame['J'] = 5 @@ -625,17 +630,22 @@ def test_delitem(self, float_frame): def test_set_columns(self, float_frame): float_frame.columns = float_frame.columns - pytest.raises(Exception, setattr, float_frame, 'columns', - float_frame.columns[:-1]) + msg = ("Length mismatch: Expected axis has 4 elements, new values have" + " 3 elements") + with pytest.raises(ValueError, match=msg): + float_frame.columns = float_frame.columns[:-1] def test_set_index(self, float_frame): float_frame.index = float_frame.index - pytest.raises(Exception, setattr, float_frame, 'index', - float_frame.index[:-1]) + msg = ("Length mismatch: Expected axis has 10 elements, new values" + " have 9 elements") + with pytest.raises(ValueError, match=msg): + float_frame.index = float_frame.index[:-1] def test_ctor_reindex(self): idx = pd.Index([0, 1, 2, 3]) - with pytest.raises(ValueError, match=''): + msg = "Length of passed values is 2, index implies 4" + with pytest.raises(ValueError, match=msg): pd.SparseDataFrame({"A": [1, 2]}, index=idx) def test_append(self, float_frame): @@ -858,6 +868,7 @@ def test_describe(self, float_frame): str(float_frame) desc = float_frame.describe() # noqa + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_join(self, float_frame): left = float_frame.loc[:, ['A', 'B']] right = float_frame.loc[:, ['C', 'D']] @@ -865,7 +876,10 @@ def test_join(self, float_frame): tm.assert_sp_frame_equal(joined, float_frame, exact_indices=False) right = float_frame.loc[:, ['B', 'D']] - pytest.raises(Exception, left.join, right) + msg = (r"columns overlap but no suffix specified: Index\(\['B'\]," + r" dtype='object'\)") + with pytest.raises(ValueError, match=msg): + left.join(right) with pytest.raises(ValueError, match='Other Series must have a name'): float_frame.join(Series( @@ -1046,8 +1060,11 @@ def _check(frame): _check(float_frame_int_kind) # for now - pytest.raises(Exception, _check, float_frame_fill0) - pytest.raises(Exception, _check, float_frame_fill2) + msg = "This routine assumes NaN fill value" + with pytest.raises(TypeError, match=msg): + _check(float_frame_fill0) + with pytest.raises(TypeError, match=msg): + _check(float_frame_fill2) def test_transpose(self, float_frame, float_frame_int_kind, float_frame_dense, diff --git a/pandas/tests/sparse/series/test_series.py b/pandas/tests/sparse/series/test_series.py index 7eed47d0de888..93cf629f20957 100644 --- a/pandas/tests/sparse/series/test_series.py +++ b/pandas/tests/sparse/series/test_series.py @@ -452,12 +452,13 @@ def _check_getitem(sp, dense): _check_getitem(self.ziseries, self.ziseries.to_dense()) # exception handling - pytest.raises(Exception, self.bseries.__getitem__, - len(self.bseries) + 1) + with pytest.raises(IndexError, match="Out of bounds access"): + self.bseries[len(self.bseries) + 1] # index not contained - pytest.raises(Exception, self.btseries.__getitem__, - self.btseries.index[-1] + BDay()) + msg = r"Timestamp\('2011-01-31 00:00:00', freq='B'\)" + with pytest.raises(KeyError, match=msg): + self.btseries[self.btseries.index[-1] + BDay()] def test_get_get_value(self): tm.assert_almost_equal(self.bseries.get(10), self.bseries[10]) @@ -523,8 +524,9 @@ def _compare(idx): self._check_all(_compare_with_dense) - pytest.raises(Exception, self.bseries.take, - [0, len(self.bseries) + 1]) + msg = "index 21 is out of bounds for size 20" + with pytest.raises(IndexError, match=msg): + self.bseries.take([0, len(self.bseries) + 1]) # Corner case # XXX: changed test. Why wsa this considered a corner case? @@ -1138,25 +1140,35 @@ def test_to_coo_text_names_text_row_levels_nosort(self): def test_to_coo_bad_partition_nonnull_intersection(self): ss = self.sparse_series[0] - pytest.raises(ValueError, ss.to_coo, ['A', 'B', 'C'], ['C', 'D']) + msg = "Is not a partition because intersection is not null" + with pytest.raises(ValueError, match=msg): + ss.to_coo(['A', 'B', 'C'], ['C', 'D']) def test_to_coo_bad_partition_small_union(self): ss = self.sparse_series[0] - pytest.raises(ValueError, ss.to_coo, ['A'], ['C', 'D']) + msg = "Is not a partition because union is not the whole" + with pytest.raises(ValueError, match=msg): + ss.to_coo(['A'], ['C', 'D']) def test_to_coo_nlevels_less_than_two(self): ss = self.sparse_series[0] ss.index = np.arange(len(ss.index)) - pytest.raises(ValueError, ss.to_coo) + msg = "to_coo requires MultiIndex with nlevels > 2" + with pytest.raises(ValueError, match=msg): + ss.to_coo() def test_to_coo_bad_ilevel(self): ss = self.sparse_series[0] - pytest.raises(KeyError, ss.to_coo, ['A', 'B'], ['C', 'D', 'E']) + with pytest.raises(KeyError, match="Level E not found"): + ss.to_coo(['A', 'B'], ['C', 'D', 'E']) def test_to_coo_duplicate_index_entries(self): ss = pd.concat([self.sparse_series[0], self.sparse_series[0]]).to_sparse() - pytest.raises(ValueError, ss.to_coo, ['A', 'B'], ['C', 'D']) + msg = ("Duplicate index entries are not allowed in to_coo" + " transformation") + with pytest.raises(ValueError, match=msg): + ss.to_coo(['A', 'B'], ['C', 'D']) def test_from_coo_dense_index(self): ss = SparseSeries.from_coo(self.coo_matrices[0], dense_index=True) diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index 621572da57541..e6f21a7b47c3b 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -9,6 +9,7 @@ from pandas._libs.tslibs.frequencies import ( INVALID_FREQ_ERR_MSG, get_freq_code, get_freq_str) import pandas._libs.tslibs.offsets as liboffsets +from pandas._libs.tslibs.offsets import ApplyTypeError import pandas.compat as compat from pandas.compat import range from pandas.compat.numpy import np_datetime64_compat @@ -150,7 +151,8 @@ def test_sub(self): # offset2 attr return off = self.offset2 - with pytest.raises(Exception): + msg = "Cannot subtract datetime from offset" + with pytest.raises(TypeError, match=msg): off - self.d assert 2 * off - off == off @@ -736,7 +738,10 @@ def test_apply_large_n(self): assert rs == xp def test_apply_corner(self): - pytest.raises(TypeError, BDay().apply, BMonthEnd()) + msg = ("Only know how to combine business day with datetime or" + " timedelta") + with pytest.raises(ApplyTypeError, match=msg): + BDay().apply(BMonthEnd()) class TestBusinessHour(Base): @@ -812,7 +817,8 @@ def test_sub(self): # we have to override test_sub here becasue self.offset2 is not # defined as self._offset(2) off = self.offset2 - with pytest.raises(Exception): + msg = "Cannot subtract datetime from offset" + with pytest.raises(TypeError, match=msg): off - self.d assert 2 * off - off == off @@ -1796,7 +1802,10 @@ def test_apply_large_n(self): assert rs == xp def test_apply_corner(self): - pytest.raises(Exception, CDay().apply, BMonthEnd()) + msg = ("Only know how to combine trading day with datetime, datetime64" + " or timedelta") + with pytest.raises(ApplyTypeError, match=msg): + CDay().apply(BMonthEnd()) def test_holidays(self): # Define a TradingDay offset diff --git a/pandas/tests/tseries/offsets/test_yqm_offsets.py b/pandas/tests/tseries/offsets/test_yqm_offsets.py index 8023ee3139dd5..9ee03d2e886f3 100644 --- a/pandas/tests/tseries/offsets/test_yqm_offsets.py +++ b/pandas/tests/tseries/offsets/test_yqm_offsets.py @@ -713,7 +713,8 @@ class TestYearBegin(Base): _offset = YearBegin def test_misspecified(self): - pytest.raises(ValueError, YearBegin, month=13) + with pytest.raises(ValueError, match="Month must go from 1 to 12"): + YearBegin(month=13) offset_cases = [] offset_cases.append((YearBegin(), { @@ -804,7 +805,8 @@ class TestYearEnd(Base): _offset = YearEnd def test_misspecified(self): - pytest.raises(ValueError, YearEnd, month=13) + with pytest.raises(ValueError, match="Month must go from 1 to 12"): + YearEnd(month=13) offset_cases = [] offset_cases.append((YearEnd(), { @@ -900,8 +902,11 @@ class TestBYearBegin(Base): _offset = BYearBegin def test_misspecified(self): - pytest.raises(ValueError, BYearBegin, month=13) - pytest.raises(ValueError, BYearEnd, month=13) + msg = "Month must go from 1 to 12" + with pytest.raises(ValueError, match=msg): + BYearBegin(month=13) + with pytest.raises(ValueError, match=msg): + BYearEnd(month=13) offset_cases = [] offset_cases.append((BYearBegin(), { @@ -993,8 +998,11 @@ class TestBYearEndLagged(Base): _offset = BYearEnd def test_bad_month_fail(self): - pytest.raises(Exception, BYearEnd, month=13) - pytest.raises(Exception, BYearEnd, month=0) + msg = "Month must go from 1 to 12" + with pytest.raises(ValueError, match=msg): + BYearEnd(month=13) + with pytest.raises(ValueError, match=msg): + BYearEnd(month=0) offset_cases = [] offset_cases.append((BYearEnd(month=6), { From ce4720575f65df38e59472d7f4d8543ffe7a0c13 Mon Sep 17 00:00:00 2001 From: Max Bolingbroke Date: Sun, 3 Mar 2019 02:36:36 +0000 Subject: [PATCH 184/215] BUG: Fix potential segfault after pd.Categorical(pd.Series(...), categories=...) (#25368) --- doc/source/whatsnew/v0.24.2.rst | 2 ++ pandas/core/arrays/categorical.py | 13 ++++--------- .../tests/arrays/categorical/test_constructors.py | 12 ++++++++++++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 926239e7e5dc5..e80b1060e867d 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -31,6 +31,8 @@ Fixed Regressions - Fixed regression in :class:`TimedeltaIndex` where `np.sum(index)` incorrectly returned a zero-dimensional object instead of a scalar (:issue:`25282`) - Fixed regression in ``IntervalDtype`` construction where passing an incorrect string with 'Interval' as a prefix could result in a ``RecursionError``. (:issue:`25338`) +- Fixed regression in :class:`Categorical`, where constructing it from a categorical ``Series`` and an explicit ``categories=`` that differed from that in the ``Series`` created an invalid object which could trigger segfaults. (:issue:`25318`) + .. _whatsnew_0242.enhancements: Enhancements diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 37a24a54be8b1..7f77a5dcce613 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -323,14 +323,6 @@ def __init__(self, values, categories=None, ordered=None, dtype=None, # we may have dtype.categories be None, and we need to # infer categories in a factorization step futher below - if is_categorical(values): - # GH23814, for perf, if values._values already an instance of - # Categorical, set values to codes, and run fastpath - if (isinstance(values, (ABCSeries, ABCIndexClass)) and - isinstance(values._values, type(self))): - values = values._values.codes.copy() - fastpath = True - if fastpath: self._codes = coerce_indexer_dtype(values, dtype.categories) self._dtype = self._dtype.update_dtype(dtype) @@ -382,7 +374,7 @@ def __init__(self, values, categories=None, ordered=None, dtype=None, dtype = CategoricalDtype(categories, dtype.ordered) elif is_categorical_dtype(values): - old_codes = (values.cat.codes if isinstance(values, ABCSeries) + old_codes = (values._values.codes if isinstance(values, ABCSeries) else values.codes) codes = _recode_for_categories(old_codes, values.dtype.categories, dtype.categories) @@ -2625,6 +2617,9 @@ def _recode_for_categories(codes, old_categories, new_categories): if len(old_categories) == 0: # All null anyway, so just retain the nulls return codes.copy() + elif new_categories.equals(old_categories): + # Same categories, so no need to actually recode + return codes.copy() indexer = coerce_indexer_dtype(new_categories.get_indexer(old_categories), new_categories) new_codes = take_1d(indexer, codes.copy(), fill_value=-1) diff --git a/pandas/tests/arrays/categorical/test_constructors.py b/pandas/tests/arrays/categorical/test_constructors.py index 25c299692ceca..f07e3aba53cd4 100644 --- a/pandas/tests/arrays/categorical/test_constructors.py +++ b/pandas/tests/arrays/categorical/test_constructors.py @@ -212,6 +212,18 @@ def test_constructor(self): c = Categorical(np.array([], dtype='int64'), # noqa categories=[3, 2, 1], ordered=True) + def test_constructor_with_existing_categories(self): + # GH25318: constructing with pd.Series used to bogusly skip recoding + # categories + c0 = Categorical(["a", "b", "c", "a"]) + c1 = Categorical(["a", "b", "c", "a"], categories=["b", "c"]) + + c2 = Categorical(c0, categories=c1.categories) + tm.assert_categorical_equal(c1, c2) + + c3 = Categorical(Series(c0), categories=c1.categories) + tm.assert_categorical_equal(c1, c3) + def test_constructor_not_sequence(self): # https://github.com/pandas-dev/pandas/issues/16022 msg = r"^Parameter 'categories' must be list-like, was" From 0c193c654bba185e39057bc99245093dc877b7da Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Sun, 3 Mar 2019 02:59:51 +0000 Subject: [PATCH 185/215] Make DataFrame.to_html output full content (#24841) --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/io/formats/html.py | 13 +++++++++++-- pandas/tests/io/formats/test_to_html.py | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index d1f1ea862110e..eb6e172648ef7 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -209,6 +209,7 @@ MultiIndex I/O ^^^ +- Bug in :func:`DataFrame.to_html()` where values were truncated using display options instead of outputting the full content (:issue:`17004`) - Fixed bug in missing text when using :meth:`to_clipboard` if copying utf-16 characters in Python 3 on Windows (:issue:`25040`) - Bug in :func:`read_json` for ``orient='table'`` when it tries to infer dtypes by default, which is not applicable as dtypes are already defined in the JSON schema (:issue:`21345`) - Bug in :func:`read_json` for ``orient='table'`` and float index, as it infers index dtype by default, which is not applicable because index dtype is already defined in the JSON schema (:issue:`25433`) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 456583509565e..66d13bf2668f9 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -12,7 +12,7 @@ from pandas.core.dtypes.generic import ABCMultiIndex -from pandas import compat +from pandas import compat, option_context from pandas.core.config import get_option from pandas.io.common import _is_url @@ -320,9 +320,15 @@ def _write_header(self, indent): self.write('', indent) + def _get_formatted_values(self): + with option_context('display.max_colwidth', 999999): + fmt_values = {i: self.fmt._format_col(i) + for i in range(self.ncols)} + return fmt_values + def _write_body(self, indent): self.write('', indent) - fmt_values = {i: self.fmt._format_col(i) for i in range(self.ncols)} + fmt_values = self._get_formatted_values() # write values if self.fmt.index and isinstance(self.frame.index, ABCMultiIndex): @@ -486,6 +492,9 @@ class NotebookFormatter(HTMLFormatter): DataFrame._repr_html_() and DataFrame.to_html(notebook=True) """ + def _get_formatted_values(self): + return {i: self.fmt._format_col(i) for i in range(self.ncols)} + def write_style(self): # We use the "scoped" attribute here so that the desired # style properties for the data frame are not then applied diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index 554cfd306e2a7..428f1411a10a6 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -15,6 +15,15 @@ import pandas.io.formats.format as fmt +lorem_ipsum = ( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod" + " tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim" + " veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex" + " ea commodo consequat. Duis aute irure dolor in reprehenderit in" + " voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur" + " sint occaecat cupidatat non proident, sunt in culpa qui officia" + " deserunt mollit anim id est laborum.") + def expected_html(datapath, name): """ @@ -600,3 +609,17 @@ def test_to_html_render_links(render_links, expected, datapath): result = df.to_html(render_links=render_links) expected = expected_html(datapath, expected) assert result == expected + + +@pytest.mark.parametrize('method,expected', [ + ('to_html', lambda x:lorem_ipsum), + ('_repr_html_', lambda x:lorem_ipsum[:x - 4] + '...') # regression case +]) +@pytest.mark.parametrize('max_colwidth', [10, 20, 50, 100]) +def test_ignore_display_max_colwidth(method, expected, max_colwidth): + # see gh-17004 + df = DataFrame([lorem_ipsum]) + with pd.option_context('display.max_colwidth', max_colwidth): + result = getattr(df, method)() + expected = expected(max_colwidth) + assert expected in result From 362f4e5ae6afce341d1185b3f906dcfa29a28c29 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 19 Jan 2019 20:50:11 -0800 Subject: [PATCH 186/215] Added support for iso time --- pandas/_libs/tslibs/strptime.pyx | 70 ++++++++++++++++++-- pandas/tests/indexes/datetimes/test_tools.py | 47 +++++++++++++ 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 87658ae92175e..a63a0080006b6 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -54,7 +54,10 @@ cdef dict _parse_code_table = {'y': 0, 'W': 16, 'Z': 17, 'p': 18, # an additional key, only with I - 'z': 19} + 'z': 19, + 'G': 20, + 'V': 21, + 'u': 22} def array_strptime(object[:] values, object fmt, @@ -76,7 +79,7 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal + int week_of_year, week_of_year_start, parse_code, ordinal, iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -169,13 +172,14 @@ def array_strptime(object[:] values, object fmt, raise ValueError("time data %r does not match format " "%r (search)" % (values[i], fmt)) + iso_year = -1 year = 1900 month = day = 1 hour = minute = second = ns = us = 0 timezone = None # Default to -1 to signify that values not known; not critical to have, # though - week_of_year = -1 + iso_week = week_of_year = -1 week_of_year_start = -1 # weekday and julian defaulted to -1 so as to signal need to calculate # values @@ -265,13 +269,43 @@ def array_strptime(object[:] values, object fmt, timezone = pytz.timezone(found_dict['Z']) elif parse_code == 19: timezone = parse_timezone_directive(found_dict['z']) + elif parse_code == 20: + iso_year = int(found_dict['G']) + elif parse_code == 21: + iso_week = int(found_dict['V']) + elif parse_code == 22: + weekday = int(found_dict['u']) + weekday -= 1 + + # don't assume default values for ISO week/year + if iso_year != -1: + if iso_week == -1 or weekday == -1: + raise ValueError("ISO year directive '%G' must be used with " + "the ISO week directive '%V' and a weekday " + "directive '%A', '%a', '%w', or '%u'.") + if julian != -1: + raise ValueError("Day of the year directive '%j' is not " + "compatible with ISO year directive '%G'. " + "Use '%Y' instead.") + elif year != -1 and week_of_year == -1 and iso_week != -1: + if weekday == -1: + raise ValueError("ISO week directive '%V' must be used with " + "the ISO year directive '%G' and a weekday " + "directive '%A', '%a', '%w', or '%u'.") + else: + raise ValueError("ISO week directive '%V' is incompatible with" + " the year directive '%Y'. Use the ISO year " + "'%G' instead.") # If we know the wk of the year and what day of that wk, we can figure # out the Julian day of the year. - if julian == -1 and week_of_year != -1 and weekday != -1: - week_starts_Mon = True if week_of_year_start == 0 else False - julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + if julian == -1 and weekday != -1: + if week_of_year != -1: + week_starts_Mon = True if week_of_year_start == 0 else False + julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, + week_starts_Mon) + elif iso_year != -1 and iso_week != -1: + year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. @@ -511,6 +545,7 @@ class TimeRE(dict): # The " \d" part of the regex is to make %c from ANSI C work 'd': r"(?P3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])", 'f': r"(?P[0-9]{1,9})", + 'G': r"(?P\d\d\d\d)", 'H': r"(?P2[0-3]|[0-1]\d|\d)", 'I': r"(?P1[0-2]|0[1-9]|[1-9])", 'j': (r"(?P36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|" @@ -518,7 +553,9 @@ class TimeRE(dict): 'm': r"(?P1[0-2]|0[1-9]|[1-9])", 'M': r"(?P[0-5]\d|\d)", 'S': r"(?P6[0-1]|[0-5]\d|\d)", + 'u': r"(?P[1-7])", 'U': r"(?P5[0-3]|[0-4]\d|\d)", + 'V': r"(?P5[0-3]|0[1-9]|[1-4]\d|\d)", 'w': r"(?P[0-6])", # W is set below by using 'U' 'y': r"(?P\d\d)", @@ -620,6 +657,25 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year, return 1 + days_to_week + day_of_week +cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): + """Calculate the Julian day based on the ISO 8601 year, week, and weekday. + ISO weeks start on Mondays, with week 01 being the week containing 4 Jan. + ISO week days range from 1 (Monday) to 7 (Sunday).""" + + cdef: + int correction, ordinal + + correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 + ordinal = (iso_week * 7) + iso_weekday - correction + # ordinal may be negative or 0 now, which means the date is in the previous + # calendar year + if ordinal < 1: + ordinal += datetime_date(iso_year, 1, 1).toordinal() + iso_year -= 1 + ordinal -= datetime_date(iso_year, 1, 1).toordinal() + return iso_year, ordinal + + cdef parse_timezone_directive(object z): """ Parse the '%z' directive and return a pytz.FixedOffset diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index dd914d8a79837..c42dfcbac1c3d 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -247,6 +247,53 @@ def test_to_datetime_parse_timezone_keeps_name(self): class TestToDatetime(object): + @pytest.mark.parametrize("s, _format, dt", [ + ['2015-1-1', '%G-%V-%u', datetime(2014, 12, 29, 0, 0)], + ['2015-1-4', '%G-%V-%u', datetime(2015, 1, 1, 0, 0)], + ['2015-1-7', '%G-%V-%u', datetime(2015, 1, 4, 0, 0)] + ]) + def test_to_datetime_iso_week_year_format(self, s, _format, dt): + assert to_datetime(s, format=_format) == dt + + @pytest.mark.parametrize("msg, s, _format", [ + ["ISO week directive '%V' must be used with the ISO year directive " + "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 50", + "%Y %V"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 51", + "%G %V"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 " + "Monday", "%G %A"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 Mon", + "%G %a"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 6", + "%G %w"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 6", + "%G %u"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "2051", + "%G"], + ["Day of the year directive '%j' is not compatible with ISO year " + "directive '%G'. Use '%Y' instead.", "1999 51 6 256", "%G %V %u %j"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 Sunday", "%Y %V %A"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 Sun", "%Y %V %a"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 1", "%Y %V %w"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 1", "%Y %V %u"], + ["ISO week directive '%V' must be used with the ISO year directive " + "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] + ]) + def test_ValueError_iso_week_year(self, msg, s, _format): + with tm.assert_raises_regex(ValueError, msg): + to_datetime(s, format=_format) + @pytest.mark.parametrize('tz', [None, 'US/Central']) def test_to_datetime_dtarr(self, tz): # DatetimeArray From e6c4f9db279b32f2a8425467766c7643fa8d401e Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 19 Jan 2019 21:22:52 -0800 Subject: [PATCH 187/215] Added whatsnew entry? --- doc/source/whatsnew/v0.24.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index a49ea2cf493a6..7ca996ab62851 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -364,6 +364,7 @@ See the :ref:`Advanced documentation on renaming` for more Other Enhancements ^^^^^^^^^^^^^^^^^^ +- Added support for %G strptime directive in parsing datetimes - :func:`merge` now directly allows merge between objects of type ``DataFrame`` and named ``Series``, without the need to convert the ``Series`` object into a ``DataFrame`` beforehand (:issue:`21220`) - ``ExcelWriter`` now accepts ``mode`` as a keyword argument, enabling append to existing workbooks when using the ``openpyxl`` engine (:issue:`3441`) - ``FrozenList`` has gained the ``.union()`` and ``.difference()`` methods. This functionality greatly simplifies groupby's that rely on explicitly excluding certain columns. See :ref:`Splitting an object into groups ` for more information (:issue:`15475`, :issue:`15506`). From b48d0fbe8fbc34eb9ef6c9eb779f0d656a873b5d Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 10:38:00 -0800 Subject: [PATCH 188/215] n --- pandas/_libs/tslibs/strptime.pyx | 7 ++++--- pandas/tests/indexes/datetimes/test_tools.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index a63a0080006b6..dc3ec51a90fea 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -79,7 +79,8 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal, iso_week, iso_year + int week_of_year, week_of_year_start, parse_code, ordinal, + iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -303,7 +304,7 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian @@ -664,7 +665,7 @@ cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): cdef: int correction, ordinal - + correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 ordinal = (iso_week * 7) + iso_weekday - correction # ordinal may be negative or 0 now, which means the date is in the previous diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index c42dfcbac1c3d..d09d352dea500 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -291,7 +291,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] ]) def test_ValueError_iso_week_year(self, msg, s, _format): - with tm.assert_raises_regex(ValueError, msg): + with pytest.raises(ValueError, message=msg): to_datetime(s, format=_format) @pytest.mark.parametrize('tz', [None, 'US/Central']) From cf16177bd4664a76b2828545efb7bb1677a549c6 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 13:11:01 -0800 Subject: [PATCH 189/215] Fix linting error --- pandas/_libs/tslibs/strptime.pyx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index dc3ec51a90fea..cfada70db525b 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -79,8 +79,8 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal, - iso_week, iso_year + int week_of_year, week_of_year_start, parse_code, ordinal + int iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -304,9 +304,10 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: - year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) + year, julian = _calc_julian_from_V(iso_year, iso_week, + weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. From a323f74cf20bf594228110bf76803ef11e83eded Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 13:32:06 -0800 Subject: [PATCH 190/215] Add changes from reviewer --- doc/source/whatsnew/v0.24.0.rst | 2 +- pandas/tests/indexes/datetimes/test_tools.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 7ca996ab62851..83cb0acf2124e 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -364,7 +364,7 @@ See the :ref:`Advanced documentation on renaming` for more Other Enhancements ^^^^^^^^^^^^^^^^^^ -- Added support for %G strptime directive in parsing datetimes +- Added support for ISO week year format when parsing datetimes using `to_datetime` (:issue:`16607`) - :func:`merge` now directly allows merge between objects of type ``DataFrame`` and named ``Series``, without the need to convert the ``Series`` object into a ``DataFrame`` beforehand (:issue:`21220`) - ``ExcelWriter`` now accepts ``mode`` as a keyword argument, enabling append to existing workbooks when using the ``openpyxl`` engine (:issue:`3441`) - ``FrozenList`` has gained the ``.union()`` and ``.difference()`` methods. This functionality greatly simplifies groupby's that rely on explicitly excluding certain columns. See :ref:`Splitting an object into groups ` for more information (:issue:`15475`, :issue:`15506`). diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index d09d352dea500..a517285f0b5cc 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -253,6 +253,7 @@ class TestToDatetime(object): ['2015-1-7', '%G-%V-%u', datetime(2015, 1, 4, 0, 0)] ]) def test_to_datetime_iso_week_year_format(self, s, _format, dt): + # See GH#16607 assert to_datetime(s, format=_format) == dt @pytest.mark.parametrize("msg, s, _format", [ @@ -291,6 +292,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] ]) def test_ValueError_iso_week_year(self, msg, s, _format): + # See GH#16607 with pytest.raises(ValueError, message=msg): to_datetime(s, format=_format) From cdd66c49fd9b58c2005ae785cafdf87bbaa2b6ee Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 21:20:52 -0800 Subject: [PATCH 191/215] Fix errors found in azure --- pandas/_libs/tslibs/strptime.pyx | 4 ++-- pandas/tests/indexes/datetimes/test_tools.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index cfada70db525b..cfe402b11092c 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -304,10 +304,10 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: year, julian = _calc_julian_from_V(iso_year, iso_week, - weekday + 1) + weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index a517285f0b5cc..a72aacce2f86d 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -293,7 +293,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): ]) def test_ValueError_iso_week_year(self, msg, s, _format): # See GH#16607 - with pytest.raises(ValueError, message=msg): + with pytest.raises(ValueError, match=msg): to_datetime(s, format=_format) @pytest.mark.parametrize('tz', [None, 'US/Central']) From baad2633058f9bb6389de7543bda23fc283ddd16 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 27 Jan 2019 16:44:25 -0800 Subject: [PATCH 192/215] Making change to one thing --- pandas/_libs/tslibs/strptime.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index cfe402b11092c..8f45879feaa3a 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -302,7 +302,7 @@ def array_strptime(object[:] values, object fmt, # out the Julian day of the year. if julian == -1 and weekday != -1: if week_of_year != -1: - week_starts_Mon = True if week_of_year_start == 0 else False + week_starts_Mon = week_of_year_start == 0 julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, week_starts_Mon) elif iso_year != -1 and iso_week != -1: From 42b4c975e55c59da7868f99f9949841aca12a08d Mon Sep 17 00:00:00 2001 From: Justin Zheng Date: Sat, 2 Mar 2019 20:46:44 -0800 Subject: [PATCH 193/215] BUG-16807-1 SparseFrame fills with default_fill_value if data is None (#24842) Closes gh-16807. --- doc/source/whatsnew/v0.25.0.rst | 2 +- pandas/core/sparse/frame.py | 4 ++-- pandas/tests/sparse/frame/test_frame.py | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index eb6e172648ef7..124ec8f4ab92c 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -247,7 +247,7 @@ Sparse ^^^^^^ - Significant speedup in `SparseArray` initialization that benefits most operations, fixing performance regression introduced in v0.20.0 (:issue:`24985`) -- +- Bug in :class:`SparseFrame` constructor where passing ``None`` as the data would cause ``default_fill_value`` to be ignored (:issue:`16807`) - diff --git a/pandas/core/sparse/frame.py b/pandas/core/sparse/frame.py index e0af11d13774c..2d54b82a3c844 100644 --- a/pandas/core/sparse/frame.py +++ b/pandas/core/sparse/frame.py @@ -124,8 +124,8 @@ def __init__(self, data=None, index=None, columns=None, default_kind=None, columns = Index([]) else: for c in columns: - data[c] = SparseArray(np.nan, index=index, - kind=self._default_kind, + data[c] = SparseArray(self._default_fill_value, + index=index, kind=self._default_kind, fill_value=self._default_fill_value) mgr = to_manager(data, columns, index) if dtype is not None: diff --git a/pandas/tests/sparse/frame/test_frame.py b/pandas/tests/sparse/frame/test_frame.py index b31738794c854..888d1fa1bfe45 100644 --- a/pandas/tests/sparse/frame/test_frame.py +++ b/pandas/tests/sparse/frame/test_frame.py @@ -270,6 +270,19 @@ def test_type_coercion_at_construction(self): default_fill_value=0) tm.assert_sp_frame_equal(result, expected) + def test_default_dtype(self): + result = pd.SparseDataFrame(columns=list('ab'), index=range(2)) + expected = pd.SparseDataFrame([[np.nan, np.nan], [np.nan, np.nan]], + columns=list('ab'), index=range(2)) + tm.assert_sp_frame_equal(result, expected) + + def test_nan_data_with_int_dtype_raises_error(self): + sdf = pd.SparseDataFrame([[np.nan, np.nan], [np.nan, np.nan]], + columns=list('ab'), index=range(2)) + msg = "Cannot convert non-finite values" + with pytest.raises(ValueError, match=msg): + pd.SparseDataFrame(sdf, dtype=np.int64) + def test_dtypes(self): df = DataFrame(np.random.randn(10000, 4)) df.loc[:9998] = np.nan @@ -1263,6 +1276,14 @@ def test_notna(self): 'B': [True, False, True, True, False]}) tm.assert_frame_equal(res.to_dense(), exp) + def test_default_fill_value_with_no_data(self): + # GH 16807 + expected = pd.SparseDataFrame([[1.0, 1.0], [1.0, 1.0]], + columns=list('ab'), index=range(2)) + result = pd.SparseDataFrame(columns=list('ab'), index=range(2), + default_fill_value=1.0) + tm.assert_frame_equal(expected, result) + class TestSparseDataFrameArithmetic(object): From 149b7f8f08d9f7db066467254e1d5c23ba7bc79f Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 2 Mar 2019 21:20:15 -0800 Subject: [PATCH 194/215] Moved to 0.25.0 --- doc/source/whatsnew/v0.24.0.rst | 1 - doc/source/whatsnew/v0.25.0.rst | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 83cb0acf2124e..a49ea2cf493a6 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -364,7 +364,6 @@ See the :ref:`Advanced documentation on renaming` for more Other Enhancements ^^^^^^^^^^^^^^^^^^ -- Added support for ISO week year format when parsing datetimes using `to_datetime` (:issue:`16607`) - :func:`merge` now directly allows merge between objects of type ``DataFrame`` and named ``Series``, without the need to convert the ``Series`` object into a ``DataFrame`` beforehand (:issue:`21220`) - ``ExcelWriter`` now accepts ``mode`` as a keyword argument, enabling append to existing workbooks when using the ``openpyxl`` engine (:issue:`3441`) - ``FrozenList`` has gained the ``.union()`` and ``.difference()`` methods. This functionality greatly simplifies groupby's that rely on explicitly excluding certain columns. See :ref:`Splitting an object into groups ` for more information (:issue:`15475`, :issue:`15506`). diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index eb6e172648ef7..5d7d9e264877b 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -134,7 +134,7 @@ Categorical Datetimelike ^^^^^^^^^^^^ -- +- Added support for ISO week year format ('%G-%V-%u') when parsing datetimes using :meth: `to_datetime` (:issue:`16607`) - - From bd49d2f2af5c44d4f96031757743ba83e6b29408 Mon Sep 17 00:00:00 2001 From: yehia67 Date: Sun, 3 Mar 2019 22:30:18 +0200 Subject: [PATCH 195/215] DOC: Add conda uninstall pandas to contributing guide (#25490) * fix #25487 add modify documentation --- doc/source/development/contributing.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/development/contributing.rst b/doc/source/development/contributing.rst index a87a66cd08ad1..434df772ae9d1 100644 --- a/doc/source/development/contributing.rst +++ b/doc/source/development/contributing.rst @@ -178,6 +178,7 @@ We'll now kick off a three-step process: # Create and activate the build environment conda env create -f environment.yml conda activate pandas-dev + conda uninstall --force pandas # or with older versions of Anaconda: source activate pandas-dev From 705c44299332f05786b19d7e44363a38f5be03bf Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 4 Mar 2019 10:39:41 -0800 Subject: [PATCH 196/215] fix segfault when running with cython coverage enabled, xref cython#2879 (#25529) --- pandas/_libs/tslibs/period.pyx | 96 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/pandas/_libs/tslibs/period.pyx b/pandas/_libs/tslibs/period.pyx index e38e9a1ca5df6..a5a50ea59753d 100644 --- a/pandas/_libs/tslibs/period.pyx +++ b/pandas/_libs/tslibs/period.pyx @@ -138,11 +138,11 @@ cdef int64_t get_daytime_conversion_factor(int from_index, int to_index) nogil: return daytime_conversion_factor_matrix[row - 6][col - 6] -cdef int64_t nofunc(int64_t ordinal, asfreq_info *af_info): - return np.iinfo(np.int32).min +cdef int64_t nofunc(int64_t ordinal, asfreq_info *af_info) nogil: + return INT32_MIN -cdef int64_t no_op(int64_t ordinal, asfreq_info *af_info): +cdef int64_t no_op(int64_t ordinal, asfreq_info *af_info) nogil: return ordinal @@ -270,7 +270,8 @@ cdef int64_t DtoB_weekday(int64_t unix_date) nogil: return ((unix_date + 4) // 7) * 5 + ((unix_date + 4) % 7) - 4 -cdef int64_t DtoB(npy_datetimestruct *dts, int roll_back, int64_t unix_date): +cdef int64_t DtoB(npy_datetimestruct *dts, int roll_back, + int64_t unix_date) nogil: cdef: int day_of_week = dayofweek(dts.year, dts.month, dts.day) @@ -286,21 +287,23 @@ cdef int64_t DtoB(npy_datetimestruct *dts, int roll_back, int64_t unix_date): return DtoB_weekday(unix_date) -cdef inline int64_t upsample_daytime(int64_t ordinal, asfreq_info *af_info): +cdef inline int64_t upsample_daytime(int64_t ordinal, + asfreq_info *af_info) nogil: if (af_info.is_end): return (ordinal + 1) * af_info.intraday_conversion_factor - 1 else: return ordinal * af_info.intraday_conversion_factor -cdef inline int64_t downsample_daytime(int64_t ordinal, asfreq_info *af_info): +cdef inline int64_t downsample_daytime(int64_t ordinal, + asfreq_info *af_info) nogil: return ordinal // (af_info.intraday_conversion_factor) cdef inline int64_t transform_via_day(int64_t ordinal, asfreq_info *af_info, freq_conv_func first_func, - freq_conv_func second_func): + freq_conv_func second_func) nogil: cdef: int64_t result @@ -313,7 +316,7 @@ cdef inline int64_t transform_via_day(int64_t ordinal, # Conversion _to_ Daily Freq cdef void AtoD_ym(int64_t ordinal, int64_t *year, - int *month, asfreq_info *af_info): + int *month, asfreq_info *af_info) nogil: year[0] = ordinal + 1970 month[0] = 1 @@ -327,7 +330,7 @@ cdef void AtoD_ym(int64_t ordinal, int64_t *year, year[0] -= 1 -cdef int64_t asfreq_AtoDT(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_AtoDT(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int64_t unix_date, year int month @@ -341,7 +344,7 @@ cdef int64_t asfreq_AtoDT(int64_t ordinal, asfreq_info *af_info): cdef void QtoD_ym(int64_t ordinal, int *year, - int *month, asfreq_info *af_info): + int *month, asfreq_info *af_info) nogil: year[0] = ordinal // 4 + 1970 month[0] = (ordinal % 4) * 3 + 1 @@ -353,7 +356,7 @@ cdef void QtoD_ym(int64_t ordinal, int *year, year[0] -= 1 -cdef int64_t asfreq_QtoDT(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_QtoDT(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int64_t unix_date int year, month @@ -366,12 +369,12 @@ cdef int64_t asfreq_QtoDT(int64_t ordinal, asfreq_info *af_info): return upsample_daytime(unix_date, af_info) -cdef void MtoD_ym(int64_t ordinal, int *year, int *month): +cdef void MtoD_ym(int64_t ordinal, int *year, int *month) nogil: year[0] = ordinal // 12 + 1970 month[0] = ordinal % 12 + 1 -cdef int64_t asfreq_MtoDT(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_MtoDT(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int64_t unix_date int year, month @@ -384,7 +387,7 @@ cdef int64_t asfreq_MtoDT(int64_t ordinal, asfreq_info *af_info): return upsample_daytime(unix_date, af_info) -cdef int64_t asfreq_WtoDT(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_WtoDT(int64_t ordinal, asfreq_info *af_info) nogil: ordinal = (ordinal * 7 + af_info.from_end - 4 + (7 - 1) * (af_info.is_end - 1)) return upsample_daytime(ordinal, af_info) @@ -393,7 +396,7 @@ cdef int64_t asfreq_WtoDT(int64_t ordinal, asfreq_info *af_info): # -------------------------------------------------------------------- # Conversion _to_ BusinessDay Freq -cdef int64_t asfreq_AtoB(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_AtoB(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int roll_back npy_datetimestruct dts @@ -404,7 +407,7 @@ cdef int64_t asfreq_AtoB(int64_t ordinal, asfreq_info *af_info): return DtoB(&dts, roll_back, unix_date) -cdef int64_t asfreq_QtoB(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_QtoB(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int roll_back npy_datetimestruct dts @@ -415,7 +418,7 @@ cdef int64_t asfreq_QtoB(int64_t ordinal, asfreq_info *af_info): return DtoB(&dts, roll_back, unix_date) -cdef int64_t asfreq_MtoB(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_MtoB(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int roll_back npy_datetimestruct dts @@ -426,7 +429,7 @@ cdef int64_t asfreq_MtoB(int64_t ordinal, asfreq_info *af_info): return DtoB(&dts, roll_back, unix_date) -cdef int64_t asfreq_WtoB(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_WtoB(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int roll_back npy_datetimestruct dts @@ -437,7 +440,7 @@ cdef int64_t asfreq_WtoB(int64_t ordinal, asfreq_info *af_info): return DtoB(&dts, roll_back, unix_date) -cdef int64_t asfreq_DTtoB(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_DTtoB(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int roll_back npy_datetimestruct dts @@ -452,7 +455,7 @@ cdef int64_t asfreq_DTtoB(int64_t ordinal, asfreq_info *af_info): # ---------------------------------------------------------------------- # Conversion _from_ Daily Freq -cdef int64_t asfreq_DTtoA(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_DTtoA(int64_t ordinal, asfreq_info *af_info) nogil: cdef: npy_datetimestruct dts @@ -464,7 +467,7 @@ cdef int64_t asfreq_DTtoA(int64_t ordinal, asfreq_info *af_info): return (dts.year - 1970) -cdef int DtoQ_yq(int64_t ordinal, asfreq_info *af_info, int *year): +cdef int DtoQ_yq(int64_t ordinal, asfreq_info *af_info, int *year) nogil: cdef: npy_datetimestruct dts int quarter @@ -485,7 +488,7 @@ cdef int DtoQ_yq(int64_t ordinal, asfreq_info *af_info, int *year): return quarter -cdef int64_t asfreq_DTtoQ(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_DTtoQ(int64_t ordinal, asfreq_info *af_info) nogil: cdef: int year, quarter @@ -495,7 +498,7 @@ cdef int64_t asfreq_DTtoQ(int64_t ordinal, asfreq_info *af_info): return ((year - 1970) * 4 + quarter - 1) -cdef int64_t asfreq_DTtoM(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_DTtoM(int64_t ordinal, asfreq_info *af_info) nogil: cdef: npy_datetimestruct dts @@ -504,7 +507,7 @@ cdef int64_t asfreq_DTtoM(int64_t ordinal, asfreq_info *af_info): return ((dts.year - 1970) * 12 + dts.month - 1) -cdef int64_t asfreq_DTtoW(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_DTtoW(int64_t ordinal, asfreq_info *af_info) nogil: ordinal = downsample_daytime(ordinal, af_info) return (ordinal + 3 - af_info.to_end) // 7 + 1 @@ -512,30 +515,30 @@ cdef int64_t asfreq_DTtoW(int64_t ordinal, asfreq_info *af_info): # -------------------------------------------------------------------- # Conversion _from_ BusinessDay Freq -cdef int64_t asfreq_BtoDT(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_BtoDT(int64_t ordinal, asfreq_info *af_info) nogil: ordinal = ((ordinal + 3) // 5) * 7 + (ordinal + 3) % 5 -3 return upsample_daytime(ordinal, af_info) -cdef int64_t asfreq_BtoA(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_BtoA(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_BtoDT, asfreq_DTtoA) -cdef int64_t asfreq_BtoQ(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_BtoQ(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_BtoDT, asfreq_DTtoQ) -cdef int64_t asfreq_BtoM(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_BtoM(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_BtoDT, asfreq_DTtoM) -cdef int64_t asfreq_BtoW(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_BtoW(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_BtoDT, asfreq_DTtoW) @@ -544,25 +547,25 @@ cdef int64_t asfreq_BtoW(int64_t ordinal, asfreq_info *af_info): # ---------------------------------------------------------------------- # Conversion _from_ Annual Freq -cdef int64_t asfreq_AtoA(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_AtoA(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_AtoDT, asfreq_DTtoA) -cdef int64_t asfreq_AtoQ(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_AtoQ(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_AtoDT, asfreq_DTtoQ) -cdef int64_t asfreq_AtoM(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_AtoM(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_AtoDT, asfreq_DTtoM) -cdef int64_t asfreq_AtoW(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_AtoW(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_AtoDT, asfreq_DTtoW) @@ -571,25 +574,25 @@ cdef int64_t asfreq_AtoW(int64_t ordinal, asfreq_info *af_info): # ---------------------------------------------------------------------- # Conversion _from_ Quarterly Freq -cdef int64_t asfreq_QtoQ(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_QtoQ(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_QtoDT, asfreq_DTtoQ) -cdef int64_t asfreq_QtoA(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_QtoA(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_QtoDT, asfreq_DTtoA) -cdef int64_t asfreq_QtoM(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_QtoM(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_QtoDT, asfreq_DTtoM) -cdef int64_t asfreq_QtoW(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_QtoW(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_QtoDT, asfreq_DTtoW) @@ -598,19 +601,19 @@ cdef int64_t asfreq_QtoW(int64_t ordinal, asfreq_info *af_info): # ---------------------------------------------------------------------- # Conversion _from_ Monthly Freq -cdef int64_t asfreq_MtoA(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_MtoA(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_MtoDT, asfreq_DTtoA) -cdef int64_t asfreq_MtoQ(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_MtoQ(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_MtoDT, asfreq_DTtoQ) -cdef int64_t asfreq_MtoW(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_MtoW(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_MtoDT, asfreq_DTtoW) @@ -619,25 +622,25 @@ cdef int64_t asfreq_MtoW(int64_t ordinal, asfreq_info *af_info): # ---------------------------------------------------------------------- # Conversion _from_ Weekly Freq -cdef int64_t asfreq_WtoA(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_WtoA(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_WtoDT, asfreq_DTtoA) -cdef int64_t asfreq_WtoQ(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_WtoQ(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_WtoDT, asfreq_DTtoQ) -cdef int64_t asfreq_WtoM(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_WtoM(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_WtoDT, asfreq_DTtoM) -cdef int64_t asfreq_WtoW(int64_t ordinal, asfreq_info *af_info): +cdef int64_t asfreq_WtoW(int64_t ordinal, asfreq_info *af_info) nogil: return transform_via_day(ordinal, af_info, asfreq_WtoDT, asfreq_DTtoW) @@ -971,7 +974,7 @@ cdef int get_yq(int64_t ordinal, int freq, int *quarter, int *year): return qtr_freq -cdef inline int month_to_quarter(int month): +cdef inline int month_to_quarter(int month) nogil: return (month - 1) // 3 + 1 @@ -1024,9 +1027,6 @@ def periodarr_to_dt64arr(int64_t[:] periodarr, int freq): with nogil: for i in range(l): - if periodarr[i] == NPY_NAT: - out[i] = NPY_NAT - continue out[i] = period_ordinal_to_dt64(periodarr[i], freq) return out.base # .base to access underlying np.ndarray From f85f7a153d310137bcf92683191a5ce5bc57db58 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 4 Mar 2019 10:40:36 -0800 Subject: [PATCH 197/215] TST: inline empty_frame = DataFrame({}) fixture (#24886) --- pandas/tests/frame/common.py | 2 +- pandas/tests/frame/conftest.py | 8 -------- pandas/tests/frame/test_analytics.py | 4 +++- pandas/tests/frame/test_api.py | 7 +++++-- pandas/tests/frame/test_apply.py | 12 +++++++++--- pandas/tests/frame/test_block_internals.py | 4 +++- pandas/tests/frame/test_constructors.py | 2 +- pandas/tests/frame/test_reshape.py | 2 +- pandas/tests/series/conftest.py | 9 --------- pandas/tests/series/test_constructors.py | 4 +++- 10 files changed, 26 insertions(+), 28 deletions(-) diff --git a/pandas/tests/frame/common.py b/pandas/tests/frame/common.py index 2ea087c0510bf..5624f7c1303b6 100644 --- a/pandas/tests/frame/common.py +++ b/pandas/tests/frame/common.py @@ -85,7 +85,7 @@ def tzframe(self): @cache_readonly def empty(self): - return pd.DataFrame({}) + return pd.DataFrame() @cache_readonly def ts1(self): diff --git a/pandas/tests/frame/conftest.py b/pandas/tests/frame/conftest.py index 69ee614ab8d2a..fbe03325a3ad9 100644 --- a/pandas/tests/frame/conftest.py +++ b/pandas/tests/frame/conftest.py @@ -127,14 +127,6 @@ def timezone_frame(): return df -@pytest.fixture -def empty_frame(): - """ - Fixture for empty DataFrame - """ - return DataFrame({}) - - @pytest.fixture def simple_frame(): """ diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index 43a45bb915819..994187a62d862 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -1096,7 +1096,9 @@ def test_operators_timedelta64(self): assert df['off1'].dtype == 'timedelta64[ns]' assert df['off2'].dtype == 'timedelta64[ns]' - def test_sum_corner(self, empty_frame): + def test_sum_corner(self): + empty_frame = DataFrame() + axis0 = empty_frame.sum(0) axis1 = empty_frame.sum(1) assert isinstance(axis0, Series) diff --git a/pandas/tests/frame/test_api.py b/pandas/tests/frame/test_api.py index 0934dd20638e4..e561b327e4fb0 100644 --- a/pandas/tests/frame/test_api.py +++ b/pandas/tests/frame/test_api.py @@ -142,7 +142,9 @@ def test_tab_completion(self): assert key not in dir(df) assert isinstance(df.__getitem__('A'), pd.DataFrame) - def test_not_hashable(self, empty_frame): + def test_not_hashable(self): + empty_frame = DataFrame() + df = self.klass([1]) pytest.raises(TypeError, hash, df) pytest.raises(TypeError, hash, empty_frame) @@ -171,7 +173,8 @@ def test_get_agg_axis(self, float_frame): pytest.raises(ValueError, float_frame._get_agg_axis, 2) - def test_nonzero(self, float_frame, float_string_frame, empty_frame): + def test_nonzero(self, float_frame, float_string_frame): + empty_frame = DataFrame() assert empty_frame.empty assert not float_frame.empty diff --git a/pandas/tests/frame/test_apply.py b/pandas/tests/frame/test_apply.py index a4cd1aa3bacb6..4d1e3e7ae1f38 100644 --- a/pandas/tests/frame/test_apply.py +++ b/pandas/tests/frame/test_apply.py @@ -74,8 +74,10 @@ def test_apply_mixed_datetimelike(self): result = df.apply(lambda x: x, axis=1) assert_frame_equal(result, df) - def test_apply_empty(self, float_frame, empty_frame): + def test_apply_empty(self, float_frame): # empty + empty_frame = DataFrame() + applied = empty_frame.apply(np.sqrt) assert applied.empty @@ -97,8 +99,10 @@ def test_apply_empty(self, float_frame, empty_frame): result = expected.apply(lambda x: x['a'], axis=1) assert_frame_equal(expected, result) - def test_apply_with_reduce_empty(self, empty_frame): + def test_apply_with_reduce_empty(self): # reduce with an empty DataFrame + empty_frame = DataFrame() + x = [] result = empty_frame.apply(x.append, axis=1, result_type='expand') assert_frame_equal(result, empty_frame) @@ -116,7 +120,9 @@ def test_apply_with_reduce_empty(self, empty_frame): # Ensure that x.append hasn't been called assert x == [] - def test_apply_deprecate_reduce(self, empty_frame): + def test_apply_deprecate_reduce(self): + empty_frame = DataFrame() + x = [] with tm.assert_produces_warning(FutureWarning): empty_frame.apply(x.append, axis=1, reduce=True) diff --git a/pandas/tests/frame/test_block_internals.py b/pandas/tests/frame/test_block_internals.py index 5419f4d5127f6..39d84f2e6086c 100644 --- a/pandas/tests/frame/test_block_internals.py +++ b/pandas/tests/frame/test_block_internals.py @@ -347,7 +347,9 @@ def test_copy(self, float_frame, float_string_frame): copy = float_string_frame.copy() assert copy._data is not float_string_frame._data - def test_pickle(self, float_string_frame, empty_frame, timezone_frame): + def test_pickle(self, float_string_frame, timezone_frame): + empty_frame = DataFrame() + unpickled = tm.round_trip_pickle(float_string_frame) assert_frame_equal(float_string_frame, unpickled) diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index a8a78b26e317c..b32255da324f4 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -247,7 +247,7 @@ def test_constructor_dict(self): assert isna(frame['col3']).all() # Corner cases - assert len(DataFrame({})) == 0 + assert len(DataFrame()) == 0 # mix dict and array, wrong size - no spec for which error should raise # first diff --git a/pandas/tests/frame/test_reshape.py b/pandas/tests/frame/test_reshape.py index daac084f657af..4fe5172fefbcd 100644 --- a/pandas/tests/frame/test_reshape.py +++ b/pandas/tests/frame/test_reshape.py @@ -58,7 +58,7 @@ def test_pivot_duplicates(self): def test_pivot_empty(self): df = DataFrame({}, columns=['a', 'b', 'c']) result = df.pivot('a', 'b', 'c') - expected = DataFrame({}) + expected = DataFrame() tm.assert_frame_equal(result, expected, check_names=False) def test_pivot_integer_bug(self): diff --git a/pandas/tests/series/conftest.py b/pandas/tests/series/conftest.py index 431aacb1c8d56..367e7a1baa7f3 100644 --- a/pandas/tests/series/conftest.py +++ b/pandas/tests/series/conftest.py @@ -1,6 +1,5 @@ import pytest -from pandas import Series import pandas.util.testing as tm @@ -32,11 +31,3 @@ def object_series(): s = tm.makeObjectSeries() s.name = 'objects' return s - - -@pytest.fixture -def empty_series(): - """ - Fixture for empty Series - """ - return Series([], index=[]) diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index d92ca48751d0a..8525b877618c9 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -47,7 +47,9 @@ def test_scalar_conversion(self): assert int(Series([1.])) == 1 assert long(Series([1.])) == 1 - def test_constructor(self, datetime_series, empty_series): + def test_constructor(self, datetime_series): + empty_series = Series() + assert datetime_series.index.is_all_dates # Pass in Series From 1c9de6984406e89491f5e989bc62cdf9b288cc09 Mon Sep 17 00:00:00 2001 From: leerssej Date: Mon, 4 Mar 2019 10:54:12 -0800 Subject: [PATCH 198/215] DOC: Polishing typos out of doc/source/user_guide/indexing.rst (#25528) --- doc/source/user_guide/indexing.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/user_guide/indexing.rst b/doc/source/user_guide/indexing.rst index be1745e2664a1..00d4dc9efc8cc 100644 --- a/doc/source/user_guide/indexing.rst +++ b/doc/source/user_guide/indexing.rst @@ -435,7 +435,7 @@ Selection By Position This is sometimes called ``chained assignment`` and should be avoided. See :ref:`Returning a View versus Copy `. -Pandas provides a suite of methods in order to get **purely integer based indexing**. The semantics follow closely Python and NumPy slicing. These are ``0-based`` indexing. When slicing, the start bounds is *included*, while the upper bound is *excluded*. Trying to use a non-integer, even a **valid** label will raise an ``IndexError``. +Pandas provides a suite of methods in order to get **purely integer based indexing**. The semantics follow closely Python and NumPy slicing. These are ``0-based`` indexing. When slicing, the start bound is *included*, while the upper bound is *excluded*. Trying to use a non-integer, even a **valid** label will raise an ``IndexError``. The ``.iloc`` attribute is the primary access method. The following are valid inputs: @@ -545,7 +545,7 @@ Selection By Callable .. versionadded:: 0.18.1 ``.loc``, ``.iloc``, and also ``[]`` indexing can accept a ``callable`` as indexer. -The ``callable`` must be a function with one argument (the calling Series, DataFrame or Panel) and that returns valid output for indexing. +The ``callable`` must be a function with one argument (the calling Series, DataFrame or Panel) that returns valid output for indexing. .. ipython:: python @@ -569,7 +569,7 @@ You can use callable indexing in ``Series``. df1.A.loc[lambda s: s > 0] Using these methods / indexers, you can chain data selection operations -without using temporary variable. +without using a temporary variable. .. ipython:: python @@ -907,7 +907,7 @@ of the DataFrame): df[df['A'] > 0] -List comprehensions and ``map`` method of Series can also be used to produce +List comprehensions and the ``map`` method of Series can also be used to produce more complex criteria: .. ipython:: python @@ -1556,7 +1556,7 @@ See :ref:`Advanced Indexing ` for usage of MultiIndexes. ind ``set_names``, ``set_levels``, and ``set_codes`` also take an optional -`level`` argument +``level`` argument .. ipython:: python From 3bbcacf95b142776150a235dabb234806d87dff9 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Mon, 4 Mar 2019 18:56:49 +0000 Subject: [PATCH 199/215] STY: use pytest.raises context manager (frame) (#25516) --- pandas/tests/frame/test_alter_axes.py | 3 +- pandas/tests/frame/test_analytics.py | 25 +++-- pandas/tests/frame/test_api.py | 19 +++- .../tests/frame/test_axis_select_reindex.py | 43 ++++++--- pandas/tests/frame/test_block_internals.py | 10 +- pandas/tests/frame/test_constructors.py | 51 ++++++---- pandas/tests/frame/test_convert_to.py | 8 +- pandas/tests/frame/test_dtypes.py | 20 ++-- pandas/tests/frame/test_indexing.py | 93 +++++++++++++------ pandas/tests/frame/test_missing.py | 39 ++++++-- pandas/tests/frame/test_mutate_columns.py | 4 +- pandas/tests/frame/test_nonunique_indexes.py | 15 ++- pandas/tests/frame/test_quantile.py | 11 ++- pandas/tests/frame/test_query_eval.py | 15 +-- pandas/tests/frame/test_replace.py | 8 +- pandas/tests/frame/test_reshape.py | 5 +- pandas/tests/frame/test_sorting.py | 8 +- pandas/tests/frame/test_timeseries.py | 28 ++++-- pandas/tests/frame/test_to_csv.py | 5 +- 19 files changed, 284 insertions(+), 126 deletions(-) diff --git a/pandas/tests/frame/test_alter_axes.py b/pandas/tests/frame/test_alter_axes.py index a25e893e08900..f4a2a5f8032a0 100644 --- a/pandas/tests/frame/test_alter_axes.py +++ b/pandas/tests/frame/test_alter_axes.py @@ -633,7 +633,8 @@ def test_rename(self, float_frame): tm.assert_index_equal(renamed.index, Index(['BAR', 'FOO'])) # have to pass something - pytest.raises(TypeError, float_frame.rename) + with pytest.raises(TypeError, match="must pass an index to rename"): + float_frame.rename() # partial columns renamed = float_frame.rename(columns={'C': 'foo', 'D': 'bar'}) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index 994187a62d862..3363a45149fff 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -898,6 +898,7 @@ def test_var_std(self, datetime_frame): result = nanops.nanvar(arr, axis=0) assert not (result < 0).any() + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") @pytest.mark.parametrize( "meth", ['sem', 'var', 'std']) def test_numeric_only_flag(self, meth): @@ -919,10 +920,12 @@ def test_numeric_only_flag(self, meth): tm.assert_series_equal(expected, result) # df1 has all numbers, df2 has a letter inside - pytest.raises(TypeError, lambda: getattr(df1, meth)( - axis=1, numeric_only=False)) - pytest.raises(TypeError, lambda: getattr(df2, meth)( - axis=1, numeric_only=False)) + msg = r"unsupported operand type\(s\) for -: 'float' and 'str'" + with pytest.raises(TypeError, match=msg): + getattr(df1, meth)(axis=1, numeric_only=False) + msg = "could not convert string to float: 'a'" + with pytest.raises(TypeError, match=msg): + getattr(df2, meth)(axis=1, numeric_only=False) def test_sem(self, datetime_frame): result = datetime_frame.sem(ddof=4) @@ -1369,6 +1372,7 @@ def test_pct_change(self): # ---------------------------------------------------------------------- # Index of max / min + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_idxmin(self, float_frame, int_frame): frame = float_frame frame.loc[5:10] = np.nan @@ -1381,8 +1385,11 @@ def test_idxmin(self, float_frame, int_frame): skipna=skipna) tm.assert_series_equal(result, expected) - pytest.raises(ValueError, frame.idxmin, axis=2) + msg = "No axis named 2 for object type " + with pytest.raises(ValueError, match=msg): + frame.idxmin(axis=2) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_idxmax(self, float_frame, int_frame): frame = float_frame frame.loc[5:10] = np.nan @@ -1395,7 +1402,9 @@ def test_idxmax(self, float_frame, int_frame): skipna=skipna) tm.assert_series_equal(result, expected) - pytest.raises(ValueError, frame.idxmax, axis=2) + msg = "No axis named 2 for object type " + with pytest.raises(ValueError, match=msg): + frame.idxmax(axis=2) # ---------------------------------------------------------------------- # Logical reductions @@ -1881,7 +1890,9 @@ def test_round_issue(self): tm.assert_index_equal(rounded.index, dfs.index) decimals = pd.Series([1, 0, 2], index=['A', 'B', 'A']) - pytest.raises(ValueError, df.round, decimals) + msg = "Index of decimals must be unique" + with pytest.raises(ValueError, match=msg): + df.round(decimals) def test_built_in_round(self): if not compat.PY3: diff --git a/pandas/tests/frame/test_api.py b/pandas/tests/frame/test_api.py index e561b327e4fb0..118341276d799 100644 --- a/pandas/tests/frame/test_api.py +++ b/pandas/tests/frame/test_api.py @@ -9,7 +9,7 @@ import numpy as np import pytest -from pandas.compat import long, lrange, range +from pandas.compat import PY2, long, lrange, range import pandas as pd from pandas import ( @@ -146,8 +146,12 @@ def test_not_hashable(self): empty_frame = DataFrame() df = self.klass([1]) - pytest.raises(TypeError, hash, df) - pytest.raises(TypeError, hash, empty_frame) + msg = ("'(Sparse)?DataFrame' objects are mutable, thus they cannot be" + " hashed") + with pytest.raises(TypeError, match=msg): + hash(df) + with pytest.raises(TypeError, match=msg): + hash(empty_frame) def test_new_empty_index(self): df1 = self.klass(np.random.randn(0, 3)) @@ -171,7 +175,9 @@ def test_get_agg_axis(self, float_frame): idx = float_frame._get_agg_axis(1) assert idx is float_frame.index - pytest.raises(ValueError, float_frame._get_agg_axis, 2) + msg = r"Axis must be 0 or 1 \(got 2\)" + with pytest.raises(ValueError, match=msg): + float_frame._get_agg_axis(2) def test_nonzero(self, float_frame, float_string_frame): empty_frame = DataFrame() @@ -354,12 +360,15 @@ def test_transpose(self, float_frame): for col, s in compat.iteritems(mixed_T): assert s.dtype == np.object_ + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_swapaxes(self): df = self.klass(np.random.randn(10, 5)) self._assert_frame_equal(df.T, df.swapaxes(0, 1)) self._assert_frame_equal(df.T, df.swapaxes(1, 0)) self._assert_frame_equal(df, df.swapaxes(0, 0)) - pytest.raises(ValueError, df.swapaxes, 2, 5) + msg = "No axis named 2 for object type " + with pytest.raises(ValueError, match=msg): + df.swapaxes(2, 5) def test_axis_aliases(self, float_frame): f = float_frame diff --git a/pandas/tests/frame/test_axis_select_reindex.py b/pandas/tests/frame/test_axis_select_reindex.py index dea925dcde676..fb00776b33cbb 100644 --- a/pandas/tests/frame/test_axis_select_reindex.py +++ b/pandas/tests/frame/test_axis_select_reindex.py @@ -7,7 +7,7 @@ import numpy as np import pytest -from pandas.compat import lrange, lzip, u +from pandas.compat import PY2, lrange, lzip, u from pandas.errors import PerformanceWarning import pandas as pd @@ -38,8 +38,11 @@ def test_drop_names(self): assert obj.columns.name == 'second' assert list(df.columns) == ['d', 'e', 'f'] - pytest.raises(KeyError, df.drop, ['g']) - pytest.raises(KeyError, df.drop, ['g'], 1) + msg = r"\['g'\] not found in axis" + with pytest.raises(KeyError, match=msg): + df.drop(['g']) + with pytest.raises(KeyError, match=msg): + df.drop(['g'], 1) # errors = 'ignore' dropped = df.drop(['g'], errors='ignore') @@ -84,10 +87,14 @@ def test_drop(self): assert_frame_equal(simple.drop( [0, 3], axis='index'), simple.loc[[1, 2], :]) - pytest.raises(KeyError, simple.drop, 5) - pytest.raises(KeyError, simple.drop, 'C', 1) - pytest.raises(KeyError, simple.drop, [1, 5]) - pytest.raises(KeyError, simple.drop, ['A', 'C'], 1) + with pytest.raises(KeyError, match=r"\[5\] not found in axis"): + simple.drop(5) + with pytest.raises(KeyError, match=r"\['C'\] not found in axis"): + simple.drop('C', 1) + with pytest.raises(KeyError, match=r"\[5\] not found in axis"): + simple.drop([1, 5]) + with pytest.raises(KeyError, match=r"\['C'\] not found in axis"): + simple.drop(['A', 'C'], 1) # errors = 'ignore' assert_frame_equal(simple.drop(5, errors='ignore'), simple) @@ -444,7 +451,9 @@ def test_reindex_dups(self): assert_frame_equal(result, expected) # reindex fails - pytest.raises(ValueError, df.reindex, index=list(range(len(df)))) + msg = "cannot reindex from a duplicate axis" + with pytest.raises(ValueError, match=msg): + df.reindex(index=list(range(len(df)))) def test_reindex_axis_style(self): # https://github.com/pandas-dev/pandas/issues/12392 @@ -963,10 +972,15 @@ def test_take(self): assert_frame_equal(result, expected, check_names=False) # illegal indices - pytest.raises(IndexError, df.take, [3, 1, 2, 30], axis=0) - pytest.raises(IndexError, df.take, [3, 1, 2, -31], axis=0) - pytest.raises(IndexError, df.take, [3, 1, 2, 5], axis=1) - pytest.raises(IndexError, df.take, [3, 1, 2, -5], axis=1) + msg = "indices are out-of-bounds" + with pytest.raises(IndexError, match=msg): + df.take([3, 1, 2, 30], axis=0) + with pytest.raises(IndexError, match=msg): + df.take([3, 1, 2, -31], axis=0) + with pytest.raises(IndexError, match=msg): + df.take([3, 1, 2, 5], axis=1) + with pytest.raises(IndexError, match=msg): + df.take([3, 1, 2, -5], axis=1) # mixed-dtype order = [4, 1, 2, 0, 3] @@ -1037,6 +1051,7 @@ def test_reindex_corner(self): smaller = self.intframe.reindex(columns=['A', 'B', 'E']) assert smaller['E'].dtype == np.float64 + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_reindex_axis(self): cols = ['A', 'B', 'E'] with tm.assert_produces_warning(FutureWarning) as m: @@ -1052,7 +1067,9 @@ def test_reindex_axis(self): reindexed2 = self.intframe.reindex(index=rows) assert_frame_equal(reindexed1, reindexed2) - pytest.raises(ValueError, self.intframe.reindex_axis, rows, axis=2) + msg = "No axis named 2 for object type " + with pytest.raises(ValueError, match=msg): + self.intframe.reindex_axis(rows, axis=2) # no-op case cols = self.frame.columns.copy() diff --git a/pandas/tests/frame/test_block_internals.py b/pandas/tests/frame/test_block_internals.py index 39d84f2e6086c..4b06d2e35cdfc 100644 --- a/pandas/tests/frame/test_block_internals.py +++ b/pandas/tests/frame/test_block_internals.py @@ -274,10 +274,12 @@ def f(dtype): columns=["A", "B", "C"], dtype=dtype) - pytest.raises(NotImplementedError, f, - [("A", "datetime64[h]"), - ("B", "str"), - ("C", "int32")]) + msg = ("compound dtypes are not implemented in the DataFrame" + " constructor") + with pytest.raises(NotImplementedError, match=msg): + f([("A", "datetime64[h]"), + ("B", "str"), + ("C", "int32")]) # these work (though results may be unexpected) f('int64') diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index b32255da324f4..fc642d211b30c 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -12,7 +12,8 @@ import pytest from pandas.compat import ( - PY3, PY36, is_platform_little_endian, lmap, long, lrange, lzip, range, zip) + PY2, PY3, PY36, is_platform_little_endian, lmap, long, lrange, lzip, range, + zip) from pandas.core.dtypes.cast import construct_1d_object_array_from_listlike from pandas.core.dtypes.common import is_integer_dtype @@ -58,8 +59,9 @@ def test_constructor_cast_failure(self): df['foo'] = np.ones((4, 2)).tolist() # this is not ok - pytest.raises(ValueError, df.__setitem__, tuple(['test']), - np.ones((4, 2))) + msg = "Wrong number of items passed 2, placement implies 1" + with pytest.raises(ValueError, match=msg): + df['test'] = np.ones((4, 2)) # this is ok df['foo2'] = np.ones((4, 2)).tolist() @@ -1259,7 +1261,9 @@ def test_constructor_Series_named(self): expected = DataFrame({0: s}) tm.assert_frame_equal(df, expected) - pytest.raises(ValueError, DataFrame, s, columns=[1, 2]) + msg = r"Shape of passed values is \(10, 1\), indices imply \(10, 2\)" + with pytest.raises(ValueError, match=msg): + DataFrame(s, columns=[1, 2]) # #2234 a = Series([], name='x') @@ -1433,8 +1437,10 @@ def test_constructor_column_duplicates(self): tm.assert_frame_equal(idf, edf) - pytest.raises(ValueError, DataFrame.from_dict, - OrderedDict([('b', 8), ('a', 5), ('a', 6)])) + msg = "If using all scalar values, you must pass an index" + with pytest.raises(ValueError, match=msg): + DataFrame.from_dict( + OrderedDict([('b', 8), ('a', 5), ('a', 6)])) def test_constructor_empty_with_string_dtype(self): # GH 9428 @@ -1465,8 +1471,11 @@ def test_constructor_single_value(self): dtype=object), index=[1, 2], columns=['a', 'c'])) - pytest.raises(ValueError, DataFrame, 'a', [1, 2]) - pytest.raises(ValueError, DataFrame, 'a', columns=['a', 'c']) + msg = "DataFrame constructor not properly called!" + with pytest.raises(ValueError, match=msg): + DataFrame('a', [1, 2]) + with pytest.raises(ValueError, match=msg): + DataFrame('a', columns=['a', 'c']) msg = 'incompatible data and dtype' with pytest.raises(TypeError, match=msg): @@ -1692,6 +1701,7 @@ def test_constructor_series_copy(self): assert not (series['A'] == 5).all() + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_constructor_with_nas(self): # GH 5016 # na's in indices @@ -1704,9 +1714,11 @@ def check(df): # No NaN found -> error if len(indexer) == 0: - def f(): + msg = ("cannot do label indexing on" + r" " + r" with these indexers \[nan\] of ") + with pytest.raises(TypeError, match=msg): df.loc[:, np.nan] - pytest.raises(TypeError, f) # single nan should result in Series elif len(indexer) == 1: tm.assert_series_equal(df.iloc[:, indexer[0]], @@ -1782,13 +1794,15 @@ def test_constructor_categorical(self): tm.assert_frame_equal(df, expected) # invalid (shape) - pytest.raises(ValueError, - lambda: DataFrame([Categorical(list('abc')), - Categorical(list('abdefg'))])) + msg = r"Shape of passed values is \(6, 2\), indices imply \(3, 2\)" + with pytest.raises(ValueError, match=msg): + DataFrame([Categorical(list('abc')), + Categorical(list('abdefg'))]) # ndim > 1 - pytest.raises(NotImplementedError, - lambda: Categorical(np.array([list('abcd')]))) + msg = "> 1 ndim Categorical are not supported at this time" + with pytest.raises(NotImplementedError, match=msg): + Categorical(np.array([list('abcd')])) def test_constructor_categorical_series(self): @@ -2164,8 +2178,11 @@ def test_from_records_bad_index_column(self): tm.assert_index_equal(df1.index, Index(df.C)) # should fail - pytest.raises(ValueError, DataFrame.from_records, df, index=[2]) - pytest.raises(KeyError, DataFrame.from_records, df, index=2) + msg = r"Shape of passed values is \(10, 3\), indices imply \(1, 3\)" + with pytest.raises(ValueError, match=msg): + DataFrame.from_records(df, index=[2]) + with pytest.raises(KeyError, match=r"^2$"): + DataFrame.from_records(df, index=2) def test_from_records_non_tuple(self): class Record(object): diff --git a/pandas/tests/frame/test_convert_to.py b/pandas/tests/frame/test_convert_to.py index 601a4c6b72fe3..db60fbf0f8563 100644 --- a/pandas/tests/frame/test_convert_to.py +++ b/pandas/tests/frame/test_convert_to.py @@ -75,11 +75,15 @@ def test_to_dict_index_not_unique_with_index_orient(self): # GH22801 # Data loss when indexes are not unique. Raise ValueError. df = DataFrame({'a': [1, 2], 'b': [0.5, 0.75]}, index=['A', 'A']) - pytest.raises(ValueError, df.to_dict, orient='index') + msg = "DataFrame index must be unique for orient='index'" + with pytest.raises(ValueError, match=msg): + df.to_dict(orient='index') def test_to_dict_invalid_orient(self): df = DataFrame({'A': [0, 1]}) - pytest.raises(ValueError, df.to_dict, orient='xinvalid') + msg = "orient 'xinvalid' not understood" + with pytest.raises(ValueError, match=msg): + df.to_dict(orient='xinvalid') def test_to_records_dt64(self): df = DataFrame([["one", "two", "three"], diff --git a/pandas/tests/frame/test_dtypes.py b/pandas/tests/frame/test_dtypes.py index a8776c84b98ca..b37bf02a6b8e7 100644 --- a/pandas/tests/frame/test_dtypes.py +++ b/pandas/tests/frame/test_dtypes.py @@ -154,8 +154,8 @@ def test_select_dtypes_include_using_list_like(self): ei = df[['h', 'i']] assert_frame_equal(ri, ei) - pytest.raises(NotImplementedError, - lambda: df.select_dtypes(include=['period'])) + with pytest.raises(NotImplementedError, match=r"^$"): + df.select_dtypes(include=['period']) def test_select_dtypes_exclude_using_list_like(self): df = DataFrame({'a': list('abc'), @@ -218,8 +218,8 @@ def test_select_dtypes_include_using_scalars(self): ei = df[['f']] assert_frame_equal(ri, ei) - pytest.raises(NotImplementedError, - lambda: df.select_dtypes(include='period')) + with pytest.raises(NotImplementedError, match=r"^$"): + df.select_dtypes(include='period') def test_select_dtypes_exclude_using_scalars(self): df = DataFrame({'a': list('abc'), @@ -245,8 +245,8 @@ def test_select_dtypes_exclude_using_scalars(self): ei = df[['a', 'b', 'c', 'd', 'e', 'g', 'h', 'i', 'j', 'k']] assert_frame_equal(ri, ei) - pytest.raises(NotImplementedError, - lambda: df.select_dtypes(exclude='period')) + with pytest.raises(NotImplementedError, match=r"^$"): + df.select_dtypes(exclude='period') def test_select_dtypes_include_exclude_using_scalars(self): df = DataFrame({'a': list('abc'), @@ -601,8 +601,12 @@ def test_astype_dict_like(self, dtype_class): # in the keys of the dtype dict dt4 = dtype_class({'b': str, 2: str}) dt5 = dtype_class({'e': str}) - pytest.raises(KeyError, df.astype, dt4) - pytest.raises(KeyError, df.astype, dt5) + msg = ("Only a column name can be used for the key in a dtype mappings" + " argument") + with pytest.raises(KeyError, match=msg): + df.astype(dt4) + with pytest.raises(KeyError, match=msg): + df.astype(dt5) assert_frame_equal(df, original) # if the dtypes provided are the same as the original dtypes, the diff --git a/pandas/tests/frame/test_indexing.py b/pandas/tests/frame/test_indexing.py index 19b8ae4eb6e0f..ffe54f7a94307 100644 --- a/pandas/tests/frame/test_indexing.py +++ b/pandas/tests/frame/test_indexing.py @@ -9,7 +9,7 @@ import pytest from pandas._libs.tslib import iNaT -from pandas.compat import long, lrange, lzip, map, range, zip +from pandas.compat import PY2, long, lrange, lzip, map, range, zip from pandas.core.dtypes.common import is_float_dtype, is_integer, is_scalar from pandas.core.dtypes.dtypes import CategoricalDtype @@ -431,8 +431,9 @@ def test_getitem_setitem_ix_negative_integers(self): def test_getattr(self): assert_series_equal(self.frame.A, self.frame['A']) - pytest.raises(AttributeError, getattr, self.frame, - 'NONEXISTENT_NAME') + msg = "'DataFrame' object has no attribute 'NONEXISTENT_NAME'" + with pytest.raises(AttributeError, match=msg): + self.frame.NONEXISTENT_NAME def test_setattr_column(self): df = DataFrame({'foobar': 1}, index=lrange(10)) @@ -793,7 +794,8 @@ def test_delitem_corner(self): f = self.frame.copy() del f['D'] assert len(f.columns) == 3 - pytest.raises(KeyError, f.__delitem__, 'D') + with pytest.raises(KeyError, match=r"^'D'$"): + del f['D'] del f['B'] assert len(f.columns) == 2 @@ -842,7 +844,9 @@ def test_getitem_fancy_2d(self): with catch_warnings(record=True): simplefilter("ignore", DeprecationWarning) - pytest.raises(ValueError, f.ix.__getitem__, f > 0.5) + msg = "Cannot index with multidimensional key" + with pytest.raises(ValueError, match=msg): + f.ix[f > 0.5] def test_slice_floats(self): index = [52195.504153, 52196.303147, 52198.369883] @@ -865,6 +869,7 @@ def test_getitem_fancy_slice_integers_step(self): df.iloc[:8:2] = np.nan assert isna(df.iloc[:8:2]).values.all() + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_getitem_setitem_integer_slice_keyerrors(self): df = DataFrame(np.random.randn(10, 5), index=lrange(0, 20, 2)) @@ -887,8 +892,10 @@ def test_getitem_setitem_integer_slice_keyerrors(self): # non-monotonic, raise KeyError df2 = df.iloc[lrange(5) + lrange(5, 10)[::-1]] - pytest.raises(KeyError, df2.loc.__getitem__, slice(3, 11)) - pytest.raises(KeyError, df2.loc.__setitem__, slice(3, 11), 0) + with pytest.raises(KeyError, match=r"^3$"): + df2.loc[3:11] + with pytest.raises(KeyError, match=r"^3$"): + df2.loc[3:11] = 0 def test_setitem_fancy_2d(self): @@ -1077,6 +1084,7 @@ def test_fancy_getitem_int_labels(self): expected = df[3] assert_series_equal(result, expected) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_fancy_index_int_labels_exceptions(self): df = DataFrame(np.random.randn(10, 5), index=np.arange(0, 20, 2)) @@ -1084,14 +1092,18 @@ def test_fancy_index_int_labels_exceptions(self): simplefilter("ignore", DeprecationWarning) # labels that aren't contained - pytest.raises(KeyError, df.ix.__setitem__, - ([0, 1, 2], [2, 3, 4]), 5) + with pytest.raises(KeyError, match=r"\[1\] not in index"): + df.ix[[0, 1, 2], [2, 3, 4]] = 5 # try to set indices not contained in frame - pytest.raises(KeyError, self.frame.ix.__setitem__, - ['foo', 'bar', 'baz'], 1) - pytest.raises(KeyError, self.frame.ix.__setitem__, - (slice(None, None), ['E']), 1) + msg = (r"None of \[Index\(\['foo', 'bar', 'baz'\]," + r" dtype='object'\)\] are in the \[index\]") + with pytest.raises(KeyError, match=msg): + self.frame.ix[['foo', 'bar', 'baz']] = 1 + msg = (r"None of \[Index\(\['E'\], dtype='object'\)\] are in the" + r" \[columns\]") + with pytest.raises(KeyError, match=msg): + self.frame.ix[:, ['E']] = 1 # partial setting now allows this GH2578 # pytest.raises(KeyError, self.frame.ix.__setitem__, @@ -1504,6 +1516,7 @@ def test_getitem_setitem_boolean_multi(self): expected.loc[[0, 2], [1]] = 5 assert_frame_equal(df, expected) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_getitem_setitem_float_labels(self): index = Index([1.5, 2, 3, 4, 5]) df = DataFrame(np.random.randn(5, 5), index=index) @@ -1537,7 +1550,11 @@ def test_getitem_setitem_float_labels(self): df = DataFrame(np.random.randn(5, 5), index=index) # positional slicing only via iloc! - pytest.raises(TypeError, lambda: df.iloc[1.0:5]) + msg = ("cannot do slice indexing on" + r" with" + r" these indexers \[1.0\] of ") + with pytest.raises(TypeError, match=msg): + df.iloc[1.0:5] result = df.iloc[4:5] expected = df.reindex([5.0]) @@ -1744,11 +1761,16 @@ def test_getitem_setitem_ix_bool_keyerror(self): # #2199 df = DataFrame({'a': [1, 2, 3]}) - pytest.raises(KeyError, df.loc.__getitem__, False) - pytest.raises(KeyError, df.loc.__getitem__, True) + with pytest.raises(KeyError, match=r"^False$"): + df.loc[False] + with pytest.raises(KeyError, match=r"^True$"): + df.loc[True] - pytest.raises(KeyError, df.loc.__setitem__, False, 0) - pytest.raises(KeyError, df.loc.__setitem__, True, 0) + msg = "cannot use a single bool to index into setitem" + with pytest.raises(KeyError, match=msg): + df.loc[False] = 0 + with pytest.raises(KeyError, match=msg): + df.loc[True] = 0 def test_getitem_list_duplicates(self): # #1943 @@ -1813,6 +1835,7 @@ def test_set_value(self): self.frame.set_value(idx, col, 1) assert self.frame[col][idx] == 1 + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_set_value_resize(self): with tm.assert_produces_warning(FutureWarning, @@ -1849,7 +1872,9 @@ def test_set_value_resize(self): assert isna(res3['baz'].drop(['foobar'])).all() with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - pytest.raises(ValueError, res3.set_value, 'foobar', 'baz', 'sam') + msg = "could not convert string to float: 'sam'" + with pytest.raises(ValueError, match=msg): + res3.set_value('foobar', 'baz', 'sam') def test_set_value_with_index_dtype_change(self): df_orig = DataFrame(np.random.randn(3, 3), @@ -1888,7 +1913,8 @@ def test_get_set_value_no_partial_indexing(self): df = DataFrame(index=index, columns=lrange(4)) with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): - pytest.raises(KeyError, df.get_value, 0, 1) + with pytest.raises(KeyError, match=r"^0$"): + df.get_value(0, 1) def test_single_element_ix_dont_upcast(self): self.frame['E'] = 1 @@ -2158,10 +2184,15 @@ def test_non_monotonic_reindex_methods(self): df_rev = pd.DataFrame(data, index=dr[[3, 4, 5] + [0, 1, 2]], columns=list('A')) # index is not monotonic increasing or decreasing - pytest.raises(ValueError, df_rev.reindex, df.index, method='pad') - pytest.raises(ValueError, df_rev.reindex, df.index, method='ffill') - pytest.raises(ValueError, df_rev.reindex, df.index, method='bfill') - pytest.raises(ValueError, df_rev.reindex, df.index, method='nearest') + msg = "index must be monotonic increasing or decreasing" + with pytest.raises(ValueError, match=msg): + df_rev.reindex(df.index, method='pad') + with pytest.raises(ValueError, match=msg): + df_rev.reindex(df.index, method='ffill') + with pytest.raises(ValueError, match=msg): + df_rev.reindex(df.index, method='bfill') + with pytest.raises(ValueError, match=msg): + df_rev.reindex(df.index, method='nearest') def test_reindex_level(self): from itertools import permutations @@ -2669,14 +2700,20 @@ def _check_align(df, cond, other, check_dtypes=True): # invalid conditions df = default_frame err1 = (df + 1).values[0:2, :] - pytest.raises(ValueError, df.where, cond, err1) + msg = "other must be the same shape as self when an ndarray" + with pytest.raises(ValueError, match=msg): + df.where(cond, err1) err2 = cond.iloc[:2, :].values other1 = _safe_add(df) - pytest.raises(ValueError, df.where, err2, other1) + msg = "Array conditional must be same shape as self" + with pytest.raises(ValueError, match=msg): + df.where(err2, other1) - pytest.raises(ValueError, df.mask, True) - pytest.raises(ValueError, df.mask, 0) + with pytest.raises(ValueError, match=msg): + df.mask(True) + with pytest.raises(ValueError, match=msg): + df.mask(0) # where inplace def _check_set(df, cond, check_dtypes=True): diff --git a/pandas/tests/frame/test_missing.py b/pandas/tests/frame/test_missing.py index 77a3d4785d295..2f3b0a9f76de9 100644 --- a/pandas/tests/frame/test_missing.py +++ b/pandas/tests/frame/test_missing.py @@ -9,7 +9,7 @@ import numpy as np import pytest -from pandas.compat import lrange +from pandas.compat import PY2, lrange import pandas.util._test_decorators as td import pandas as pd @@ -83,6 +83,7 @@ def test_dropIncompleteRows(self): tm.assert_index_equal(samesize_frame.index, self.frame.index) tm.assert_index_equal(inp_frame2.index, self.frame.index) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_dropna(self): df = DataFrame(np.random.randn(6, 4)) df[2][:2] = np.nan @@ -139,7 +140,9 @@ def test_dropna(self): assert_frame_equal(dropped, expected) # bad input - pytest.raises(ValueError, df.dropna, axis=3) + msg = "No axis named 3 for object type " + with pytest.raises(ValueError, match=msg): + df.dropna(axis=3) def test_drop_and_dropna_caching(self): # tst that cacher updates @@ -158,10 +161,15 @@ def test_drop_and_dropna_caching(self): def test_dropna_corner(self): # bad input - pytest.raises(ValueError, self.frame.dropna, how='foo') - pytest.raises(TypeError, self.frame.dropna, how=None) + msg = "invalid how option: foo" + with pytest.raises(ValueError, match=msg): + self.frame.dropna(how='foo') + msg = "must specify how or thresh" + with pytest.raises(TypeError, match=msg): + self.frame.dropna(how=None) # non-existent column - 8303 - pytest.raises(KeyError, self.frame.dropna, subset=['A', 'X']) + with pytest.raises(KeyError, match=r"^\['X'\]$"): + self.frame.dropna(subset=['A', 'X']) def test_dropna_multiple_axes(self): df = DataFrame([[1, np.nan, 2, 3], @@ -226,8 +234,12 @@ def test_fillna(self): result = self.mixed_frame.fillna(value=0) result = self.mixed_frame.fillna(method='pad') - pytest.raises(ValueError, self.tsframe.fillna) - pytest.raises(ValueError, self.tsframe.fillna, 5, method='ffill') + msg = "Must specify a fill 'value' or 'method'" + with pytest.raises(ValueError, match=msg): + self.tsframe.fillna() + msg = "Cannot specify both 'value' and 'method'" + with pytest.raises(ValueError, match=msg): + self.tsframe.fillna(5, method='ffill') # mixed numeric (but no float16) mf = self.mixed_float.reindex(columns=['A', 'B', 'D']) @@ -595,11 +607,18 @@ def test_fillna_invalid_method(self): def test_fillna_invalid_value(self): # list - pytest.raises(TypeError, self.frame.fillna, [1, 2]) + msg = ("\"value\" parameter must be a scalar or dict, but you passed" + " a \"{}\"") + with pytest.raises(TypeError, match=msg.format('list')): + self.frame.fillna([1, 2]) # tuple - pytest.raises(TypeError, self.frame.fillna, (1, 2)) + with pytest.raises(TypeError, match=msg.format('tuple')): + self.frame.fillna((1, 2)) # frame with series - pytest.raises(TypeError, self.frame.iloc[:, 0].fillna, self.frame) + msg = ("\"value\" parameter must be a scalar, dict or Series, but you" + " passed a \"DataFrame\"") + with pytest.raises(TypeError, match=msg): + self.frame.iloc[:, 0].fillna(self.frame) def test_fillna_col_reordering(self): cols = ["COL." + str(i) for i in range(5, 0, -1)] diff --git a/pandas/tests/frame/test_mutate_columns.py b/pandas/tests/frame/test_mutate_columns.py index 1f4da1bbb0470..6bef7e3f65b21 100644 --- a/pandas/tests/frame/test_mutate_columns.py +++ b/pandas/tests/frame/test_mutate_columns.py @@ -177,7 +177,9 @@ def test_insert(self): with pytest.raises(ValueError, match='already exists'): df.insert(1, 'a', df['b']) - pytest.raises(ValueError, df.insert, 1, 'c', df['b']) + msg = "cannot insert c, already exists" + with pytest.raises(ValueError, match=msg): + df.insert(1, 'c', df['b']) df.columns.name = 'some_name' # preserve columns name field diff --git a/pandas/tests/frame/test_nonunique_indexes.py b/pandas/tests/frame/test_nonunique_indexes.py index a5bed14cf06d2..799d548100b5e 100644 --- a/pandas/tests/frame/test_nonunique_indexes.py +++ b/pandas/tests/frame/test_nonunique_indexes.py @@ -187,8 +187,11 @@ def check(result, expected=None): # reindex is invalid! df = DataFrame([[1, 5, 7.], [1, 5, 7.], [1, 5, 7.]], columns=['bar', 'a', 'a']) - pytest.raises(ValueError, df.reindex, columns=['bar']) - pytest.raises(ValueError, df.reindex, columns=['bar', 'foo']) + msg = "cannot reindex from a duplicate axis" + with pytest.raises(ValueError, match=msg): + df.reindex(columns=['bar']) + with pytest.raises(ValueError, match=msg): + df.reindex(columns=['bar', 'foo']) # drop df = DataFrame([[1, 5, 7.], [1, 5, 7.], [1, 5, 7.]], @@ -306,7 +309,9 @@ def check(result, expected=None): # boolean with the duplicate raises df = DataFrame(np.arange(12).reshape(3, 4), columns=dups, dtype='float64') - pytest.raises(ValueError, lambda: df[df.A > 6]) + msg = "cannot reindex from a duplicate axis" + with pytest.raises(ValueError, match=msg): + df[df.A > 6] # dup aligining operations should work # GH 5185 @@ -323,7 +328,9 @@ def check(result, expected=None): columns=['A', 'A']) # not-comparing like-labelled - pytest.raises(ValueError, lambda: df1 == df2) + msg = "Can only compare identically-labeled DataFrame objects" + with pytest.raises(ValueError, match=msg): + df1 == df2 df1r = df1.reindex_like(df2) result = df1r == df2 diff --git a/pandas/tests/frame/test_quantile.py b/pandas/tests/frame/test_quantile.py index d1f1299a5202e..19b6636978643 100644 --- a/pandas/tests/frame/test_quantile.py +++ b/pandas/tests/frame/test_quantile.py @@ -5,6 +5,8 @@ import numpy as np import pytest +from pandas.compat import PY2 + import pandas as pd from pandas import DataFrame, Series, Timestamp from pandas.tests.frame.common import TestData @@ -71,6 +73,7 @@ def test_quantile_axis_mixed(self): with pytest.raises(TypeError): df.quantile(.5, axis=1, numeric_only=False) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_quantile_axis_parameter(self): # GH 9543/9544 @@ -92,8 +95,12 @@ def test_quantile_axis_parameter(self): result = df.quantile(.5, axis="columns") assert_series_equal(result, expected) - pytest.raises(ValueError, df.quantile, 0.1, axis=-1) - pytest.raises(ValueError, df.quantile, 0.1, axis="column") + msg = "No axis named -1 for object type " + with pytest.raises(ValueError, match=msg): + df.quantile(0.1, axis=-1) + msg = "No axis named column for object type " + with pytest.raises(ValueError, match=msg): + df.quantile(0.1, axis="column") def test_quantile_interpolation(self): # see gh-10174 diff --git a/pandas/tests/frame/test_query_eval.py b/pandas/tests/frame/test_query_eval.py index 0d06d0006a9e2..ba02cb54bcea1 100644 --- a/pandas/tests/frame/test_query_eval.py +++ b/pandas/tests/frame/test_query_eval.py @@ -78,10 +78,10 @@ def test_query_numexpr(self): result = df.eval('A+1', engine='numexpr') assert_series_equal(result, self.expected2, check_names=False) else: - pytest.raises(ImportError, - lambda: df.query('A>0', engine='numexpr')) - pytest.raises(ImportError, - lambda: df.eval('A+1', engine='numexpr')) + with pytest.raises(ImportError): + df.query('A>0', engine='numexpr') + with pytest.raises(ImportError): + df.eval('A+1', engine='numexpr') class TestDataFrameEval(TestData): @@ -852,9 +852,10 @@ def test_str_query_method(self, parser, engine): for lhs, op, rhs in zip(lhs, ops, rhs): ex = '{lhs} {op} {rhs}'.format(lhs=lhs, op=op, rhs=rhs) - pytest.raises(NotImplementedError, df.query, ex, - engine=engine, parser=parser, - local_dict={'strings': df.strings}) + msg = r"'(Not)?In' nodes are not implemented" + with pytest.raises(NotImplementedError, match=msg): + df.query(ex, engine=engine, parser=parser, + local_dict={'strings': df.strings}) else: res = df.query('"a" == strings', engine=engine, parser=parser) assert_frame_equal(res, expect) diff --git a/pandas/tests/frame/test_replace.py b/pandas/tests/frame/test_replace.py index 127a64da38ba3..50c66d3f8db00 100644 --- a/pandas/tests/frame/test_replace.py +++ b/pandas/tests/frame/test_replace.py @@ -837,7 +837,9 @@ def test_replace_input_formats_listlike(self): expected.replace(to_rep[i], values[i], inplace=True) assert_frame_equal(result, expected) - pytest.raises(ValueError, df.replace, to_rep, values[1:]) + msg = r"Replacement lists must match in length\. Expecting 3 got 2" + with pytest.raises(ValueError, match=msg): + df.replace(to_rep, values[1:]) def test_replace_input_formats_scalar(self): df = DataFrame({'A': [np.nan, 0, np.inf], 'B': [0, 2, 5], @@ -850,7 +852,9 @@ def test_replace_input_formats_scalar(self): for k, v in compat.iteritems(df)} assert_frame_equal(filled, DataFrame(expected)) - pytest.raises(TypeError, df.replace, to_rep, [np.nan, 0, '']) + msg = "value argument must be scalar, dict, or Series" + with pytest.raises(TypeError, match=msg): + df.replace(to_rep, [np.nan, 0, '']) # list to scalar to_rep = [np.nan, 0, ''] diff --git a/pandas/tests/frame/test_reshape.py b/pandas/tests/frame/test_reshape.py index 4fe5172fefbcd..8abf3a6706886 100644 --- a/pandas/tests/frame/test_reshape.py +++ b/pandas/tests/frame/test_reshape.py @@ -394,7 +394,10 @@ def test_stack_mixed_levels(self): # When mixed types are passed and the ints are not level # names, raise - pytest.raises(ValueError, df2.stack, level=['animal', 0]) + msg = ("level should contain all level names or all level numbers, not" + " a mixture of the two") + with pytest.raises(ValueError, match=msg): + df2.stack(level=['animal', 0]) # GH #8584: Having 0 in the level names could raise a # strange error about lexsort depth diff --git a/pandas/tests/frame/test_sorting.py b/pandas/tests/frame/test_sorting.py index 85e6373b384e4..8b29394bcab84 100644 --- a/pandas/tests/frame/test_sorting.py +++ b/pandas/tests/frame/test_sorting.py @@ -7,7 +7,7 @@ import numpy as np import pytest -from pandas.compat import lrange +from pandas.compat import PY2, lrange import pandas as pd from pandas import ( @@ -21,6 +21,7 @@ class TestDataFrameSorting(TestData): + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_sort_values(self): frame = DataFrame([[1, 1, 2], [3, 1, 0], [4, 5, 6]], index=[1, 2, 3], columns=list('ABC')) @@ -54,8 +55,9 @@ def test_sort_values(self): sorted_df = frame.sort_values(by=['B', 'A'], ascending=[True, False]) assert_frame_equal(sorted_df, expected) - pytest.raises(ValueError, lambda: frame.sort_values( - by=['A', 'B'], axis=2, inplace=True)) + msg = "No axis named 2 for object type " + with pytest.raises(ValueError, match=msg): + frame.sort_values(by=['A', 'B'], axis=2, inplace=True) # by row (axis=1): GH 10806 sorted_df = frame.sort_values(by=3, axis=1) diff --git a/pandas/tests/frame/test_timeseries.py b/pandas/tests/frame/test_timeseries.py index 31e81a9ca77c2..716a9e30e4cc3 100644 --- a/pandas/tests/frame/test_timeseries.py +++ b/pandas/tests/frame/test_timeseries.py @@ -8,7 +8,7 @@ import pytest import pytz -from pandas.compat import product +from pandas.compat import PY2, product import pandas as pd from pandas import ( @@ -395,7 +395,9 @@ def test_tshift(self): assert_frame_equal(unshifted, inferred_ts) no_freq = self.tsframe.iloc[[0, 5, 7], :] - pytest.raises(ValueError, no_freq.tshift) + msg = "Freq was not given and was not set in the index" + with pytest.raises(ValueError, match=msg): + no_freq.tshift() def test_truncate(self): ts = self.tsframe[::3] @@ -436,9 +438,10 @@ def test_truncate(self): truncated = ts.truncate(after=end_missing) assert_frame_equal(truncated, expected) - pytest.raises(ValueError, ts.truncate, - before=ts.index[-1] - ts.index.freq, - after=ts.index[0] + ts.index.freq) + msg = "Truncate: 2000-01-06 00:00:00 must be after 2000-02-04 00:00:00" + with pytest.raises(ValueError, match=msg): + ts.truncate(before=ts.index[-1] - ts.index.freq, + after=ts.index[0] + ts.index.freq) def test_truncate_copy(self): index = self.tsframe.index @@ -781,14 +784,18 @@ def test_between_time_axis_raises(self, axis): ts = DataFrame(rand_data, index=rng, columns=rng) stime, etime = ('08:00:00', '09:00:00') + msg = "Index must be DatetimeIndex" if axis in ['columns', 1]: ts.index = mask - pytest.raises(TypeError, ts.between_time, stime, etime) - pytest.raises(TypeError, ts.between_time, stime, etime, axis=0) + with pytest.raises(TypeError, match=msg): + ts.between_time(stime, etime) + with pytest.raises(TypeError, match=msg): + ts.between_time(stime, etime, axis=0) if axis in ['index', 0]: ts.columns = mask - pytest.raises(TypeError, ts.between_time, stime, etime, axis=1) + with pytest.raises(TypeError, match=msg): + ts.between_time(stime, etime, axis=1) def test_operation_on_NaT(self): # Both NaT and Timestamp are in DataFrame. @@ -829,6 +836,7 @@ def test_datetime_assignment_with_NaT_and_diff_time_units(self): 'new': [1e9, None]}, dtype='datetime64[ns]') tm.assert_frame_equal(result, expected) + @pytest.mark.skipif(PY2, reason="pytest.raises match regex fails") def test_frame_to_period(self): K = 5 @@ -854,7 +862,9 @@ def test_frame_to_period(self): pts = df.to_period('M', axis=1) tm.assert_index_equal(pts.columns, exp.columns.asfreq('M')) - pytest.raises(ValueError, df.to_period, axis=2) + msg = "No axis named 2 for object type " + with pytest.raises(ValueError, match=msg): + df.to_period(axis=2) @pytest.mark.parametrize("fn", ['tz_localize', 'tz_convert']) def test_tz_convert_and_localize(self, fn): diff --git a/pandas/tests/frame/test_to_csv.py b/pandas/tests/frame/test_to_csv.py index 61eefccede5dd..54a8712a9c645 100644 --- a/pandas/tests/frame/test_to_csv.py +++ b/pandas/tests/frame/test_to_csv.py @@ -109,8 +109,9 @@ def test_to_csv_from_csv2(self): xp.columns = col_aliases assert_frame_equal(xp, rs) - pytest.raises(ValueError, self.frame2.to_csv, path, - header=['AA', 'X']) + msg = "Writing 4 cols but got 2 aliases" + with pytest.raises(ValueError, match=msg): + self.frame2.to_csv(path, header=['AA', 'X']) def test_to_csv_from_csv3(self): From 076b5a85bcbed979c3b8df5dd825b1b2771894ff Mon Sep 17 00:00:00 2001 From: Bharat Raghunathan Date: Tue, 5 Mar 2019 08:37:07 +0530 Subject: [PATCH 200/215] DOC: Fix #24268 by updating description for keep in Series.nlargest (#25358) * DOC: Fix #24268 by updating description for keep --- pandas/core/series.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index cada6663ce651..f6598ed1ee614 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3098,8 +3098,10 @@ def nlargest(self, n=5, keep='first'): When there are duplicate values that cannot all fit in a Series of `n` elements: - - ``first`` : take the first occurrences based on the index order - - ``last`` : take the last occurrences based on the index order + - ``first`` : return the first `n` occurrences in order + of appearance. + - ``last`` : return the last `n` occurrences in reverse + order of appearance. - ``all`` : keep all occurrences. This can result in a Series of size larger than `n`. @@ -3194,8 +3196,10 @@ def nsmallest(self, n=5, keep='first'): When there are duplicate values that cannot all fit in a Series of `n` elements: - - ``first`` : take the first occurrences based on the index order - - ``last`` : take the last occurrences based on the index order + - ``first`` : return the first `n` occurrences in order + of appearance. + - ``last`` : return the last `n` occurrences in reverse + order of appearance. - ``all`` : keep all occurrences. This can result in a Series of size larger than `n`. @@ -3236,7 +3240,7 @@ def nsmallest(self, n=5, keep='first'): Monserat 5200 dtype: int64 - The `n` largest elements where ``n=5`` by default. + The `n` smallest elements where ``n=5`` by default. >>> s.nsmallest() Monserat 5200 From 557bae072bfebdc1bc90e5bdc0a14579a4d7588b Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Mon, 4 Mar 2019 23:19:04 -0800 Subject: [PATCH 201/215] Update docstrings --- pandas/_libs/tslibs/strptime.pyx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 8f45879feaa3a..86a4fdd7b9a49 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -636,7 +636,16 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year, int day_of_week, int week_starts_Mon): """Calculate the Julian day based on the year, week of the year, and day of the week, with week_start_day representing whether the week of the year - assumes the week starts on Sunday or Monday (6 or 0).""" + assumes the week starts on Sunday or Monday (6 or 0). + + :param year: the year + :param week_of_year: week taken from format U or W + :param day_of_week: weekday + :param week_starts_Mon: represent whether the week of the year + assumes the week starts on Sunday or Monday (6 or 0) + :returns: converted julian day. + :rtype: int + """ cdef: int first_weekday, week_0_length, days_to_week @@ -662,7 +671,14 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year, cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): """Calculate the Julian day based on the ISO 8601 year, week, and weekday. ISO weeks start on Mondays, with week 01 being the week containing 4 Jan. - ISO week days range from 1 (Monday) to 7 (Sunday).""" + ISO week days range from 1 (Monday) to 7 (Sunday). + + :param iso_year: the year taken from format %G + :param iso_week: the week taken from format %V + :param iso_weekday: weekday taken from format %u + :returns: the passed in year and the Gregorian ordinal date / julian date system + :rtype: (int, int) + """ cdef: int correction, ordinal From 2de551f602675a0e3c4268e65ad475e195e09a7f Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 19 Jan 2019 20:50:11 -0800 Subject: [PATCH 202/215] Added support for iso time --- pandas/_libs/tslibs/strptime.pyx | 70 ++++++++++++++++++-- pandas/tests/indexes/datetimes/test_tools.py | 47 +++++++++++++ 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 87658ae92175e..a63a0080006b6 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -54,7 +54,10 @@ cdef dict _parse_code_table = {'y': 0, 'W': 16, 'Z': 17, 'p': 18, # an additional key, only with I - 'z': 19} + 'z': 19, + 'G': 20, + 'V': 21, + 'u': 22} def array_strptime(object[:] values, object fmt, @@ -76,7 +79,7 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal + int week_of_year, week_of_year_start, parse_code, ordinal, iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -169,13 +172,14 @@ def array_strptime(object[:] values, object fmt, raise ValueError("time data %r does not match format " "%r (search)" % (values[i], fmt)) + iso_year = -1 year = 1900 month = day = 1 hour = minute = second = ns = us = 0 timezone = None # Default to -1 to signify that values not known; not critical to have, # though - week_of_year = -1 + iso_week = week_of_year = -1 week_of_year_start = -1 # weekday and julian defaulted to -1 so as to signal need to calculate # values @@ -265,13 +269,43 @@ def array_strptime(object[:] values, object fmt, timezone = pytz.timezone(found_dict['Z']) elif parse_code == 19: timezone = parse_timezone_directive(found_dict['z']) + elif parse_code == 20: + iso_year = int(found_dict['G']) + elif parse_code == 21: + iso_week = int(found_dict['V']) + elif parse_code == 22: + weekday = int(found_dict['u']) + weekday -= 1 + + # don't assume default values for ISO week/year + if iso_year != -1: + if iso_week == -1 or weekday == -1: + raise ValueError("ISO year directive '%G' must be used with " + "the ISO week directive '%V' and a weekday " + "directive '%A', '%a', '%w', or '%u'.") + if julian != -1: + raise ValueError("Day of the year directive '%j' is not " + "compatible with ISO year directive '%G'. " + "Use '%Y' instead.") + elif year != -1 and week_of_year == -1 and iso_week != -1: + if weekday == -1: + raise ValueError("ISO week directive '%V' must be used with " + "the ISO year directive '%G' and a weekday " + "directive '%A', '%a', '%w', or '%u'.") + else: + raise ValueError("ISO week directive '%V' is incompatible with" + " the year directive '%Y'. Use the ISO year " + "'%G' instead.") # If we know the wk of the year and what day of that wk, we can figure # out the Julian day of the year. - if julian == -1 and week_of_year != -1 and weekday != -1: - week_starts_Mon = True if week_of_year_start == 0 else False - julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + if julian == -1 and weekday != -1: + if week_of_year != -1: + week_starts_Mon = True if week_of_year_start == 0 else False + julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, + week_starts_Mon) + elif iso_year != -1 and iso_week != -1: + year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. @@ -511,6 +545,7 @@ class TimeRE(dict): # The " \d" part of the regex is to make %c from ANSI C work 'd': r"(?P3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])", 'f': r"(?P[0-9]{1,9})", + 'G': r"(?P\d\d\d\d)", 'H': r"(?P2[0-3]|[0-1]\d|\d)", 'I': r"(?P1[0-2]|0[1-9]|[1-9])", 'j': (r"(?P36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|" @@ -518,7 +553,9 @@ class TimeRE(dict): 'm': r"(?P1[0-2]|0[1-9]|[1-9])", 'M': r"(?P[0-5]\d|\d)", 'S': r"(?P6[0-1]|[0-5]\d|\d)", + 'u': r"(?P[1-7])", 'U': r"(?P5[0-3]|[0-4]\d|\d)", + 'V': r"(?P5[0-3]|0[1-9]|[1-4]\d|\d)", 'w': r"(?P[0-6])", # W is set below by using 'U' 'y': r"(?P\d\d)", @@ -620,6 +657,25 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year, return 1 + days_to_week + day_of_week +cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): + """Calculate the Julian day based on the ISO 8601 year, week, and weekday. + ISO weeks start on Mondays, with week 01 being the week containing 4 Jan. + ISO week days range from 1 (Monday) to 7 (Sunday).""" + + cdef: + int correction, ordinal + + correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 + ordinal = (iso_week * 7) + iso_weekday - correction + # ordinal may be negative or 0 now, which means the date is in the previous + # calendar year + if ordinal < 1: + ordinal += datetime_date(iso_year, 1, 1).toordinal() + iso_year -= 1 + ordinal -= datetime_date(iso_year, 1, 1).toordinal() + return iso_year, ordinal + + cdef parse_timezone_directive(object z): """ Parse the '%z' directive and return a pytz.FixedOffset diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index dd914d8a79837..c42dfcbac1c3d 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -247,6 +247,53 @@ def test_to_datetime_parse_timezone_keeps_name(self): class TestToDatetime(object): + @pytest.mark.parametrize("s, _format, dt", [ + ['2015-1-1', '%G-%V-%u', datetime(2014, 12, 29, 0, 0)], + ['2015-1-4', '%G-%V-%u', datetime(2015, 1, 1, 0, 0)], + ['2015-1-7', '%G-%V-%u', datetime(2015, 1, 4, 0, 0)] + ]) + def test_to_datetime_iso_week_year_format(self, s, _format, dt): + assert to_datetime(s, format=_format) == dt + + @pytest.mark.parametrize("msg, s, _format", [ + ["ISO week directive '%V' must be used with the ISO year directive " + "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 50", + "%Y %V"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 51", + "%G %V"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 " + "Monday", "%G %A"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 Mon", + "%G %a"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 6", + "%G %w"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "1999 6", + "%G %u"], + ["ISO year directive '%G' must be used with the ISO week directive " + "'%V' and a weekday directive '%A', '%a', '%w', or '%u'.", "2051", + "%G"], + ["Day of the year directive '%j' is not compatible with ISO year " + "directive '%G'. Use '%Y' instead.", "1999 51 6 256", "%G %V %u %j"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 Sunday", "%Y %V %A"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 Sun", "%Y %V %a"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 1", "%Y %V %w"], + ["ISO week directive '%V' is incompatible with the year directive " + "'%Y'. Use the ISO year '%G' instead.", "1999 51 1", "%Y %V %u"], + ["ISO week directive '%V' must be used with the ISO year directive " + "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] + ]) + def test_ValueError_iso_week_year(self, msg, s, _format): + with tm.assert_raises_regex(ValueError, msg): + to_datetime(s, format=_format) + @pytest.mark.parametrize('tz', [None, 'US/Central']) def test_to_datetime_dtarr(self, tz): # DatetimeArray From cb1132c858786437e5a6372fb7b69d83f7facc05 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 19 Jan 2019 21:22:52 -0800 Subject: [PATCH 203/215] Added whatsnew entry? --- doc/source/whatsnew/v0.24.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index a49ea2cf493a6..7ca996ab62851 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -364,6 +364,7 @@ See the :ref:`Advanced documentation on renaming` for more Other Enhancements ^^^^^^^^^^^^^^^^^^ +- Added support for %G strptime directive in parsing datetimes - :func:`merge` now directly allows merge between objects of type ``DataFrame`` and named ``Series``, without the need to convert the ``Series`` object into a ``DataFrame`` beforehand (:issue:`21220`) - ``ExcelWriter`` now accepts ``mode`` as a keyword argument, enabling append to existing workbooks when using the ``openpyxl`` engine (:issue:`3441`) - ``FrozenList`` has gained the ``.union()`` and ``.difference()`` methods. This functionality greatly simplifies groupby's that rely on explicitly excluding certain columns. See :ref:`Splitting an object into groups ` for more information (:issue:`15475`, :issue:`15506`). From bd1c7dce0005af71b94daffd4b194a36403b80a3 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 10:38:00 -0800 Subject: [PATCH 204/215] n --- pandas/_libs/tslibs/strptime.pyx | 7 ++++--- pandas/tests/indexes/datetimes/test_tools.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index a63a0080006b6..dc3ec51a90fea 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -79,7 +79,8 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal, iso_week, iso_year + int week_of_year, week_of_year_start, parse_code, ordinal, + iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -303,7 +304,7 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian @@ -664,7 +665,7 @@ cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): cdef: int correction, ordinal - + correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 ordinal = (iso_week * 7) + iso_weekday - correction # ordinal may be negative or 0 now, which means the date is in the previous diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index c42dfcbac1c3d..d09d352dea500 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -291,7 +291,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] ]) def test_ValueError_iso_week_year(self, msg, s, _format): - with tm.assert_raises_regex(ValueError, msg): + with pytest.raises(ValueError, message=msg): to_datetime(s, format=_format) @pytest.mark.parametrize('tz', [None, 'US/Central']) From d8d06f9c7e0561001e5cad57ae815d9c260db975 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 13:11:01 -0800 Subject: [PATCH 205/215] Fix linting error --- pandas/_libs/tslibs/strptime.pyx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index dc3ec51a90fea..cfada70db525b 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -79,8 +79,8 @@ def array_strptime(object[:] values, object fmt, int64_t[:] iresult object[:] result_timezone int year, month, day, minute, hour, second, weekday, julian - int week_of_year, week_of_year_start, parse_code, ordinal, - iso_week, iso_year + int week_of_year, week_of_year_start, parse_code, ordinal + int iso_week, iso_year int64_t us, ns object val, group_key, ampm, found, timezone dict found_key @@ -304,9 +304,10 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: - year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) + year, julian = _calc_julian_from_V(iso_year, iso_week, + weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. From 5accc8f60af6e0eb47a65b14b27e226aef4db7ca Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 13:32:06 -0800 Subject: [PATCH 206/215] Add changes from reviewer --- doc/source/whatsnew/v0.24.0.rst | 2 +- pandas/tests/indexes/datetimes/test_tools.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 7ca996ab62851..83cb0acf2124e 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -364,7 +364,7 @@ See the :ref:`Advanced documentation on renaming` for more Other Enhancements ^^^^^^^^^^^^^^^^^^ -- Added support for %G strptime directive in parsing datetimes +- Added support for ISO week year format when parsing datetimes using `to_datetime` (:issue:`16607`) - :func:`merge` now directly allows merge between objects of type ``DataFrame`` and named ``Series``, without the need to convert the ``Series`` object into a ``DataFrame`` beforehand (:issue:`21220`) - ``ExcelWriter`` now accepts ``mode`` as a keyword argument, enabling append to existing workbooks when using the ``openpyxl`` engine (:issue:`3441`) - ``FrozenList`` has gained the ``.union()`` and ``.difference()`` methods. This functionality greatly simplifies groupby's that rely on explicitly excluding certain columns. See :ref:`Splitting an object into groups ` for more information (:issue:`15475`, :issue:`15506`). diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index d09d352dea500..a517285f0b5cc 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -253,6 +253,7 @@ class TestToDatetime(object): ['2015-1-7', '%G-%V-%u', datetime(2015, 1, 4, 0, 0)] ]) def test_to_datetime_iso_week_year_format(self, s, _format, dt): + # See GH#16607 assert to_datetime(s, format=_format) == dt @pytest.mark.parametrize("msg, s, _format", [ @@ -291,6 +292,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): "'%G' and a weekday directive '%A', '%a', '%w', or '%u'.", "20", "%V"] ]) def test_ValueError_iso_week_year(self, msg, s, _format): + # See GH#16607 with pytest.raises(ValueError, message=msg): to_datetime(s, format=_format) From 64dec4c7d20e3e7f0408868b6d47ef44efd32fb0 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 21:20:52 -0800 Subject: [PATCH 207/215] Fix errors found in azure --- pandas/_libs/tslibs/strptime.pyx | 4 ++-- pandas/tests/indexes/datetimes/test_tools.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index cfada70db525b..cfe402b11092c 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -304,10 +304,10 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = True if week_of_year_start == 0 else False julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: year, julian = _calc_julian_from_V(iso_year, iso_week, - weekday + 1) + weekday + 1) # Cannot pre-calculate datetime_date() since can change in Julian # calculation and thus could have different value for the day of the wk # calculation. diff --git a/pandas/tests/indexes/datetimes/test_tools.py b/pandas/tests/indexes/datetimes/test_tools.py index a517285f0b5cc..a72aacce2f86d 100644 --- a/pandas/tests/indexes/datetimes/test_tools.py +++ b/pandas/tests/indexes/datetimes/test_tools.py @@ -293,7 +293,7 @@ def test_to_datetime_iso_week_year_format(self, s, _format, dt): ]) def test_ValueError_iso_week_year(self, msg, s, _format): # See GH#16607 - with pytest.raises(ValueError, message=msg): + with pytest.raises(ValueError, match=msg): to_datetime(s, format=_format) @pytest.mark.parametrize('tz', [None, 'US/Central']) From 8f3c92ea35c74868f7478fbbf818071fcbc62ded Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 27 Jan 2019 16:44:25 -0800 Subject: [PATCH 208/215] Making change to one thing --- pandas/_libs/tslibs/strptime.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index cfe402b11092c..8f45879feaa3a 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -302,7 +302,7 @@ def array_strptime(object[:] values, object fmt, # out the Julian day of the year. if julian == -1 and weekday != -1: if week_of_year != -1: - week_starts_Mon = True if week_of_year_start == 0 else False + week_starts_Mon = week_of_year_start == 0 julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, week_starts_Mon) elif iso_year != -1 and iso_week != -1: From ad03061032c1f4a1bde30053c0dc45fa3d3d20e3 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 19 Jan 2019 20:50:11 -0800 Subject: [PATCH 209/215] Added support for iso time --- pandas/_libs/tslibs/strptime.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 8f45879feaa3a..575a37aedeaa8 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -666,7 +666,6 @@ cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): cdef: int correction, ordinal - correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 ordinal = (iso_week * 7) + iso_weekday - correction # ordinal may be negative or 0 now, which means the date is in the previous From 4d7af480539068b8bc819290015fa28909f4d333 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 10:38:00 -0800 Subject: [PATCH 210/215] n --- pandas/_libs/tslibs/strptime.pyx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 575a37aedeaa8..01ca2ed5ff098 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -666,6 +666,10 @@ cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): cdef: int correction, ordinal +<<<<<<< HEAD +======= + +>>>>>>> n correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 ordinal = (iso_week * 7) + iso_weekday - correction # ordinal may be negative or 0 now, which means the date is in the previous From 287f0ed384dbf0ab9d9ba97e91cfe7d7b6a3b77c Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 13:11:01 -0800 Subject: [PATCH 211/215] Fix linting error --- pandas/_libs/tslibs/strptime.pyx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 01ca2ed5ff098..47e399d800807 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -304,7 +304,7 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = week_of_year_start == 0 julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) @@ -666,10 +666,6 @@ cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): cdef: int correction, ordinal -<<<<<<< HEAD -======= - ->>>>>>> n correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 ordinal = (iso_week * 7) + iso_weekday - correction # ordinal may be negative or 0 now, which means the date is in the previous From 1c3caf5278b7de1d4d26429d559827f4325b9c6c Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sun, 20 Jan 2019 21:20:52 -0800 Subject: [PATCH 212/215] Fix errors found in azure --- pandas/_libs/tslibs/strptime.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 47e399d800807..575a37aedeaa8 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -304,7 +304,7 @@ def array_strptime(object[:] values, object fmt, if week_of_year != -1: week_starts_Mon = week_of_year_start == 0 julian = _calc_julian_from_U_or_W(year, week_of_year, weekday, - week_starts_Mon) + week_starts_Mon) elif iso_year != -1 and iso_week != -1: year, julian = _calc_julian_from_V(iso_year, iso_week, weekday + 1) From 12951b9a3cd188b0db8e7aca625b773215411e3d Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Sat, 2 Mar 2019 21:20:15 -0800 Subject: [PATCH 213/215] Moved to 0.25.0 --- doc/source/whatsnew/v0.24.0.rst | 1 - doc/source/whatsnew/v0.25.0.rst | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.rst b/doc/source/whatsnew/v0.24.0.rst index 83cb0acf2124e..a49ea2cf493a6 100644 --- a/doc/source/whatsnew/v0.24.0.rst +++ b/doc/source/whatsnew/v0.24.0.rst @@ -364,7 +364,6 @@ See the :ref:`Advanced documentation on renaming` for more Other Enhancements ^^^^^^^^^^^^^^^^^^ -- Added support for ISO week year format when parsing datetimes using `to_datetime` (:issue:`16607`) - :func:`merge` now directly allows merge between objects of type ``DataFrame`` and named ``Series``, without the need to convert the ``Series`` object into a ``DataFrame`` beforehand (:issue:`21220`) - ``ExcelWriter`` now accepts ``mode`` as a keyword argument, enabling append to existing workbooks when using the ``openpyxl`` engine (:issue:`3441`) - ``FrozenList`` has gained the ``.union()`` and ``.difference()`` methods. This functionality greatly simplifies groupby's that rely on explicitly excluding certain columns. See :ref:`Splitting an object into groups ` for more information (:issue:`15475`, :issue:`15506`). diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 124ec8f4ab92c..ce6f259f97062 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -134,7 +134,7 @@ Categorical Datetimelike ^^^^^^^^^^^^ -- +- Added support for ISO week year format ('%G-%V-%u') when parsing datetimes using :meth: `to_datetime` (:issue:`16607`) - - From 3c1959c42773df3382d4b16e56837e1b57890926 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Mon, 4 Mar 2019 23:19:04 -0800 Subject: [PATCH 214/215] Update docstrings --- pandas/_libs/tslibs/strptime.pyx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index 575a37aedeaa8..e7674c5c364a1 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -636,7 +636,16 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year, int day_of_week, int week_starts_Mon): """Calculate the Julian day based on the year, week of the year, and day of the week, with week_start_day representing whether the week of the year - assumes the week starts on Sunday or Monday (6 or 0).""" + assumes the week starts on Sunday or Monday (6 or 0). + + :param year: the year + :param week_of_year: week taken from format U or W + :param day_of_week: weekday + :param week_starts_Mon: represent whether the week of the year + assumes the week starts on Sunday or Monday (6 or 0) + :returns: converted julian day. + :rtype: int + """ cdef: int first_weekday, week_0_length, days_to_week @@ -662,7 +671,14 @@ cdef _calc_julian_from_U_or_W(int year, int week_of_year, cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): """Calculate the Julian day based on the ISO 8601 year, week, and weekday. ISO weeks start on Mondays, with week 01 being the week containing 4 Jan. - ISO week days range from 1 (Monday) to 7 (Sunday).""" + ISO week days range from 1 (Monday) to 7 (Sunday). + + :param iso_year: the year taken from format %G + :param iso_week: the week taken from format %V + :param iso_weekday: weekday taken from format %u + :returns: the passed in year and the Gregorian ordinal date / julian date system + :rtype: (int, int) + """ cdef: int correction, ordinal From f8bdc92f52e808d0c966b7464582bb819f57cf47 Mon Sep 17 00:00:00 2001 From: Ruijing Li Date: Mon, 4 Mar 2019 23:38:38 -0800 Subject: [PATCH 215/215] Rebased changes --- pandas/_libs/tslibs/strptime.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/_libs/tslibs/strptime.pyx b/pandas/_libs/tslibs/strptime.pyx index e7674c5c364a1..3ee006bca25eb 100644 --- a/pandas/_libs/tslibs/strptime.pyx +++ b/pandas/_libs/tslibs/strptime.pyx @@ -682,6 +682,7 @@ cdef _calc_julian_from_V(int iso_year, int iso_week, int iso_weekday): cdef: int correction, ordinal + correction = datetime_date(iso_year, 1, 4).isoweekday() + 3 ordinal = (iso_week * 7) + iso_weekday - correction # ordinal may be negative or 0 now, which means the date is in the previous

    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