From 550885735e13c0c5595177a84ce3ae39f4b891aa Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Tue, 9 Oct 2018 23:21:06 +0200 Subject: [PATCH 01/17] Add strict-kwarg to is_list_like --- doc/source/whatsnew/v0.24.0.txt | 1 + pandas/core/algorithms.py | 6 ++-- pandas/core/apply.py | 2 +- pandas/core/arrays/base.py | 3 +- pandas/core/arrays/categorical.py | 10 +++---- pandas/core/arrays/datetimelike.py | 2 +- pandas/core/arrays/integer.py | 4 +-- pandas/core/arrays/timedeltas.py | 2 +- pandas/core/base.py | 3 +- pandas/core/computation/ops.py | 4 +-- pandas/core/computation/pytables.py | 5 ++-- pandas/core/dtypes/cast.py | 6 ++-- pandas/core/dtypes/common.py | 4 +-- pandas/core/dtypes/inference.py | 32 ++++++++++++++------- pandas/core/dtypes/missing.py | 2 +- pandas/core/frame.py | 19 +++++++------ pandas/core/generic.py | 38 ++++++++++++++----------- pandas/core/groupby/base.py | 3 +- pandas/core/groupby/grouper.py | 4 +-- pandas/core/indexes/accessors.py | 4 +-- pandas/core/indexes/base.py | 21 +++++++------- pandas/core/indexes/category.py | 2 +- pandas/core/indexes/datetimelike.py | 2 +- pandas/core/indexes/datetimes.py | 2 +- pandas/core/indexes/interval.py | 4 +-- pandas/core/indexes/multi.py | 36 ++++++++++++----------- pandas/core/indexes/timedeltas.py | 5 ++-- pandas/core/indexing.py | 16 +++++------ pandas/core/internals/blocks.py | 23 ++++++++------- pandas/core/ops.py | 5 ++-- pandas/core/panel.py | 4 +-- pandas/core/reshape/melt.py | 8 +++--- pandas/core/reshape/merge.py | 4 +-- pandas/core/reshape/pivot.py | 5 ++-- pandas/core/reshape/reshape.py | 2 +- pandas/core/reshape/util.py | 4 +-- pandas/core/series.py | 23 ++++++++------- pandas/core/sorting.py | 4 +-- pandas/core/sparse/array.py | 2 +- pandas/core/strings.py | 11 +++---- pandas/core/tools/datetimes.py | 6 ++-- pandas/core/tools/timedeltas.py | 2 +- pandas/core/util/hashing.py | 2 +- pandas/core/window.py | 3 +- pandas/io/excel.py | 13 +++++---- pandas/io/formats/format.py | 4 +-- pandas/io/formats/style.py | 2 +- pandas/io/html.py | 3 +- pandas/io/parsers.py | 6 ++-- pandas/io/pytables.py | 2 +- pandas/io/sql.py | 4 +-- pandas/tests/computation/test_eval.py | 2 +- pandas/tests/dtypes/test_inference.py | 17 +++++++---- pandas/tests/extension/decimal/array.py | 2 +- pandas/tests/plotting/common.py | 6 ++-- pandas/tests/plotting/test_frame.py | 2 +- 56 files changed, 229 insertions(+), 184 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index a4209ba90aaee..6598a381a950d 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -629,6 +629,7 @@ Other API Changes - :class:`Index` subtraction will attempt to operate element-wise instead of raising ``TypeError`` (:issue:`19369`) - :class:`pandas.io.formats.style.Styler` supports a ``number-format`` property when using :meth:`~pandas.io.formats.style.Styler.to_excel` (:issue:`22015`) - :meth:`DataFrame.corr` and :meth:`Series.corr` now raise a ``ValueError`` along with a helpful error message instead of a ``KeyError`` when supplied with an invalid method (:issue:`22298`) +- :meth:`dtypes.common.is_list_like` has gained a ``strict``-kwarg, which is ``False`` by default. If set to ``True``, sets are not considered list-like. (:issue:`22397`) - :meth:`shift` will now always return a copy, instead of the previous behaviour of returning self when shifting by 0 (:issue:`22397`) .. _whatsnew_0240.deprecations: diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index e91cc8ec1e996..f5e8ebe210863 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -396,11 +396,11 @@ def isin(comps, values): boolean array same length as comps """ - if not is_list_like(comps): + if not is_list_like(comps, strict=False): raise TypeError("only list-like objects are allowed to be passed" " to isin(), you passed a [{comps_type}]" .format(comps_type=type(comps).__name__)) - if not is_list_like(values): + if not is_list_like(values, strict=False): raise TypeError("only list-like objects are allowed to be passed" " to isin(), you passed a [{values_type}]" .format(values_type=type(values).__name__)) @@ -1178,7 +1178,7 @@ class SelectNFrame(SelectN): def __init__(self, obj, n, keep, columns): super(SelectNFrame, self).__init__(obj, n, keep) - if not is_list_like(columns): + if not is_list_like(columns, strict=False): columns = [columns] columns = list(columns) self.columns = columns diff --git a/pandas/core/apply.py b/pandas/core/apply.py index 40cd952a62138..2b3fe0ef891f5 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -110,7 +110,7 @@ def get_result(self): """ compute the results """ # dispatch to agg - if is_list_like(self.f) or is_dict_like(self.f): + if is_list_like(self.f, strict=False) or is_dict_like(self.f): return self.obj.aggregate(self.f, axis=self.axis, *self.args, **self.kwds) diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index efe587c6aaaad..53f72624e8d87 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -769,7 +769,8 @@ def _create_method(cls, op, coerce_to_dtype=True): def _binop(self, other): def convert_values(param): - if isinstance(param, ExtensionArray) or is_list_like(param): + if (isinstance(param, ExtensionArray) + or is_list_like(param, strict=False)): ovalues = param else: # Assume its an object ovalues = [param] * len(self) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 216bccf7d6309..3d6034ba1c541 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1017,7 +1017,7 @@ def add_categories(self, new_categories, inplace=False): set_categories """ inplace = validate_bool_kwarg(inplace, 'inplace') - if not is_list_like(new_categories): + if not is_list_like(new_categories, strict=False): new_categories = [new_categories] already_included = set(new_categories) & set(self.dtype.categories) if len(already_included) != 0: @@ -1065,7 +1065,7 @@ def remove_categories(self, removals, inplace=False): set_categories """ inplace = validate_bool_kwarg(inplace, 'inplace') - if not is_list_like(removals): + if not is_list_like(removals, strict=False): removals = [removals] removal_set = set(list(removals)) @@ -1981,7 +1981,7 @@ def __setitem__(self, key, value): raise ValueError("Cannot set a Categorical with another, " "without identical categories") - rvalue = value if is_list_like(value) else [value] + rvalue = value if is_list_like(value, strict=False) else [value] from pandas import Index to_add = Index(rvalue).difference(self.categories) @@ -2350,7 +2350,7 @@ def isin(self, values): array([ True, False, True, False, True, False]) """ from pandas.core.series import _sanitize_array - if not is_list_like(values): + if not is_list_like(values, strict=False): raise TypeError("only list-like objects are allowed to be passed" " to isin(), you passed a [{values_type}]" .format(values_type=type(values).__name__)) @@ -2523,7 +2523,7 @@ def _factorize_from_iterable(values): """ from pandas.core.indexes.category import CategoricalIndex - if not is_list_like(values): + if not is_list_like(values, strict=False): raise TypeError("Input must be list-like") if is_categorical(values): diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index e4ace2bfe1509..0b0b24f410384 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -751,7 +751,7 @@ def _evaluate_compare(self, other, op): if not isinstance(other, type(self)): # coerce to a similar object - if not is_list_like(other): + if not is_list_like(other, strict=False): # scalar other = [other] elif lib.is_scalar(lib.item_from_zerodim(other)): diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index e58109a25e1a5..a6f098f7a3c70 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -505,7 +505,7 @@ def cmp_method(self, other): mask = None if isinstance(other, IntegerArray): other, mask = other._data, other._mask - elif is_list_like(other): + elif is_list_like(other, strict=False): other = np.asarray(other) if other.ndim > 0 and len(self) != len(other): raise ValueError('Lengths must match to compare') @@ -568,7 +568,7 @@ def integer_arithmetic_method(self, other): elif getattr(other, 'ndim', 0) > 1: raise NotImplementedError( "can only perform ops with 1-d structures") - elif is_list_like(other): + elif is_list_like(other, strict=False): other = np.asarray(other) if not other.ndim: other = other.item() diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index df9e57cb5f0e1..25d6c7f4c10a6 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -76,7 +76,7 @@ def wrapper(self, other): if isna(other): result.fill(nat_result) - elif not is_list_like(other): + elif not is_list_like(other, strict=False): raise TypeError(msg.format(cls=type(self).__name__, typ=type(other).__name__)) else: diff --git a/pandas/core/base.py b/pandas/core/base.py index 00c049497c0d8..dc493cc71c3fd 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -543,7 +543,8 @@ def is_any_frame(): name=getattr(self, 'name', None)) return result, True - elif is_list_like(arg) and arg not in compat.string_types: + elif (is_list_like(arg, strict=False) + and arg not in compat.string_types): # we require a list, but not an 'str' return self._aggregate_multiple_funcs(arg, _level=_level, diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index ca0c4db4947c4..4c16d59d7c44d 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -234,7 +234,7 @@ def _in(x, y): try: return x.isin(y) except AttributeError: - if is_list_like(x): + if is_list_like(x, strict=False): try: return y.isin(x) except AttributeError: @@ -249,7 +249,7 @@ def _not_in(x, y): try: return ~x.isin(y) except AttributeError: - if is_list_like(x): + if is_list_like(x, strict=False): try: return ~y.isin(x) except AttributeError: diff --git a/pandas/core/computation/pytables.py b/pandas/core/computation/pytables.py index e08df3e340138..a7ae037b24f38 100644 --- a/pandas/core/computation/pytables.py +++ b/pandas/core/computation/pytables.py @@ -127,7 +127,7 @@ def pr(left, right): def conform(self, rhs): """ inplace conform rhs """ - if not is_list_like(rhs): + if not is_list_like(rhs, strict=False): rhs = [rhs] if isinstance(rhs, np.ndarray): rhs = rhs.ravel() @@ -472,7 +472,8 @@ def _validate_where(w): TypeError : An invalid data type was passed in for w (e.g. dict). """ - if not (isinstance(w, (Expr, string_types)) or is_list_like(w)): + if not (isinstance(w, (Expr, string_types)) + or is_list_like(w, strict=False)): raise TypeError("where must be passed as a string, Expr, " "or list-like of Exprs") diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index a95a45d5f9ae4..5cde4c4fdc69a 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -472,7 +472,7 @@ def infer_dtype_from_array(arr, pandas_dtype=False): if isinstance(arr, np.ndarray): return arr.dtype, arr - if not is_list_like(arr): + if not is_list_like(arr, strict=False): arr = [arr] if pandas_dtype and is_extension_type(arr): @@ -518,7 +518,7 @@ def maybe_infer_dtype_type(element): tipo = None if hasattr(element, 'dtype'): tipo = element.dtype - elif is_list_like(element): + elif is_list_like(element, strict=False): element = np.asarray(element) tipo = element.dtype return tipo @@ -914,7 +914,7 @@ def maybe_infer_to_datetimelike(value, convert_dates=False): v = value - if not is_list_like(v): + if not is_list_like(v, strict=False): v = [v] v = np.array(v, copy=False) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index a9fc9d13d4ab3..227a0b457bcdd 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -327,8 +327,8 @@ def is_offsetlike(arr_or_obj): """ if isinstance(arr_or_obj, ABCDateOffset): return True - elif (is_list_like(arr_or_obj) and len(arr_or_obj) and - is_object_dtype(arr_or_obj)): + elif (is_list_like(arr_or_obj, strict=False) and len(arr_or_obj) + and is_object_dtype(arr_or_obj)): return all(isinstance(x, ABCDateOffset) for x in arr_or_obj) return False diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 67f391615eedb..2dd1e3f217ecf 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -7,6 +7,7 @@ from pandas.compat import (PY2, string_types, text_type, string_and_binary_types, re_type) from pandas._libs import lib +import warnings is_bool = lib.is_bool @@ -247,7 +248,7 @@ def is_re_compilable(obj): return True -def is_list_like(obj): +def is_list_like(obj, strict=None): """ Check if the object is list-like. @@ -259,6 +260,8 @@ def is_list_like(obj): Parameters ---------- obj : The object to check. + strict : boolean, default None + Whether `set` should be counted as list-like Returns ------- @@ -282,12 +285,20 @@ def is_list_like(obj): >>> is_list_like(np.array(2))) False """ - - return (isinstance(obj, compat.Iterable) and - # we do not count strings/unicode/bytes as list-like - not isinstance(obj, string_and_binary_types) and - # exclude zero-dimensional numpy arrays, effectively scalars - not (isinstance(obj, np.ndarray) and obj.ndim == 0)) + if strict is None and isinstance(obj, set): + # only raise warning if necessary + warnings.warn('is_list_like will in the future return False for sets. ' + 'To keep the previous behavior, pass `strict=False`. To ' + 'adapt the future behavior and silence this warning, ' + 'pass `strict=True`', FutureWarning) + strict = False if strict is None else strict + + list_like = (isinstance(obj, compat.Iterable) + # we do not count strings/unicode/bytes as set-like + and not isinstance(obj, string_and_binary_types) + # exclude zero-dimensional numpy arrays, effectively scalars + and not (isinstance(obj, np.ndarray) and obj.ndim == 0)) + return list_like and (not strict or not isinstance(obj, set)) def is_array_like(obj): @@ -320,7 +331,7 @@ def is_array_like(obj): False """ - return is_list_like(obj) and hasattr(obj, "dtype") + return is_list_like(obj, strict=False) and hasattr(obj, "dtype") def is_nested_list_like(obj): @@ -363,8 +374,9 @@ def is_nested_list_like(obj): -------- is_list_like """ - return (is_list_like(obj) and hasattr(obj, '__len__') and - len(obj) > 0 and all(is_list_like(item) for item in obj)) + return (is_list_like(obj, strict=False) and hasattr(obj, '__len__') + and len(obj) > 0 and all(is_list_like(item, strict=False) + for item in obj)) def is_dict_like(obj): diff --git a/pandas/core/dtypes/missing.py b/pandas/core/dtypes/missing.py index 66998aa6866f6..b04bae77faf1a 100644 --- a/pandas/core/dtypes/missing.py +++ b/pandas/core/dtypes/missing.py @@ -464,7 +464,7 @@ def _infer_fill_value(val): element to provide proper block construction """ - if not is_list_like(val): + if not is_list_like(val, strict=False): val = [val] val = np.array(val, copy=False) if is_datetimelike(val): diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 35c476f3539ee..8f1082fb16f80 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -421,7 +421,8 @@ def __init__(self, data=None, index=None, columns=None, dtype=None, if not isinstance(data, compat.Sequence): data = list(data) if len(data) > 0: - if is_list_like(data[0]) and getattr(data[0], 'ndim', 1) == 1: + if (is_list_like(data[0], strict=False) + and getattr(data[0], 'ndim', 1) == 1): if is_named_tuple(data[0]) and columns is None: columns = data[0]._fields arrays, columns = _to_arrays(data, columns, dtype=dtype) @@ -2790,7 +2791,8 @@ def __getitem__(self, key): # We are left with two options: a single key, and a collection of keys, # We interpret tuples as collections only for non-MultiIndex - is_single_key = isinstance(key, tuple) or not is_list_like(key) + is_single_key = (isinstance(key, tuple) + or not is_list_like(key, strict=False)) if is_single_key: if self.columns.nlevels > 1: @@ -3152,9 +3154,9 @@ def select_dtypes(self, include=None, exclude=None): 5 False 2.0 """ - if not is_list_like(include): + if not is_list_like(include, strict=False): include = (include,) if include is not None else () - if not is_list_like(exclude): + if not is_list_like(exclude, strict=False): exclude = (exclude,) if exclude is not None else () selection = tuple(map(frozenset, (include, exclude))) @@ -3279,7 +3281,7 @@ def _ensure_valid_index(self, value): passed value """ # GH5632, make sure that we are a Series convertible - if not len(self.index) and is_list_like(value): + if not len(self.index) and is_list_like(value, strict=False): try: value = Series(value) except (ValueError, NotImplementedError, TypeError): @@ -7661,7 +7663,7 @@ def isin(self, values): "a duplicate axis.") return self.eq(values.reindex_like(self)) else: - if not is_list_like(values): + if not is_list_like(values, strict=False): raise TypeError("only list-like or dict-like objects are " "allowed to be passed to DataFrame.isin(), " "you passed a " @@ -7731,7 +7733,7 @@ def extract_index(data): elif isinstance(v, dict): have_dicts = True indexes.append(list(v.keys())) - elif is_list_like(v) and getattr(v, 'ndim', 1) == 1: + elif is_list_like(v, strict=False) and getattr(v, 'ndim', 1) == 1: have_raw_arrays = True raw_lengths.append(len(v)) @@ -7774,7 +7776,8 @@ def convert(v): # this is equiv of np.asarray, but does object conversion # and platform dtype preservation try: - if is_list_like(values[0]) or hasattr(values[0], 'len'): + if (is_list_like(values[0], strict=False) + or hasattr(values[0], 'len')): values = np.array([convert(v) for v in values]) elif isinstance(values[0], np.ndarray) and values[0].ndim == 0: # GH#21861 diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 0b5a10283946c..275c7d70cc7dd 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1179,8 +1179,8 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False): 2 3 6 """ inplace = validate_bool_kwarg(inplace, 'inplace') - non_mapper = is_scalar(mapper) or (is_list_like(mapper) and not - is_dict_like(mapper)) + non_mapper = is_scalar(mapper) or (is_list_like(mapper, strict=False) + and not is_dict_like(mapper)) if non_mapper: return self._set_axis_name(mapper, axis=axis, inplace=inplace) else: @@ -3263,7 +3263,7 @@ def xs(self, key, axis=0, level=None, drop_level=True): # if we encounter an array-like and we only have 1 dim # that means that their are list/ndarrays inside the Series! # so just return them (GH 6394) - if not is_list_like(new_values) or self.ndim == 1: + if not is_list_like(new_values, strict=False) or self.ndim == 1: return com.maybe_box_datetimelike(new_values) result = self._constructor_sliced( @@ -4674,7 +4674,8 @@ def __setattr__(self, name, value): else: object.__setattr__(self, name, value) except (AttributeError, TypeError): - if isinstance(self, ABCDataFrame) and (is_list_like(value)): + if (isinstance(self, ABCDataFrame) + and is_list_like(value, strict=False)): warnings.warn("Pandas doesn't allow columns to be " "created via a new attribute name - see " "https://pandas.pydata.org/pandas-docs/" @@ -5668,7 +5669,7 @@ def fillna(self, value=None, method=None, axis=None, inplace=False, if isinstance(value, (dict, ABCSeries)): from pandas import Series value = Series(value) - elif not is_list_like(value): + elif not is_list_like(value, strict=False): pass else: raise TypeError('"value" parameter must be a scalar, dict ' @@ -5693,7 +5694,7 @@ def fillna(self, value=None, method=None, axis=None, inplace=False, obj.fillna(v, limit=limit, inplace=True, downcast=downcast) return result if not inplace else None - elif not is_list_like(value): + elif not is_list_like(value, strict=False): new_data = self._data.fillna(value=value, limit=limit, inplace=inplace, downcast=downcast) @@ -6094,7 +6095,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, return None if inplace else res # {'A': NA} -> 0 - elif not is_list_like(value): + elif not is_list_like(value, strict=False): keys = [(k, src) for k, src in compat.iteritems(to_replace) if k in self] keys_len = len(keys) - 1 @@ -6110,8 +6111,9 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, raise TypeError('value argument must be scalar, dict, or ' 'Series') - elif is_list_like(to_replace): # [NA, ''] -> [0, 'missing'] - if is_list_like(value): + elif is_list_like(to_replace, strict=False): + # [NA, ''] -> [0, 'missing'] + if is_list_like(value, strict=False): if len(to_replace) != len(value): raise ValueError('Replacement lists must match ' 'in length. Expecting %d got %d ' % @@ -6127,8 +6129,9 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, value=value, inplace=inplace, regex=regex) elif to_replace is None: - if not (is_re_compilable(regex) or - is_list_like(regex) or is_dict_like(regex)): + if not (is_re_compilable(regex) + or is_list_like(regex, strict=False) + or is_dict_like(regex)): raise TypeError("'regex' must be a string or a compiled " "regular expression or a list or dict of " "strings or regular expressions, you " @@ -6149,7 +6152,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, inplace=inplace, regex=regex) - elif not is_list_like(value): # NA -> 0 + elif not is_list_like(value, strict=False): # NA -> 0 new_data = self._data.replace(to_replace=to_replace, value=value, inplace=inplace, regex=regex) @@ -6474,10 +6477,10 @@ def asof(self, where, subset=None): else: if subset is None: subset = self.columns - if not is_list_like(subset): + if not is_list_like(subset, strict=False): subset = [subset] - is_list = is_list_like(where) + is_list = is_list_like(where, strict=False) if not is_list: start = self.index[0] if isinstance(self.index, PeriodIndex): @@ -6709,7 +6712,8 @@ def _clip_with_one_bound(self, threshold, method, axis, inplace): # GH #15390 # In order for where method to work, the threshold must # be transformed to NDFrame from other array like structure. - if (not isinstance(threshold, ABCSeries)) and is_list_like(threshold): + if (not isinstance(threshold, ABCSeries) + and is_list_like(threshold, strict=False)): if isinstance(self, ABCSeries): threshold = pd.Series(threshold, index=self.index) else: @@ -6810,9 +6814,9 @@ def clip(self, lower=None, upper=None, axis=None, inplace=False, # so ignore # GH 19992 # numpy doesn't drop a list-like bound containing NaN - if not is_list_like(lower) and np.any(pd.isnull(lower)): + if not is_list_like(lower, strict=False) and np.any(pd.isnull(lower)): lower = None - if not is_list_like(upper) and np.any(pd.isnull(upper)): + if not is_list_like(upper, strict=False) and np.any(pd.isnull(upper)): upper = None # GH 2747 (arguments were reversed) diff --git a/pandas/core/groupby/base.py b/pandas/core/groupby/base.py index ac84971de08d8..228e2434ebb2c 100644 --- a/pandas/core/groupby/base.py +++ b/pandas/core/groupby/base.py @@ -57,7 +57,8 @@ def _gotitem(self, key, ndim, subset=None): **kwargs) self._reset_cache() if subset.ndim == 2: - if is_scalar(key) and key in subset or is_list_like(key): + if (is_scalar(key) and key in subset + or is_list_like(key, strict=False)): self._selection = key return self diff --git a/pandas/core/groupby/grouper.py b/pandas/core/groupby/grouper.py index e7144fb1d2932..d23acea6f168e 100644 --- a/pandas/core/groupby/grouper.py +++ b/pandas/core/groupby/grouper.py @@ -440,7 +440,7 @@ def _get_grouper(obj, key=None, axis=0, level=None, sort=True, # on the outside of this condition. # (GH 17621) if isinstance(group_axis, MultiIndex): - if is_list_like(level) and len(level) == 1: + if is_list_like(level, strict=False) and len(level) == 1: level = level[0] if key is None and is_scalar(level): @@ -452,7 +452,7 @@ def _get_grouper(obj, key=None, axis=0, level=None, sort=True, # allow level to be a length-one list-like object # (e.g., level=[0]) # GH 13901 - if is_list_like(level): + if is_list_like(level, strict=False): nlevels = len(level) if nlevels == 1: level = level[0] diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index a1868980faed3..594770294a3cd 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -63,7 +63,7 @@ def _delegate_property_get(self, name): if isinstance(result, np.ndarray): if is_integer_dtype(result): result = result.astype('int64') - elif not is_list_like(result): + elif not is_list_like(result, strict=False): return result result = np.asarray(result) @@ -97,7 +97,7 @@ def _delegate_method(self, name, *args, **kwargs): method = getattr(values, name) result = method(*args, **kwargs) - if not is_list_like(result): + if not is_list_like(result, strict=False): return result result = Series(result, index=self.index, name=self.name) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 51c84d6e28cb4..4fb92ca6fb266 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -445,7 +445,7 @@ def __new__(cls, data=None, dtype=None, copy=False, name=None, elif data is None or is_scalar(data): cls._scalar_data_error(data) else: - if tupleize_cols and is_list_like(data): + if tupleize_cols and is_list_like(data, strict=False): # GH21470: convert iterable to list before determining if empty if is_iterator(data): data = list(data) @@ -1020,11 +1020,11 @@ def _validate_names(self, name=None, names=None, deep=False): elif names is None and name is None: return deepcopy(self.names) if deep else self.names elif names is not None: - if not is_list_like(names): + if not is_list_like(names, strict=False): raise TypeError("Must pass list-like as `names`.") return names else: - if not is_list_like(name): + if not is_list_like(name, strict=False): return [name] return name @@ -1222,7 +1222,7 @@ def _to_safe_for_reshape(self): return self def _assert_can_do_setop(self, other): - if not is_list_like(other): + if not is_list_like(other, strict=False): raise TypeError('Input must be Index or array-like') return True @@ -1267,7 +1267,7 @@ def _set_names(self, values, level=None): ------ TypeError if each name is not hashable. """ - if not is_list_like(values): + if not is_list_like(values, strict=False): raise ValueError('Names must be a list-like') if len(values) != 1: raise ValueError('Length of new names must be 1, got %d' % @@ -1337,17 +1337,18 @@ def set_names(self, names, level=None, inplace=False): if level is not None and not isinstance(self, MultiIndex): raise ValueError('Level must be None for non-MultiIndex') - if level is not None and not is_list_like(level) and is_list_like( - names): + if (level is not None and not is_list_like(level, strict=False) + and is_list_like(names, strict=False)): msg = "Names must be a string when a single level is provided." raise TypeError(msg) - if not is_list_like(names) and level is None and self.nlevels > 1: + if (not is_list_like(names, strict=False) + and level is None and self.nlevels > 1): raise TypeError("Must pass list-like as `names`.") - if not is_list_like(names): + if not is_list_like(names, strict=False): names = [names] - if level is not None and not is_list_like(level): + if level is not None and not is_list_like(level, strict=False): level = [level] if inplace: diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 45703c220a4be..4d2aaaa3ddaad 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -244,7 +244,7 @@ def _is_dtype_compat(self, other): "when appending") else: values = other - if not is_list_like(values): + if not is_list_like(values, strict=False): values = [values] other = CategoricalIndex(self._create_categorical( other, dtype=self.dtype)) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 1ec30ecbb3a3b..ed896e55270b6 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -322,7 +322,7 @@ def __contains__(self, key): try: res = self.get_loc(key) return (is_scalar(res) or isinstance(res, slice) or - (is_list_like(res) and len(res))) + (is_list_like(res, strict=False) and len(res))) except (KeyError, TypeError, ValueError): return False diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index e40ceadc1a083..616f5336a7899 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1396,7 +1396,7 @@ def delete(self, loc): if loc in (0, -len(self), -1, len(self) - 1): freq = self.freq else: - if is_list_like(loc): + if is_list_like(loc, strict=False): loc = lib.maybe_indices_to_slice( ensure_int64(np.array(loc)), len(self)) if isinstance(loc, slice) and loc.step in (1, None): diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index 25d4dd0cbcc81..b9760f3b8d683 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -560,7 +560,7 @@ def _maybe_convert_i8(self, key): Int64Index if converted list-like. """ original = key - if is_list_like(key): + if is_list_like(key, strict=False): key = ensure_index(key) if not self._needs_i8_conversion(key): @@ -734,7 +734,7 @@ def get_loc(self, key, method=None): def get_value(self, series, key): if com.is_bool_indexer(key): loc = key - elif is_list_like(key): + elif is_list_like(key, strict=False): loc = self.get_indexer(key) elif isinstance(key, slice): diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 3cccb65503378..367f25b21889d 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -389,15 +389,16 @@ def set_levels(self, levels, level=None, inplace=False, labels=[[0, 0, 1, 1], [0, 1, 0, 1]], names=[u'foo', u'bar']) """ - if level is not None and not is_list_like(level): - if not is_list_like(levels): + if level is not None and not is_list_like(level, strict=False): + if not is_list_like(levels, strict=False): raise TypeError("Levels must be list-like") - if is_list_like(levels[0]): + if is_list_like(levels[0], strict=False): raise TypeError("Levels must be list-like") level = [level] levels = [levels] - elif level is None or is_list_like(level): - if not is_list_like(levels) or not is_list_like(levels[0]): + elif level is None or is_list_like(level, strict=False): + if (not is_list_like(levels, strict=False) + or not is_list_like(levels[0], strict=False)): raise TypeError("Levels must be list of lists-like") if inplace: @@ -485,15 +486,16 @@ def set_labels(self, labels, level=None, inplace=False, labels=[[1, 0, 1, 0], [0, 0, 1, 1]], names=[u'foo', u'bar']) """ - if level is not None and not is_list_like(level): - if not is_list_like(labels): + if level is not None and not is_list_like(level, strict=False): + if not is_list_like(labels, strict=False): raise TypeError("Labels must be list-like") - if is_list_like(labels[0]): + if is_list_like(labels[0], strict=False): raise TypeError("Labels must be list-like") level = [level] labels = [labels] - elif level is None or is_list_like(level): - if not is_list_like(labels) or not is_list_like(labels[0]): + elif level is None or is_list_like(level, strict=False): + if (not is_list_like(labels, strict=False) + or not is_list_like(labels[0], strict=False)): raise TypeError("Labels must be list of lists-like") if inplace: @@ -685,7 +687,7 @@ def _set_names(self, names, level=None, validate=True): """ # GH 15110 # Don't allow a single string for names in a MultiIndex - if names is not None and not is_list_like(names): + if names is not None and not is_list_like(names, strict=False): raise ValueError('Names should be list-like for a MultiIndex') names = list(names) @@ -1178,7 +1180,7 @@ def to_frame(self, index=True, name=None): from pandas import DataFrame if name is not None: - if not is_list_like(name): + if not is_list_like(name, strict=False): raise TypeError("'name' must be a list / sequence " "of column names.") @@ -1295,7 +1297,7 @@ def from_arrays(cls, arrays, sortorder=None, names=None): MultiIndex.from_product : Make a MultiIndex from cartesian product of iterables """ - if not is_list_like(arrays): + if not is_list_like(arrays, strict=False): raise TypeError("Input must be a list / sequence of array-likes.") elif is_iterator(arrays): arrays = list(arrays) @@ -1344,7 +1346,7 @@ def from_tuples(cls, tuples, sortorder=None, names=None): MultiIndex.from_product : Make a MultiIndex from cartesian product of iterables """ - if not is_list_like(tuples): + if not is_list_like(tuples, strict=False): raise TypeError('Input must be a list / sequence of tuple-likes.') elif is_iterator(tuples): tuples = list(tuples) @@ -1403,7 +1405,7 @@ def from_product(cls, iterables, sortorder=None, names=None): from pandas.core.arrays.categorical import _factorize_from_iterables from pandas.core.reshape.util import cartesian_product - if not is_list_like(iterables): + if not is_list_like(iterables, strict=False): raise TypeError("Input must be a list / sequence of iterables.") elif is_iterator(iterables): iterables = list(iterables) @@ -1976,7 +1978,7 @@ def get_indexer(self, target, method=None, limit=None, tolerance=None): target = ensure_index(target) # empty indexer - if is_list_like(target) and not len(target): + if is_list_like(target, strict=False) and not len(target): return ensure_platform_int(np.array([])) if not isinstance(target, MultiIndex): @@ -2574,7 +2576,7 @@ def _update_indexer(idxr, indexer=indexer): indexer = _update_indexer(_convert_to_indexer(k), indexer=indexer) - elif is_list_like(k): + elif is_list_like(k, strict=False): # a collection of labels to include from this level (these # are or'd) indexers = None diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index ee604f44b98e0..a0a683106bef2 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -488,7 +488,8 @@ def get_loc(self, key, method=None, tolerance=None): ------- loc : int """ - if is_list_like(key) or (isinstance(key, datetime) and key is not NaT): + if (is_list_like(key, strict=False) + or (isinstance(key, datetime) and key is not NaT)): # GH#20464 datetime check here is to ensure we don't allow # datetime objects to be incorrectly treated as timedelta # objects; NaT is a special case because it plays a double role @@ -698,7 +699,7 @@ def delete(self, loc): if loc in (0, -len(self), -1, len(self) - 1): freq = self.freq else: - if is_list_like(loc): + if is_list_like(loc, strict=False): loc = lib.maybe_indices_to_slice( ensure_int64(np.array(loc)), len(self)) if isinstance(loc, slice) and loc.step in (1, None): diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index b3c913f21dd86..39cf27e388ce1 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2528,7 +2528,7 @@ def check_setitem_lengths(indexer, value, values): """ # boolean with truth values == len of the value is ok too if isinstance(indexer, (np.ndarray, list)): - if is_list_like(value) and len(indexer) != len(value): + if is_list_like(value, strict=False) and len(indexer) != len(value): if not (isinstance(indexer, np.ndarray) and indexer.dtype == np.bool_ and len(indexer[indexer]) == len(value)): @@ -2537,7 +2537,7 @@ def check_setitem_lengths(indexer, value, values): # slice elif isinstance(indexer, slice): - if is_list_like(value) and len(values): + if is_list_like(value, strict=False) and len(values): if len(value) != length_of_indexer(indexer, values): raise ValueError("cannot set using a slice indexer with a " "different length than the value") @@ -2677,16 +2677,16 @@ def is_nested_tuple(tup, labels): for i, k in enumerate(tup): - if is_list_like(k) or isinstance(k, slice): + if is_list_like(k, strict=False) or isinstance(k, slice): return isinstance(labels, MultiIndex) return False def is_list_like_indexer(key): - # allow a list_like, but exclude NamedTuples which can be indexers - return is_list_like(key) and not (isinstance(key, tuple) and - type(key) is not tuple) + # allow a list-like, but exclude NamedTuples which can be indexers + return (is_list_like(key, strict=False) + and not (isinstance(key, tuple) and type(key) is not tuple)) def is_label_like(key): @@ -2734,9 +2734,9 @@ def _non_reducing_slice(slice_): def pred(part): # true when slice does *not* reduce - return isinstance(part, slice) or is_list_like(part) + return isinstance(part, slice) or is_list_like(part, strict=False) - if not is_list_like(slice_): + if not is_list_like(slice_, strict=False): if not isinstance(slice_, slice): # a 1-d slice, like df.loc[1] slice_ = [[slice_]] diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index 0e57dd33b1c4e..f24ae14013d93 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -910,7 +910,7 @@ def _is_scalar_indexer(indexer): def _is_empty_indexer(indexer): # return a boolean if we have an empty indexer - if is_list_like(indexer) and not len(indexer): + if is_list_like(indexer, strict=False) and not len(indexer): return True if arr_value.ndim == 1: if not isinstance(indexer, tuple): @@ -978,7 +978,8 @@ def putmask(self, mask, new, align=True, inplace=False, axis=0, mask = getattr(mask, 'values', mask) # if we are passed a scalar None, convert it here - if not is_list_like(new) and isna(new) and not self.is_object: + if (not is_list_like(new, strict=False) + and isna(new) and not self.is_object): new = self.fill_value if self._can_hold_element(new): @@ -1002,7 +1003,7 @@ def putmask(self, mask, new, align=True, inplace=False, axis=0, # # TODO: this prob needs some better checking # for 2D cases - if ((is_list_like(new) and + if ((is_list_like(new, strict=False) and np.any(mask[mask]) and getattr(new, 'ndim', 1) == 1)): @@ -1378,7 +1379,8 @@ def get_result(other): # avoid numpy warning of elementwise comparisons elif func.__name__ == 'eq': - if is_list_like(other) and not isinstance(other, np.ndarray): + if (is_list_like(other, strict=False) + and not isinstance(other, np.ndarray)): other = np.asarray(other) # if we can broadcast, then ok @@ -1434,7 +1436,8 @@ def handle_error(): # differentiate between an invalid ndarray-ndarray comparison # and an invalid type comparison - if isinstance(values, np.ndarray) and is_list_like(other): + if (isinstance(values, np.ndarray) + and is_list_like(other, strict=False)): raise ValueError( 'Invalid broadcasting comparison [{other!r}] with ' 'block values'.format(other=other)) @@ -1648,7 +1651,7 @@ def _nanpercentile(values, q, axis, **kw): from pandas import Float64Index is_empty = values.shape[axis] == 0 - if is_list_like(qs): + if is_list_like(qs, strict=False): ax = Float64Index(qs) if is_empty: @@ -2484,8 +2487,8 @@ def should_store(self, value): def replace(self, to_replace, value, inplace=False, filter=None, regex=False, convert=True, mgr=None): - to_rep_is_list = is_list_like(to_replace) - value_is_list = is_list_like(value) + to_rep_is_list = is_list_like(to_replace, strict=False) + value_is_list = is_list_like(value, strict=False) both_lists = to_rep_is_list and value_is_list either_list = to_rep_is_list or value_is_list @@ -3473,7 +3476,7 @@ def _putmask_smart(v, m, n): # that numpy does when numeric are mixed with strings # n should be the length of the mask or a scalar here - if not is_list_like(n): + if not is_list_like(n, strict=False): n = np.repeat(n, len(m)) elif isinstance(n, np.ndarray) and n.ndim == 0: # numpy scalar n = np.repeat(np.array(n, ndmin=1), len(m)) @@ -3505,7 +3508,7 @@ def _putmask_smart(v, m, n): is_integer_dtype(nn_at.dtype))): comp = (nn == nn_at) - if is_list_like(comp) and comp.all(): + if is_list_like(comp, strict=False) and comp.all(): nv = v.copy() nv[m] = nn_at return nv diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 20559bca9caed..2967b38abda53 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -1613,7 +1613,8 @@ def wrapper(self, other): else: # scalars, list, tuple, np.array is_other_int_dtype = is_integer_dtype(np.asarray(other)) - if is_list_like(other) and not isinstance(other, np.ndarray): + if (is_list_like(other, strict=False) + and not isinstance(other, np.ndarray)): # TODO: Can we do this before the is_integer_dtype check? # could the is_integer_dtype check be checking the wrong # thing? e.g. other = [[0, 1], [2, 3], [4, 5]]? @@ -1829,7 +1830,7 @@ def to_series(right): raise ValueError('Unable to coerce to Series/DataFrame, dim ' 'must be <= 2: {dim}'.format(dim=right.shape)) - elif (is_list_like(right) and + elif (is_list_like(right, strict=False) and not isinstance(right, (ABCSeries, ABCDataFrame))): # GH17901 right = to_series(right) diff --git a/pandas/core/panel.py b/pandas/core/panel.py index 1e2d4000413bb..b8b672d8d6009 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -286,7 +286,7 @@ def __getitem__(self, key): if isinstance(self._info_axis, MultiIndex): return self._getitem_multilevel(key) - if not (is_list_like(key) or isinstance(key, slice)): + if not (is_list_like(key, strict=False) or isinstance(key, slice)): return super(Panel, self).__getitem__(key) return self.loc[key] @@ -884,7 +884,7 @@ def _ixs(self, i, axis=0): # if we have a multi-index and a single tuple, then its a reduction # (GH 7516) if not (isinstance(ax, MultiIndex) and isinstance(key, tuple)): - if is_list_like(key): + if is_list_like(key, strict=False): indexer = {self._get_axis_name(axis): key} return self.reindex(**indexer) diff --git a/pandas/core/reshape/melt.py b/pandas/core/reshape/melt.py index 26221143c0cdf..6b1172a1fb17b 100644 --- a/pandas/core/reshape/melt.py +++ b/pandas/core/reshape/melt.py @@ -26,7 +26,7 @@ def melt(frame, id_vars=None, value_vars=None, var_name=None, value_name='value', col_level=None): # TODO: what about the existing index? if id_vars is not None: - if not is_list_like(id_vars): + if not is_list_like(id_vars, strict=False): id_vars = [id_vars] elif (isinstance(frame.columns, ABCMultiIndex) and not isinstance(id_vars, list)): @@ -38,7 +38,7 @@ def melt(frame, id_vars=None, value_vars=None, var_name=None, id_vars = [] if value_vars is not None: - if not is_list_like(value_vars): + if not is_list_like(value_vars, strict=False): value_vars = [value_vars] elif (isinstance(frame.columns, ABCMultiIndex) and not isinstance(value_vars, list)): @@ -409,7 +409,7 @@ def melt_stub(df, stub, i, j, value_vars, sep): return newdf.set_index(i + [j]) - if not is_list_like(stubnames): + if not is_list_like(stubnames, strict=False): stubnames = [stubnames] else: stubnames = list(stubnames) @@ -417,7 +417,7 @@ def melt_stub(df, stub, i, j, value_vars, sep): if any(col in stubnames for col in df.columns): raise ValueError("stubname can't be identical to a column name") - if not is_list_like(i): + if not is_list_like(i, strict=False): i = [i] else: i = list(i) diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py index d0c7b66978661..ddf51f3154ad8 100644 --- a/pandas/core/reshape/merge.py +++ b/pandas/core/reshape/merge.py @@ -1296,9 +1296,9 @@ def _validate_specification(self): # add 'by' to our key-list so we can have it in the # output as a key if self.left_by is not None: - if not is_list_like(self.left_by): + if not is_list_like(self.left_by, strict=False): self.left_by = [self.left_by] - if not is_list_like(self.right_by): + if not is_list_like(self.right_by, strict=False): self.right_by = [self.right_by] if len(self.left_by) != len(self.right_by): diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index b525dddeb1ba5..7fe900e339648 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -48,7 +48,7 @@ def pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', values_passed = values is not None if values_passed: - if is_list_like(values): + if is_list_like(values, strict=False): values_multi = True values = list(values) else: @@ -383,7 +383,8 @@ def pivot(data, index=None, columns=None, values=None): index = data[index] index = MultiIndex.from_arrays([index, data[columns]]) - if is_list_like(values) and not isinstance(values, tuple): + if (is_list_like(values, strict=False) + and not isinstance(values, tuple)): # Exclude tuple because it is seen as a single column name indexed = data._constructor(data[values].values, index=index, columns=values) diff --git a/pandas/core/reshape/reshape.py b/pandas/core/reshape/reshape.py index 50f6e310705d7..e306f4626a7ed 100644 --- a/pandas/core/reshape/reshape.py +++ b/pandas/core/reshape/reshape.py @@ -743,7 +743,7 @@ def check_len(item, name): len_msg = ("Length of '{name}' ({len_item}) did not match the " "length of the columns being encoded ({len_enc}).") - if is_list_like(item): + if is_list_like(item, strict=False): if not len(item) == data_to_encode.shape[1]: len_msg = \ len_msg.format(name=name, len_item=len(item), diff --git a/pandas/core/reshape/util.py b/pandas/core/reshape/util.py index 1c2033d90cd8a..af99172b2c65a 100644 --- a/pandas/core/reshape/util.py +++ b/pandas/core/reshape/util.py @@ -32,10 +32,10 @@ def cartesian_product(X): pandas.compat.product : An alias for itertools.product. """ msg = "Input must be a list-like of list-likes" - if not is_list_like(X): + if not is_list_like(X, strict=False): raise TypeError(msg) for x in X: - if not is_list_like(x): + if not is_list_like(x, strict=False): raise TypeError(msg) if len(X) == 0: diff --git a/pandas/core/series.py b/pandas/core/series.py index a613b22ea9046..4cd1745baf990 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -249,10 +249,10 @@ def __init__(self, data=None, index=None, dtype=None, name=None, data = data.to_dense() if index is None: - if not is_list_like(data): + if not is_list_like(data, strict=False): data = [data] index = ibase.default_index(len(data)) - elif is_list_like(data): + elif is_list_like(data, strict=False): # a scalar numpy array is list-like but doesn't # have a proper length @@ -775,8 +775,8 @@ def __getitem__(self, key): result = self.index.get_value(self, key) if not is_scalar(result): - if is_list_like(result) and not isinstance(result, Series): - + if (is_list_like(result, strict=False) + and not isinstance(result, Series)): # we need to box if loc of the key isn't scalar here # otherwise have inline ndarray/lists try: @@ -1895,7 +1895,7 @@ def quantile(self, q=0.5, interpolation='linear'): result = self._data.quantile(qs=q, interpolation=interpolation) - if is_list_like(q): + if is_list_like(q, strict=False): return self._constructor(result, index=Float64Index(q), name=self.name) @@ -2561,7 +2561,7 @@ def _try_kind_sort(arr): argsorted = _try_kind_sort(arr[good]) - if is_list_like(ascending): + if is_list_like(ascending, strict=False): if len(ascending) != 1: raise ValueError('Length of ascending (%d) must be 1 ' 'for Series' % (len(ascending))) @@ -3494,8 +3494,8 @@ def rename(self, index=None, **kwargs): kwargs['inplace'] = validate_bool_kwarg(kwargs.get('inplace', False), 'inplace') - non_mapping = is_scalar(index) or (is_list_like(index) and - not is_dict_like(index)) + non_mapping = is_scalar(index) or (is_list_like(index, strict=False) + and not is_dict_like(index)) if non_mapping: return self._set_name(index, inplace=kwargs.get('inplace')) return super(Series, self).rename(index=index, **kwargs) @@ -4234,9 +4234,10 @@ def _try_cast(arr, take_fast_path): subarr = maybe_cast_to_datetime(arr, dtype) # Take care in creating object arrays (but iterators are not # supported): - if is_object_dtype(dtype) and (is_list_like(subarr) and - not (is_iterator(subarr) or - isinstance(subarr, np.ndarray))): + if (is_object_dtype(dtype) + and (is_list_like(subarr, strict=False) + and not (is_iterator(subarr) + or isinstance(subarr, np.ndarray)))): subarr = construct_1d_object_array_from_listlike(subarr) elif not is_extension_type(subarr): subarr = construct_1d_ndarray_preserving_na(subarr, dtype, diff --git a/pandas/core/sorting.py b/pandas/core/sorting.py index 5aa9ea658482b..fe8bdb4d32c67 100644 --- a/pandas/core/sorting.py +++ b/pandas/core/sorting.py @@ -424,7 +424,7 @@ def safe_sort(values, labels=None, na_sentinel=-1, assume_unique=False): ValueError * If ``labels`` is not None and ``values`` contain duplicates. """ - if not is_list_like(values): + if not is_list_like(values, strict=False): raise TypeError("Only list-like objects are allowed to be passed to" "safe_sort as values") @@ -459,7 +459,7 @@ def sort_mixed(values): if labels is None: return ordered - if not is_list_like(labels): + if not is_list_like(labels, strict=False): raise TypeError("Only list-like objects or None are allowed to be" "passed to safe_sort as labels") labels = ensure_platform_int(np.asarray(labels)) diff --git a/pandas/core/sparse/array.py b/pandas/core/sparse/array.py index 186a2490a5f2e..47ffdd3539bd4 100644 --- a/pandas/core/sparse/array.py +++ b/pandas/core/sparse/array.py @@ -776,7 +776,7 @@ def _sanitize_values(arr): if isinstance(arr, np.ndarray): pass - elif is_list_like(arr) and len(arr) > 0: + elif is_list_like(arr, strict=False) and len(arr) > 0: arr = maybe_convert_platform(arr) else: diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 5a23951145cb4..27bfc58bc5dc5 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -1973,7 +1973,7 @@ def _wrap_result(self, result, use_codes=True, # not needed when inferred def cons_row(x): - if is_list_like(x): + if is_list_like(x, strict=False): return x else: return [x] @@ -2083,12 +2083,12 @@ def _get_series_list(self, others, ignore_index=False): elif isinstance(others, np.ndarray) and others.ndim == 2: others = DataFrame(others, index=idx) return ([others[x] for x in others], False) - elif is_list_like(others): + elif is_list_like(others, strict=True): others = list(others) # ensure iterators do not get read twice etc # in case of list-like `others`, all elements must be # either one-dimensional list-likes or scalars - if all(is_list_like(x) for x in others): + if all(is_list_like(x, strict=True) for x in others): los = [] join_warn = False depr_warn = False @@ -2116,7 +2116,8 @@ def _get_series_list(self, others, ignore_index=False): # nested list-likes are forbidden: # -> elements of nxt must not be list-like is_legal = ((no_deep and nxt.dtype == object) - or all(not is_list_like(x) for x in nxt)) + or all(not is_list_like(x, strict=True) + for x in nxt)) # DataFrame is false positive of is_legal # because "x in df" returns column names @@ -2134,7 +2135,7 @@ def _get_series_list(self, others, ignore_index=False): 'deprecated and will be removed in a future ' 'version.', FutureWarning, stacklevel=3) return (los, join_warn) - elif all(not is_list_like(x) for x in others): + elif all(not is_list_like(x, strict=True) for x in others): return ([Series(others, index=idx)], False) raise TypeError(err_msg) diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py index eb8d2b0b6c809..ac851b8ee3cdf 100644 --- a/pandas/core/tools/datetimes.py +++ b/pandas/core/tools/datetimes.py @@ -375,7 +375,7 @@ def _adjust_to_origin(arg, origin, unit): offset = offset // tslibs.Timedelta(1, unit=unit) # scalars & ndarray-like can handle the addition - if is_list_like(arg) and not isinstance( + if is_list_like(arg, strict=False) and not isinstance( arg, (ABCSeries, ABCIndexClass, np.ndarray)): arg = np.asarray(arg) arg = arg + offset @@ -580,7 +580,7 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, else: convert_listlike = partial(convert_listlike, name=arg.name) result = convert_listlike(arg, box, format) - elif is_list_like(arg): + elif is_list_like(arg, strict=False): cache_array = _maybe_cache(arg, format, cache, convert_listlike) if not cache_array.empty: result = _convert_and_box_cache(arg, cache_array, box, errors) @@ -868,7 +868,7 @@ def _convert_listlike(arg, format): return Series(values, index=arg.index, name=arg.name) elif isinstance(arg, ABCIndexClass): return _convert_listlike(arg, format) - elif is_list_like(arg): + elif is_list_like(arg, strict=False): return _convert_listlike(arg, format) return _convert_listlike(np.array([arg]), format)[0] diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index 4dc4fcb00d84d..f1e64dc04af3b 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -88,7 +88,7 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'): elif isinstance(arg, np.ndarray) and arg.ndim == 0: # extract array scalar and process below arg = arg.item() - elif is_list_like(arg) and getattr(arg, 'ndim', 1) == 1: + elif is_list_like(arg, strict=False) and getattr(arg, 'ndim', 1) == 1: return _convert_listlike(arg, unit=unit, box=box, errors=errors) elif getattr(arg, 'ndim', 1) > 1: raise TypeError('arg must be a string, timedelta, list, tuple, ' diff --git a/pandas/core/util/hashing.py b/pandas/core/util/hashing.py index e62d70847437c..a873d49af1e9c 100644 --- a/pandas/core/util/hashing.py +++ b/pandas/core/util/hashing.py @@ -140,7 +140,7 @@ def hash_tuples(vals, encoding='utf8', hash_key=None): if isinstance(vals, tuple): vals = [vals] is_tuple = True - elif not is_list_like(vals): + elif not is_list_like(vals, strict=False): raise TypeError("must be convertible to a list-of-tuples") from pandas import Categorical, MultiIndex diff --git a/pandas/core/window.py b/pandas/core/window.py index ea0ec79d655fb..650860195b76f 100644 --- a/pandas/core/window.py +++ b/pandas/core/window.py @@ -149,7 +149,8 @@ def _gotitem(self, key, ndim, subset=None): self = self._shallow_copy(subset) self._reset_cache() if subset.ndim == 2: - if is_scalar(key) and key in subset or is_list_like(key): + if (is_scalar(key) and key in subset + or is_list_like(key, strict=False)): self._selection = key return self diff --git a/pandas/io/excel.py b/pandas/io/excel.py index 00b4c704c681b..a538b567fbdc4 100644 --- a/pandas/io/excel.py +++ b/pandas/io/excel.py @@ -639,13 +639,13 @@ def _parse_cell(cell_contents, cell_typ): output[asheetname] = DataFrame() continue - if is_list_like(header) and len(header) == 1: + if is_list_like(header, strict=False) and len(header) == 1: header = header[0] # forward fill and pull out names for MultiIndex column header_names = None if header is not None: - if is_list_like(header): + if is_list_like(header, strict=False): header_names = [] control_row = [True] * len(data[0]) for row in header: @@ -660,9 +660,9 @@ def _parse_cell(cell_contents, cell_typ): else: data[header] = _trim_excel_header(data[header]) - if is_list_like(index_col): + if is_list_like(index_col, strict=False): # forward fill values for MultiIndex index - if not is_list_like(header): + if not is_list_like(header, strict=False): offset = 1 + header else: offset = 1 + max(header) @@ -675,7 +675,8 @@ def _parse_cell(cell_contents, cell_typ): else: last = data[row][col] - has_index_names = is_list_like(header) and len(header) > 1 + has_index_names = (is_list_like(header, strict=False) + and len(header) > 1) # GH 12292 : error when read one empty column from excel file try: @@ -794,7 +795,7 @@ def _pop_header_name(row, index_col): return none_fill(row[0]), row[1:] else: # pop out header name and fill w/ blank - i = index_col if not is_list_like(index_col) else max(index_col) + i = index_col if not is_list_like(index_col, strict=False) else max(index_col) return none_fill(row[i]), row[:i] + [''] + row[i + 1:] diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index db86409adc2b0..a74559a6966a1 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -517,7 +517,7 @@ def _to_str_columns(self): str_index = self._get_formatted_index(frame) - if not is_list_like(self.header) and not self.header: + if not is_list_like(self.header, strict=False) and not self.header: stringified = [] for i, c in enumerate(frame): fmt_values = self._format_col(i) @@ -526,7 +526,7 @@ def _to_str_columns(self): adj=self.adj) stringified.append(fmt_values) else: - if is_list_like(self.header): + if is_list_like(self.header, strict=False): if len(self.header) != len(self.columns): raise ValueError(('Writing {ncols} cols but got {nalias} ' 'aliases' diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index f4bb53ba4f218..7d9bcbb456fa8 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -1104,7 +1104,7 @@ def bar(self, subset=None, axis=0, color='#d65f5f', width=100, if align not in ('left', 'zero', 'mid'): raise ValueError("`align` must be one of {'left', 'zero',' mid'}") - if not (is_list_like(color)): + if not (is_list_like(color, strict=False)): color = [color, color] elif len(color) == 1: color = [color[0], color[0]] diff --git a/pandas/io/html.py b/pandas/io/html.py index 04534ff591a2c..ec29957b53913 100644 --- a/pandas/io/html.py +++ b/pandas/io/html.py @@ -105,7 +105,8 @@ def _get_skiprows(skiprows): """ if isinstance(skiprows, slice): return lrange(skiprows.start or 0, skiprows.stop, skiprows.step or 1) - elif isinstance(skiprows, numbers.Integral) or is_list_like(skiprows): + elif (isinstance(skiprows, numbers.Integral) + or is_list_like(skiprows, strict=False)): return skiprows elif skiprows is None: return 0 diff --git a/pandas/io/parsers.py b/pandas/io/parsers.py index a4f1155117b12..b3a2eee64b5dd 100755 --- a/pandas/io/parsers.py +++ b/pandas/io/parsers.py @@ -1225,7 +1225,7 @@ def _validate_usecols_arg(usecols): if callable(usecols): return usecols, None # GH20529, ensure is iterable container but not string. - elif not is_list_like(usecols): + elif not is_list_like(usecols, strict=False): raise ValueError(msg) else: usecols_dtype = lib.infer_dtype(usecols) @@ -3157,7 +3157,7 @@ def _clean_na_values(na_values, keep_default_na=True): # where we append the default NaN values, provided # that `keep_default_na=True`. for k, v in compat.iteritems(old_na_values): - if not is_list_like(v): + if not is_list_like(v, strict=False): v = [v] if keep_default_na: @@ -3166,7 +3166,7 @@ def _clean_na_values(na_values, keep_default_na=True): na_values[k] = v na_fvalues = {k: _floatify_na_values(v) for k, v in na_values.items()} else: - if not is_list_like(na_values): + if not is_list_like(na_values, strict=False): na_values = [na_values] na_values = _stringify_na_values(na_values) if keep_default_na: diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index ff37036533b4f..3a03f020e9fe5 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -4758,7 +4758,7 @@ def __init__(self, table, where=None, start=None, stop=None, **kwargs): self.terms = None self.coordinates = None - if is_list_like(where): + if is_list_like(where, strict=False): # see if we have a passed coordinate like try: diff --git a/pandas/io/sql.py b/pandas/io/sql.py index a582d32741ae9..54d2de1c90abe 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -748,7 +748,7 @@ def _create_table_setup(self): for name, typ, is_index in column_names_and_types] if self.keys is not None: - if not is_list_like(self.keys): + if not is_list_like(self.keys, strict=False): keys = [self.keys] else: keys = self.keys @@ -1289,7 +1289,7 @@ def _create_table_setup(self): for cname, ctype, _ in column_names_and_types] if self.keys is not None and len(self.keys): - if not is_list_like(self.keys): + if not is_list_like(self.keys, strict=False): keys = [self.keys] else: keys = self.keys diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index eef8646e4d6d2..68c852d50c596 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -268,7 +268,7 @@ def check_operands(left, right, cmp_op): def check_simple_cmp_op(self, lhs, cmp1, rhs): ex = 'lhs {0} rhs'.format(cmp1) - if cmp1 in ('in', 'not in') and not is_list_like(rhs): + if cmp1 in ('in', 'not in') and not is_list_like(rhs, strict=False): pytest.raises(TypeError, pd.eval, ex, engine=self.engine, parser=self.parser, local_dict={'lhs': lhs, 'rhs': rhs}) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 76cd6aabb93ae..7b866ff357ad9 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -67,17 +67,24 @@ def __getitem__(self): "ll", [ [], [1], (1, ), (1, 2), {'a': 1}, - {1, 'a'}, Series([1]), + Series([1]), Series([]), Series(['a']).str, - np.array([2])]) + np.array([2]) + ]) def test_is_list_like_passes(ll): - assert inference.is_list_like(ll) + assert inference.is_list_like(ll, strict=False) + + +def test_is_list_like_strict(): + ll = {1, 'a'} + assert inference.is_list_like(ll, strict=False) + assert not inference.is_list_like(ll, strict=True) @pytest.mark.parametrize( "ll", [1, '2', object(), str, np.array(2)]) def test_is_list_like_fails(ll): - assert not inference.is_list_like(ll) + assert not inference.is_list_like(ll, strict=False) def test_is_array_like(): @@ -106,7 +113,7 @@ class DtypeList(list): ]) def test_is_nested_list_like_passes(inner, outer): result = outer([inner for _ in range(5)]) - assert inference.is_list_like(result) + assert inference.is_list_like(result, strict=False) @pytest.mark.parametrize('obj', [ diff --git a/pandas/tests/extension/decimal/array.py b/pandas/tests/extension/decimal/array.py index a1ee3a4fefef2..31a320b272ec0 100644 --- a/pandas/tests/extension/decimal/array.py +++ b/pandas/tests/extension/decimal/array.py @@ -104,7 +104,7 @@ def astype(self, dtype, copy=True): return super(DecimalArray, self).astype(dtype, copy) def __setitem__(self, key, value): - if pd.api.types.is_list_like(value): + if pd.api.types.is_list_like(value, strict=False): value = [decimal.Decimal(v) for v in value] else: value = decimal.Decimal(value) diff --git a/pandas/tests/plotting/common.py b/pandas/tests/plotting/common.py index 5c88926828fa6..b48f65e7a89d3 100644 --- a/pandas/tests/plotting/common.py +++ b/pandas/tests/plotting/common.py @@ -163,8 +163,8 @@ def _check_visible(self, collections, visible=True): expected visibility """ from matplotlib.collections import Collection - if not isinstance(collections, - Collection) and not is_list_like(collections): + if (not isinstance(collections, Collection) + and not is_list_like(collections, strict=False)): collections = [collections] for patch in collections: @@ -251,7 +251,7 @@ def _check_text_labels(self, texts, expected): expected : str or list-like which has the same length as texts expected text label, or its list """ - if not is_list_like(texts): + if not is_list_like(texts, strict=False): assert texts.get_text() == expected else: labels = [t.get_text() for t in texts] diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index cd297c356d60e..acd3070c9077d 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -2644,7 +2644,7 @@ def _check_errorbar_color(containers, expected, has_err='has_xerr'): errs = [c.lines for c in ax.containers if getattr(c, has_err, False)][0] for el in errs: - if is_list_like(el): + if is_list_like(el, strict=False): lines.extend(el) else: lines.append(el) From 544b7ec510be34ad29fe0d204ff708b80a09e751 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Tue, 9 Oct 2018 23:38:13 +0200 Subject: [PATCH 02/17] Tiny fix --- pandas/core/dtypes/inference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 2dd1e3f217ecf..50e830229b1e6 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -289,7 +289,7 @@ def is_list_like(obj, strict=None): # only raise warning if necessary warnings.warn('is_list_like will in the future return False for sets. ' 'To keep the previous behavior, pass `strict=False`. To ' - 'adapt the future behavior and silence this warning, ' + 'adopt the future behavior and silence this warning, ' 'pass `strict=True`', FutureWarning) strict = False if strict is None else strict From ae9a45bf69cd234586ca8b18836ba98891408bc6 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 10 Oct 2018 01:19:42 +0200 Subject: [PATCH 03/17] Lint --- pandas/io/excel.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/io/excel.py b/pandas/io/excel.py index a538b567fbdc4..a864bd1da89f9 100644 --- a/pandas/io/excel.py +++ b/pandas/io/excel.py @@ -795,7 +795,8 @@ def _pop_header_name(row, index_col): return none_fill(row[0]), row[1:] else: # pop out header name and fill w/ blank - i = index_col if not is_list_like(index_col, strict=False) else max(index_col) + i = (index_col if not is_list_like(index_col, strict=False) + else max(index_col)) return none_fill(row[i]), row[:i] + [''] + row[i + 1:] From 15e3265524fa8a45331844d1dff9a2b6e989943b Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 10 Oct 2018 17:57:03 +0200 Subject: [PATCH 04/17] Review (jreback & jschendel) --- doc/source/whatsnew/v0.24.0.txt | 1 - pandas/core/algorithms.py | 6 +-- pandas/core/apply.py | 2 +- pandas/core/arrays/base.py | 3 +- pandas/core/arrays/categorical.py | 10 ++--- pandas/core/arrays/datetimelike.py | 2 +- pandas/core/arrays/integer.py | 4 +- pandas/core/arrays/timedeltas.py | 2 +- pandas/core/base.py | 3 +- pandas/core/computation/ops.py | 4 +- pandas/core/computation/pytables.py | 5 +-- pandas/core/dtypes/cast.py | 6 +-- pandas/core/dtypes/common.py | 4 +- pandas/core/dtypes/inference.py | 60 +++++++++++++++---------- pandas/core/dtypes/missing.py | 2 +- pandas/core/frame.py | 19 ++++---- pandas/core/generic.py | 38 +++++++--------- pandas/core/groupby/base.py | 3 +- pandas/core/groupby/grouper.py | 4 +- pandas/core/indexes/accessors.py | 4 +- pandas/core/indexes/base.py | 21 +++++---- pandas/core/indexes/category.py | 2 +- pandas/core/indexes/datetimelike.py | 2 +- pandas/core/indexes/datetimes.py | 2 +- pandas/core/indexes/interval.py | 4 +- pandas/core/indexes/multi.py | 36 +++++++-------- pandas/core/indexes/timedeltas.py | 5 +-- pandas/core/indexing.py | 16 +++---- pandas/core/internals/blocks.py | 23 +++++----- pandas/core/ops.py | 5 +-- pandas/core/panel.py | 4 +- pandas/core/reshape/melt.py | 8 ++-- pandas/core/reshape/merge.py | 4 +- pandas/core/reshape/pivot.py | 5 +-- pandas/core/reshape/reshape.py | 2 +- pandas/core/reshape/util.py | 4 +- pandas/core/series.py | 23 +++++----- pandas/core/sorting.py | 4 +- pandas/core/sparse/array.py | 2 +- pandas/core/strings.py | 10 ++--- pandas/core/tools/datetimes.py | 6 +-- pandas/core/tools/timedeltas.py | 2 +- pandas/core/util/hashing.py | 2 +- pandas/core/window.py | 3 +- pandas/io/excel.py | 14 +++--- pandas/io/formats/format.py | 4 +- pandas/io/formats/style.py | 2 +- pandas/io/html.py | 3 +- pandas/io/parsers.py | 6 +-- pandas/io/pytables.py | 2 +- pandas/io/sql.py | 4 +- pandas/tests/computation/test_eval.py | 2 +- pandas/tests/dtypes/test_inference.py | 47 ++++++++++++------- pandas/tests/extension/decimal/array.py | 2 +- pandas/tests/plotting/common.py | 6 +-- pandas/tests/plotting/test_frame.py | 2 +- 56 files changed, 237 insertions(+), 234 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index d93562ce4c0b9..40dd48880e0eb 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -629,7 +629,6 @@ Other API Changes - :class:`Index` subtraction will attempt to operate element-wise instead of raising ``TypeError`` (:issue:`19369`) - :class:`pandas.io.formats.style.Styler` supports a ``number-format`` property when using :meth:`~pandas.io.formats.style.Styler.to_excel` (:issue:`22015`) - :meth:`DataFrame.corr` and :meth:`Series.corr` now raise a ``ValueError`` along with a helpful error message instead of a ``KeyError`` when supplied with an invalid method (:issue:`22298`) -- :meth:`dtypes.common.is_list_like` has gained a ``strict``-kwarg, which is ``False`` by default. If set to ``True``, sets are not considered list-like. (:issue:`22397`) - :meth:`shift` will now always return a copy, instead of the previous behaviour of returning self when shifting by 0 (:issue:`22397`) .. _whatsnew_0240.deprecations: diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index f5e8ebe210863..e91cc8ec1e996 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -396,11 +396,11 @@ def isin(comps, values): boolean array same length as comps """ - if not is_list_like(comps, strict=False): + if not is_list_like(comps): raise TypeError("only list-like objects are allowed to be passed" " to isin(), you passed a [{comps_type}]" .format(comps_type=type(comps).__name__)) - if not is_list_like(values, strict=False): + if not is_list_like(values): raise TypeError("only list-like objects are allowed to be passed" " to isin(), you passed a [{values_type}]" .format(values_type=type(values).__name__)) @@ -1178,7 +1178,7 @@ class SelectNFrame(SelectN): def __init__(self, obj, n, keep, columns): super(SelectNFrame, self).__init__(obj, n, keep) - if not is_list_like(columns, strict=False): + if not is_list_like(columns): columns = [columns] columns = list(columns) self.columns = columns diff --git a/pandas/core/apply.py b/pandas/core/apply.py index 2b3fe0ef891f5..40cd952a62138 100644 --- a/pandas/core/apply.py +++ b/pandas/core/apply.py @@ -110,7 +110,7 @@ def get_result(self): """ compute the results """ # dispatch to agg - if is_list_like(self.f, strict=False) or is_dict_like(self.f): + if is_list_like(self.f) or is_dict_like(self.f): return self.obj.aggregate(self.f, axis=self.axis, *self.args, **self.kwds) diff --git a/pandas/core/arrays/base.py b/pandas/core/arrays/base.py index 53f72624e8d87..efe587c6aaaad 100644 --- a/pandas/core/arrays/base.py +++ b/pandas/core/arrays/base.py @@ -769,8 +769,7 @@ def _create_method(cls, op, coerce_to_dtype=True): def _binop(self, other): def convert_values(param): - if (isinstance(param, ExtensionArray) - or is_list_like(param, strict=False)): + if isinstance(param, ExtensionArray) or is_list_like(param): ovalues = param else: # Assume its an object ovalues = [param] * len(self) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index 3d6034ba1c541..216bccf7d6309 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -1017,7 +1017,7 @@ def add_categories(self, new_categories, inplace=False): set_categories """ inplace = validate_bool_kwarg(inplace, 'inplace') - if not is_list_like(new_categories, strict=False): + if not is_list_like(new_categories): new_categories = [new_categories] already_included = set(new_categories) & set(self.dtype.categories) if len(already_included) != 0: @@ -1065,7 +1065,7 @@ def remove_categories(self, removals, inplace=False): set_categories """ inplace = validate_bool_kwarg(inplace, 'inplace') - if not is_list_like(removals, strict=False): + if not is_list_like(removals): removals = [removals] removal_set = set(list(removals)) @@ -1981,7 +1981,7 @@ def __setitem__(self, key, value): raise ValueError("Cannot set a Categorical with another, " "without identical categories") - rvalue = value if is_list_like(value, strict=False) else [value] + rvalue = value if is_list_like(value) else [value] from pandas import Index to_add = Index(rvalue).difference(self.categories) @@ -2350,7 +2350,7 @@ def isin(self, values): array([ True, False, True, False, True, False]) """ from pandas.core.series import _sanitize_array - if not is_list_like(values, strict=False): + if not is_list_like(values): raise TypeError("only list-like objects are allowed to be passed" " to isin(), you passed a [{values_type}]" .format(values_type=type(values).__name__)) @@ -2523,7 +2523,7 @@ def _factorize_from_iterable(values): """ from pandas.core.indexes.category import CategoricalIndex - if not is_list_like(values, strict=False): + if not is_list_like(values): raise TypeError("Input must be list-like") if is_categorical(values): diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 0b0b24f410384..e4ace2bfe1509 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -751,7 +751,7 @@ def _evaluate_compare(self, other, op): if not isinstance(other, type(self)): # coerce to a similar object - if not is_list_like(other, strict=False): + if not is_list_like(other): # scalar other = [other] elif lib.is_scalar(lib.item_from_zerodim(other)): diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index a6f098f7a3c70..e58109a25e1a5 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -505,7 +505,7 @@ def cmp_method(self, other): mask = None if isinstance(other, IntegerArray): other, mask = other._data, other._mask - elif is_list_like(other, strict=False): + elif is_list_like(other): other = np.asarray(other) if other.ndim > 0 and len(self) != len(other): raise ValueError('Lengths must match to compare') @@ -568,7 +568,7 @@ def integer_arithmetic_method(self, other): elif getattr(other, 'ndim', 0) > 1: raise NotImplementedError( "can only perform ops with 1-d structures") - elif is_list_like(other, strict=False): + elif is_list_like(other): other = np.asarray(other) if not other.ndim: other = other.item() diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 25d6c7f4c10a6..df9e57cb5f0e1 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -76,7 +76,7 @@ def wrapper(self, other): if isna(other): result.fill(nat_result) - elif not is_list_like(other, strict=False): + elif not is_list_like(other): raise TypeError(msg.format(cls=type(self).__name__, typ=type(other).__name__)) else: diff --git a/pandas/core/base.py b/pandas/core/base.py index dc493cc71c3fd..00c049497c0d8 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -543,8 +543,7 @@ def is_any_frame(): name=getattr(self, 'name', None)) return result, True - elif (is_list_like(arg, strict=False) - and arg not in compat.string_types): + elif is_list_like(arg) and arg not in compat.string_types: # we require a list, but not an 'str' return self._aggregate_multiple_funcs(arg, _level=_level, diff --git a/pandas/core/computation/ops.py b/pandas/core/computation/ops.py index 4c16d59d7c44d..ca0c4db4947c4 100644 --- a/pandas/core/computation/ops.py +++ b/pandas/core/computation/ops.py @@ -234,7 +234,7 @@ def _in(x, y): try: return x.isin(y) except AttributeError: - if is_list_like(x, strict=False): + if is_list_like(x): try: return y.isin(x) except AttributeError: @@ -249,7 +249,7 @@ def _not_in(x, y): try: return ~x.isin(y) except AttributeError: - if is_list_like(x, strict=False): + if is_list_like(x): try: return ~y.isin(x) except AttributeError: diff --git a/pandas/core/computation/pytables.py b/pandas/core/computation/pytables.py index a7ae037b24f38..e08df3e340138 100644 --- a/pandas/core/computation/pytables.py +++ b/pandas/core/computation/pytables.py @@ -127,7 +127,7 @@ def pr(left, right): def conform(self, rhs): """ inplace conform rhs """ - if not is_list_like(rhs, strict=False): + if not is_list_like(rhs): rhs = [rhs] if isinstance(rhs, np.ndarray): rhs = rhs.ravel() @@ -472,8 +472,7 @@ def _validate_where(w): TypeError : An invalid data type was passed in for w (e.g. dict). """ - if not (isinstance(w, (Expr, string_types)) - or is_list_like(w, strict=False)): + if not (isinstance(w, (Expr, string_types)) or is_list_like(w)): raise TypeError("where must be passed as a string, Expr, " "or list-like of Exprs") diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index 5cde4c4fdc69a..a95a45d5f9ae4 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -472,7 +472,7 @@ def infer_dtype_from_array(arr, pandas_dtype=False): if isinstance(arr, np.ndarray): return arr.dtype, arr - if not is_list_like(arr, strict=False): + if not is_list_like(arr): arr = [arr] if pandas_dtype and is_extension_type(arr): @@ -518,7 +518,7 @@ def maybe_infer_dtype_type(element): tipo = None if hasattr(element, 'dtype'): tipo = element.dtype - elif is_list_like(element, strict=False): + elif is_list_like(element): element = np.asarray(element) tipo = element.dtype return tipo @@ -914,7 +914,7 @@ def maybe_infer_to_datetimelike(value, convert_dates=False): v = value - if not is_list_like(v, strict=False): + if not is_list_like(v): v = [v] v = np.array(v, copy=False) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index 227a0b457bcdd..a9fc9d13d4ab3 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -327,8 +327,8 @@ def is_offsetlike(arr_or_obj): """ if isinstance(arr_or_obj, ABCDateOffset): return True - elif (is_list_like(arr_or_obj, strict=False) and len(arr_or_obj) - and is_object_dtype(arr_or_obj)): + elif (is_list_like(arr_or_obj) and len(arr_or_obj) and + is_object_dtype(arr_or_obj)): return all(isinstance(x, ABCDateOffset) for x in arr_or_obj) return False diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 50e830229b1e6..9df7a78c1c3b2 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -4,10 +4,10 @@ import numpy as np from numbers import Number from pandas import compat -from pandas.compat import (PY2, string_types, text_type, +from pandas.compat import (PY2, PY36, string_types, text_type, string_and_binary_types, re_type) from pandas._libs import lib -import warnings +from collections import abc, OrderedDict is_bool = lib.is_bool @@ -248,7 +248,7 @@ def is_re_compilable(obj): return True -def is_list_like(obj, strict=None): +def is_list_like(obj): """ Check if the object is list-like. @@ -260,8 +260,6 @@ def is_list_like(obj, strict=None): Parameters ---------- obj : The object to check. - strict : boolean, default None - Whether `set` should be counted as list-like Returns ------- @@ -285,21 +283,38 @@ def is_list_like(obj, strict=None): >>> is_list_like(np.array(2))) False """ - if strict is None and isinstance(obj, set): - # only raise warning if necessary - warnings.warn('is_list_like will in the future return False for sets. ' - 'To keep the previous behavior, pass `strict=False`. To ' - 'adopt the future behavior and silence this warning, ' - 'pass `strict=True`', FutureWarning) - strict = False if strict is None else strict - - list_like = (isinstance(obj, compat.Iterable) - # we do not count strings/unicode/bytes as set-like - and not isinstance(obj, string_and_binary_types) - # exclude zero-dimensional numpy arrays, effectively scalars - and not (isinstance(obj, np.ndarray) and obj.ndim == 0)) - return list_like and (not strict or not isinstance(obj, set)) + return (isinstance(obj, compat.Iterable) and + # we do not count strings/unicode/bytes as list-like + not isinstance(obj, string_and_binary_types) and + # exclude zero-dimensional numpy arrays, effectively scalars + not (isinstance(obj, np.ndarray) and obj.ndim == 0)) + + +def is_ordered_list_like(obj): + """ + Check if the object is list-like and has a defined order + + Works like :meth:`is_list_like` but excludes sets (as well as unordered + `dict` before Python 3.6) + + Note that iterators can not be inspected for order - this check will return + True but it is up to the user to make sure that their iterators are + generated in an ordered way. + + Parameters + ---------- + obj : The object to check. + + Returns + ------- + is_ordered_list_like : bool + Whether `obj` is an ordered list-like + """ + list_like = is_list_like(obj) + unordered_dict = not PY36 and (isinstance(obj, dict) + and not isinstance(obj, OrderedDict)) + return list_like and not unordered_dict and not isinstance(obj, abc.Set) def is_array_like(obj): """ @@ -331,7 +346,7 @@ def is_array_like(obj): False """ - return is_list_like(obj, strict=False) and hasattr(obj, "dtype") + return is_list_like(obj) and hasattr(obj, "dtype") def is_nested_list_like(obj): @@ -374,9 +389,8 @@ def is_nested_list_like(obj): -------- is_list_like """ - return (is_list_like(obj, strict=False) and hasattr(obj, '__len__') - and len(obj) > 0 and all(is_list_like(item, strict=False) - for item in obj)) + return (is_list_like(obj) and hasattr(obj, '__len__') and + len(obj) > 0 and all(is_list_like(item) for item in obj)) def is_dict_like(obj): diff --git a/pandas/core/dtypes/missing.py b/pandas/core/dtypes/missing.py index b04bae77faf1a..66998aa6866f6 100644 --- a/pandas/core/dtypes/missing.py +++ b/pandas/core/dtypes/missing.py @@ -464,7 +464,7 @@ def _infer_fill_value(val): element to provide proper block construction """ - if not is_list_like(val, strict=False): + if not is_list_like(val): val = [val] val = np.array(val, copy=False) if is_datetimelike(val): diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 8f1082fb16f80..35c476f3539ee 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -421,8 +421,7 @@ def __init__(self, data=None, index=None, columns=None, dtype=None, if not isinstance(data, compat.Sequence): data = list(data) if len(data) > 0: - if (is_list_like(data[0], strict=False) - and getattr(data[0], 'ndim', 1) == 1): + if is_list_like(data[0]) and getattr(data[0], 'ndim', 1) == 1: if is_named_tuple(data[0]) and columns is None: columns = data[0]._fields arrays, columns = _to_arrays(data, columns, dtype=dtype) @@ -2791,8 +2790,7 @@ def __getitem__(self, key): # We are left with two options: a single key, and a collection of keys, # We interpret tuples as collections only for non-MultiIndex - is_single_key = (isinstance(key, tuple) - or not is_list_like(key, strict=False)) + is_single_key = isinstance(key, tuple) or not is_list_like(key) if is_single_key: if self.columns.nlevels > 1: @@ -3154,9 +3152,9 @@ def select_dtypes(self, include=None, exclude=None): 5 False 2.0 """ - if not is_list_like(include, strict=False): + if not is_list_like(include): include = (include,) if include is not None else () - if not is_list_like(exclude, strict=False): + if not is_list_like(exclude): exclude = (exclude,) if exclude is not None else () selection = tuple(map(frozenset, (include, exclude))) @@ -3281,7 +3279,7 @@ def _ensure_valid_index(self, value): passed value """ # GH5632, make sure that we are a Series convertible - if not len(self.index) and is_list_like(value, strict=False): + if not len(self.index) and is_list_like(value): try: value = Series(value) except (ValueError, NotImplementedError, TypeError): @@ -7663,7 +7661,7 @@ def isin(self, values): "a duplicate axis.") return self.eq(values.reindex_like(self)) else: - if not is_list_like(values, strict=False): + if not is_list_like(values): raise TypeError("only list-like or dict-like objects are " "allowed to be passed to DataFrame.isin(), " "you passed a " @@ -7733,7 +7731,7 @@ def extract_index(data): elif isinstance(v, dict): have_dicts = True indexes.append(list(v.keys())) - elif is_list_like(v, strict=False) and getattr(v, 'ndim', 1) == 1: + elif is_list_like(v) and getattr(v, 'ndim', 1) == 1: have_raw_arrays = True raw_lengths.append(len(v)) @@ -7776,8 +7774,7 @@ def convert(v): # this is equiv of np.asarray, but does object conversion # and platform dtype preservation try: - if (is_list_like(values[0], strict=False) - or hasattr(values[0], 'len')): + if is_list_like(values[0]) or hasattr(values[0], 'len'): values = np.array([convert(v) for v in values]) elif isinstance(values[0], np.ndarray) and values[0].ndim == 0: # GH#21861 diff --git a/pandas/core/generic.py b/pandas/core/generic.py index caf9ea028df2e..bb82c531b698e 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1179,8 +1179,8 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False): 2 3 6 """ inplace = validate_bool_kwarg(inplace, 'inplace') - non_mapper = is_scalar(mapper) or (is_list_like(mapper, strict=False) - and not is_dict_like(mapper)) + non_mapper = is_scalar(mapper) or (is_list_like(mapper) and not + is_dict_like(mapper)) if non_mapper: return self._set_axis_name(mapper, axis=axis, inplace=inplace) else: @@ -3263,7 +3263,7 @@ def xs(self, key, axis=0, level=None, drop_level=True): # if we encounter an array-like and we only have 1 dim # that means that their are list/ndarrays inside the Series! # so just return them (GH 6394) - if not is_list_like(new_values, strict=False) or self.ndim == 1: + if not is_list_like(new_values) or self.ndim == 1: return com.maybe_box_datetimelike(new_values) result = self._constructor_sliced( @@ -4674,8 +4674,7 @@ def __setattr__(self, name, value): else: object.__setattr__(self, name, value) except (AttributeError, TypeError): - if (isinstance(self, ABCDataFrame) - and is_list_like(value, strict=False)): + if isinstance(self, ABCDataFrame) and (is_list_like(value)): warnings.warn("Pandas doesn't allow columns to be " "created via a new attribute name - see " "https://pandas.pydata.org/pandas-docs/" @@ -5670,7 +5669,7 @@ def fillna(self, value=None, method=None, axis=None, inplace=False, if isinstance(value, (dict, ABCSeries)): from pandas import Series value = Series(value) - elif not is_list_like(value, strict=False): + elif not is_list_like(value): pass else: raise TypeError('"value" parameter must be a scalar, dict ' @@ -5695,7 +5694,7 @@ def fillna(self, value=None, method=None, axis=None, inplace=False, obj.fillna(v, limit=limit, inplace=True, downcast=downcast) return result if not inplace else None - elif not is_list_like(value, strict=False): + elif not is_list_like(value): new_data = self._data.fillna(value=value, limit=limit, inplace=inplace, downcast=downcast) @@ -6096,7 +6095,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, return None if inplace else res # {'A': NA} -> 0 - elif not is_list_like(value, strict=False): + elif not is_list_like(value): keys = [(k, src) for k, src in compat.iteritems(to_replace) if k in self] keys_len = len(keys) - 1 @@ -6112,9 +6111,8 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, raise TypeError('value argument must be scalar, dict, or ' 'Series') - elif is_list_like(to_replace, strict=False): - # [NA, ''] -> [0, 'missing'] - if is_list_like(value, strict=False): + elif is_list_like(to_replace): # [NA, ''] -> [0, 'missing'] + if is_list_like(value): if len(to_replace) != len(value): raise ValueError('Replacement lists must match ' 'in length. Expecting %d got %d ' % @@ -6130,9 +6128,8 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, value=value, inplace=inplace, regex=regex) elif to_replace is None: - if not (is_re_compilable(regex) - or is_list_like(regex, strict=False) - or is_dict_like(regex)): + if not (is_re_compilable(regex) or + is_list_like(regex) or is_dict_like(regex)): raise TypeError("'regex' must be a string or a compiled " "regular expression or a list or dict of " "strings or regular expressions, you " @@ -6153,7 +6150,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, inplace=inplace, regex=regex) - elif not is_list_like(value, strict=False): # NA -> 0 + elif not is_list_like(value): # NA -> 0 new_data = self._data.replace(to_replace=to_replace, value=value, inplace=inplace, regex=regex) @@ -6480,10 +6477,10 @@ def asof(self, where, subset=None): else: if subset is None: subset = self.columns - if not is_list_like(subset, strict=False): + if not is_list_like(subset): subset = [subset] - is_list = is_list_like(where, strict=False) + is_list = is_list_like(where) if not is_list: start = self.index[0] if isinstance(self.index, PeriodIndex): @@ -6715,8 +6712,7 @@ def _clip_with_one_bound(self, threshold, method, axis, inplace): # GH #15390 # In order for where method to work, the threshold must # be transformed to NDFrame from other array like structure. - if (not isinstance(threshold, ABCSeries) - and is_list_like(threshold, strict=False)): + if (not isinstance(threshold, ABCSeries)) and is_list_like(threshold): if isinstance(self, ABCSeries): threshold = pd.Series(threshold, index=self.index) else: @@ -6817,9 +6813,9 @@ def clip(self, lower=None, upper=None, axis=None, inplace=False, # so ignore # GH 19992 # numpy doesn't drop a list-like bound containing NaN - if not is_list_like(lower, strict=False) and np.any(pd.isnull(lower)): + if not is_list_like(lower) and np.any(pd.isnull(lower)): lower = None - if not is_list_like(upper, strict=False) and np.any(pd.isnull(upper)): + if not is_list_like(upper) and np.any(pd.isnull(upper)): upper = None # GH 2747 (arguments were reversed) diff --git a/pandas/core/groupby/base.py b/pandas/core/groupby/base.py index 228e2434ebb2c..ac84971de08d8 100644 --- a/pandas/core/groupby/base.py +++ b/pandas/core/groupby/base.py @@ -57,8 +57,7 @@ def _gotitem(self, key, ndim, subset=None): **kwargs) self._reset_cache() if subset.ndim == 2: - if (is_scalar(key) and key in subset - or is_list_like(key, strict=False)): + if is_scalar(key) and key in subset or is_list_like(key): self._selection = key return self diff --git a/pandas/core/groupby/grouper.py b/pandas/core/groupby/grouper.py index d23acea6f168e..e7144fb1d2932 100644 --- a/pandas/core/groupby/grouper.py +++ b/pandas/core/groupby/grouper.py @@ -440,7 +440,7 @@ def _get_grouper(obj, key=None, axis=0, level=None, sort=True, # on the outside of this condition. # (GH 17621) if isinstance(group_axis, MultiIndex): - if is_list_like(level, strict=False) and len(level) == 1: + if is_list_like(level) and len(level) == 1: level = level[0] if key is None and is_scalar(level): @@ -452,7 +452,7 @@ def _get_grouper(obj, key=None, axis=0, level=None, sort=True, # allow level to be a length-one list-like object # (e.g., level=[0]) # GH 13901 - if is_list_like(level, strict=False): + if is_list_like(level): nlevels = len(level) if nlevels == 1: level = level[0] diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 594770294a3cd..a1868980faed3 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -63,7 +63,7 @@ def _delegate_property_get(self, name): if isinstance(result, np.ndarray): if is_integer_dtype(result): result = result.astype('int64') - elif not is_list_like(result, strict=False): + elif not is_list_like(result): return result result = np.asarray(result) @@ -97,7 +97,7 @@ def _delegate_method(self, name, *args, **kwargs): method = getattr(values, name) result = method(*args, **kwargs) - if not is_list_like(result, strict=False): + if not is_list_like(result): return result result = Series(result, index=self.index, name=self.name) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 4fb92ca6fb266..51c84d6e28cb4 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -445,7 +445,7 @@ def __new__(cls, data=None, dtype=None, copy=False, name=None, elif data is None or is_scalar(data): cls._scalar_data_error(data) else: - if tupleize_cols and is_list_like(data, strict=False): + if tupleize_cols and is_list_like(data): # GH21470: convert iterable to list before determining if empty if is_iterator(data): data = list(data) @@ -1020,11 +1020,11 @@ def _validate_names(self, name=None, names=None, deep=False): elif names is None and name is None: return deepcopy(self.names) if deep else self.names elif names is not None: - if not is_list_like(names, strict=False): + if not is_list_like(names): raise TypeError("Must pass list-like as `names`.") return names else: - if not is_list_like(name, strict=False): + if not is_list_like(name): return [name] return name @@ -1222,7 +1222,7 @@ def _to_safe_for_reshape(self): return self def _assert_can_do_setop(self, other): - if not is_list_like(other, strict=False): + if not is_list_like(other): raise TypeError('Input must be Index or array-like') return True @@ -1267,7 +1267,7 @@ def _set_names(self, values, level=None): ------ TypeError if each name is not hashable. """ - if not is_list_like(values, strict=False): + if not is_list_like(values): raise ValueError('Names must be a list-like') if len(values) != 1: raise ValueError('Length of new names must be 1, got %d' % @@ -1337,18 +1337,17 @@ def set_names(self, names, level=None, inplace=False): if level is not None and not isinstance(self, MultiIndex): raise ValueError('Level must be None for non-MultiIndex') - if (level is not None and not is_list_like(level, strict=False) - and is_list_like(names, strict=False)): + if level is not None and not is_list_like(level) and is_list_like( + names): msg = "Names must be a string when a single level is provided." raise TypeError(msg) - if (not is_list_like(names, strict=False) - and level is None and self.nlevels > 1): + if not is_list_like(names) and level is None and self.nlevels > 1: raise TypeError("Must pass list-like as `names`.") - if not is_list_like(names, strict=False): + if not is_list_like(names): names = [names] - if level is not None and not is_list_like(level, strict=False): + if level is not None and not is_list_like(level): level = [level] if inplace: diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index 4d2aaaa3ddaad..45703c220a4be 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -244,7 +244,7 @@ def _is_dtype_compat(self, other): "when appending") else: values = other - if not is_list_like(values, strict=False): + if not is_list_like(values): values = [values] other = CategoricalIndex(self._create_categorical( other, dtype=self.dtype)) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index ed896e55270b6..1ec30ecbb3a3b 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -322,7 +322,7 @@ def __contains__(self, key): try: res = self.get_loc(key) return (is_scalar(res) or isinstance(res, slice) or - (is_list_like(res, strict=False) and len(res))) + (is_list_like(res) and len(res))) except (KeyError, TypeError, ValueError): return False diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 616f5336a7899..e40ceadc1a083 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1396,7 +1396,7 @@ def delete(self, loc): if loc in (0, -len(self), -1, len(self) - 1): freq = self.freq else: - if is_list_like(loc, strict=False): + if is_list_like(loc): loc = lib.maybe_indices_to_slice( ensure_int64(np.array(loc)), len(self)) if isinstance(loc, slice) and loc.step in (1, None): diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index b9760f3b8d683..25d4dd0cbcc81 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -560,7 +560,7 @@ def _maybe_convert_i8(self, key): Int64Index if converted list-like. """ original = key - if is_list_like(key, strict=False): + if is_list_like(key): key = ensure_index(key) if not self._needs_i8_conversion(key): @@ -734,7 +734,7 @@ def get_loc(self, key, method=None): def get_value(self, series, key): if com.is_bool_indexer(key): loc = key - elif is_list_like(key, strict=False): + elif is_list_like(key): loc = self.get_indexer(key) elif isinstance(key, slice): diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 367f25b21889d..3cccb65503378 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -389,16 +389,15 @@ def set_levels(self, levels, level=None, inplace=False, labels=[[0, 0, 1, 1], [0, 1, 0, 1]], names=[u'foo', u'bar']) """ - if level is not None and not is_list_like(level, strict=False): - if not is_list_like(levels, strict=False): + if level is not None and not is_list_like(level): + if not is_list_like(levels): raise TypeError("Levels must be list-like") - if is_list_like(levels[0], strict=False): + if is_list_like(levels[0]): raise TypeError("Levels must be list-like") level = [level] levels = [levels] - elif level is None or is_list_like(level, strict=False): - if (not is_list_like(levels, strict=False) - or not is_list_like(levels[0], strict=False)): + elif level is None or is_list_like(level): + if not is_list_like(levels) or not is_list_like(levels[0]): raise TypeError("Levels must be list of lists-like") if inplace: @@ -486,16 +485,15 @@ def set_labels(self, labels, level=None, inplace=False, labels=[[1, 0, 1, 0], [0, 0, 1, 1]], names=[u'foo', u'bar']) """ - if level is not None and not is_list_like(level, strict=False): - if not is_list_like(labels, strict=False): + if level is not None and not is_list_like(level): + if not is_list_like(labels): raise TypeError("Labels must be list-like") - if is_list_like(labels[0], strict=False): + if is_list_like(labels[0]): raise TypeError("Labels must be list-like") level = [level] labels = [labels] - elif level is None or is_list_like(level, strict=False): - if (not is_list_like(labels, strict=False) - or not is_list_like(labels[0], strict=False)): + elif level is None or is_list_like(level): + if not is_list_like(labels) or not is_list_like(labels[0]): raise TypeError("Labels must be list of lists-like") if inplace: @@ -687,7 +685,7 @@ def _set_names(self, names, level=None, validate=True): """ # GH 15110 # Don't allow a single string for names in a MultiIndex - if names is not None and not is_list_like(names, strict=False): + if names is not None and not is_list_like(names): raise ValueError('Names should be list-like for a MultiIndex') names = list(names) @@ -1180,7 +1178,7 @@ def to_frame(self, index=True, name=None): from pandas import DataFrame if name is not None: - if not is_list_like(name, strict=False): + if not is_list_like(name): raise TypeError("'name' must be a list / sequence " "of column names.") @@ -1297,7 +1295,7 @@ def from_arrays(cls, arrays, sortorder=None, names=None): MultiIndex.from_product : Make a MultiIndex from cartesian product of iterables """ - if not is_list_like(arrays, strict=False): + if not is_list_like(arrays): raise TypeError("Input must be a list / sequence of array-likes.") elif is_iterator(arrays): arrays = list(arrays) @@ -1346,7 +1344,7 @@ def from_tuples(cls, tuples, sortorder=None, names=None): MultiIndex.from_product : Make a MultiIndex from cartesian product of iterables """ - if not is_list_like(tuples, strict=False): + if not is_list_like(tuples): raise TypeError('Input must be a list / sequence of tuple-likes.') elif is_iterator(tuples): tuples = list(tuples) @@ -1405,7 +1403,7 @@ def from_product(cls, iterables, sortorder=None, names=None): from pandas.core.arrays.categorical import _factorize_from_iterables from pandas.core.reshape.util import cartesian_product - if not is_list_like(iterables, strict=False): + if not is_list_like(iterables): raise TypeError("Input must be a list / sequence of iterables.") elif is_iterator(iterables): iterables = list(iterables) @@ -1978,7 +1976,7 @@ def get_indexer(self, target, method=None, limit=None, tolerance=None): target = ensure_index(target) # empty indexer - if is_list_like(target, strict=False) and not len(target): + if is_list_like(target) and not len(target): return ensure_platform_int(np.array([])) if not isinstance(target, MultiIndex): @@ -2576,7 +2574,7 @@ def _update_indexer(idxr, indexer=indexer): indexer = _update_indexer(_convert_to_indexer(k), indexer=indexer) - elif is_list_like(k, strict=False): + elif is_list_like(k): # a collection of labels to include from this level (these # are or'd) indexers = None diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index a0a683106bef2..ee604f44b98e0 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -488,8 +488,7 @@ def get_loc(self, key, method=None, tolerance=None): ------- loc : int """ - if (is_list_like(key, strict=False) - or (isinstance(key, datetime) and key is not NaT)): + if is_list_like(key) or (isinstance(key, datetime) and key is not NaT): # GH#20464 datetime check here is to ensure we don't allow # datetime objects to be incorrectly treated as timedelta # objects; NaT is a special case because it plays a double role @@ -699,7 +698,7 @@ def delete(self, loc): if loc in (0, -len(self), -1, len(self) - 1): freq = self.freq else: - if is_list_like(loc, strict=False): + if is_list_like(loc): loc = lib.maybe_indices_to_slice( ensure_int64(np.array(loc)), len(self)) if isinstance(loc, slice) and loc.step in (1, None): diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 39cf27e388ce1..b3c913f21dd86 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -2528,7 +2528,7 @@ def check_setitem_lengths(indexer, value, values): """ # boolean with truth values == len of the value is ok too if isinstance(indexer, (np.ndarray, list)): - if is_list_like(value, strict=False) and len(indexer) != len(value): + if is_list_like(value) and len(indexer) != len(value): if not (isinstance(indexer, np.ndarray) and indexer.dtype == np.bool_ and len(indexer[indexer]) == len(value)): @@ -2537,7 +2537,7 @@ def check_setitem_lengths(indexer, value, values): # slice elif isinstance(indexer, slice): - if is_list_like(value, strict=False) and len(values): + if is_list_like(value) and len(values): if len(value) != length_of_indexer(indexer, values): raise ValueError("cannot set using a slice indexer with a " "different length than the value") @@ -2677,16 +2677,16 @@ def is_nested_tuple(tup, labels): for i, k in enumerate(tup): - if is_list_like(k, strict=False) or isinstance(k, slice): + if is_list_like(k) or isinstance(k, slice): return isinstance(labels, MultiIndex) return False def is_list_like_indexer(key): - # allow a list-like, but exclude NamedTuples which can be indexers - return (is_list_like(key, strict=False) - and not (isinstance(key, tuple) and type(key) is not tuple)) + # allow a list_like, but exclude NamedTuples which can be indexers + return is_list_like(key) and not (isinstance(key, tuple) and + type(key) is not tuple) def is_label_like(key): @@ -2734,9 +2734,9 @@ def _non_reducing_slice(slice_): def pred(part): # true when slice does *not* reduce - return isinstance(part, slice) or is_list_like(part, strict=False) + return isinstance(part, slice) or is_list_like(part) - if not is_list_like(slice_, strict=False): + if not is_list_like(slice_): if not isinstance(slice_, slice): # a 1-d slice, like df.loc[1] slice_ = [[slice_]] diff --git a/pandas/core/internals/blocks.py b/pandas/core/internals/blocks.py index b39544d8b9c09..93930fd844b95 100644 --- a/pandas/core/internals/blocks.py +++ b/pandas/core/internals/blocks.py @@ -910,7 +910,7 @@ def _is_scalar_indexer(indexer): def _is_empty_indexer(indexer): # return a boolean if we have an empty indexer - if is_list_like(indexer, strict=False) and not len(indexer): + if is_list_like(indexer) and not len(indexer): return True if arr_value.ndim == 1: if not isinstance(indexer, tuple): @@ -978,8 +978,7 @@ def putmask(self, mask, new, align=True, inplace=False, axis=0, mask = getattr(mask, 'values', mask) # if we are passed a scalar None, convert it here - if (not is_list_like(new, strict=False) - and isna(new) and not self.is_object): + if not is_list_like(new) and isna(new) and not self.is_object: new = self.fill_value if self._can_hold_element(new): @@ -1003,7 +1002,7 @@ def putmask(self, mask, new, align=True, inplace=False, axis=0, # # TODO: this prob needs some better checking # for 2D cases - if ((is_list_like(new, strict=False) and + if ((is_list_like(new) and np.any(mask[mask]) and getattr(new, 'ndim', 1) == 1)): @@ -1379,8 +1378,7 @@ def get_result(other): # avoid numpy warning of elementwise comparisons elif func.__name__ == 'eq': - if (is_list_like(other, strict=False) - and not isinstance(other, np.ndarray)): + if is_list_like(other) and not isinstance(other, np.ndarray): other = np.asarray(other) # if we can broadcast, then ok @@ -1436,8 +1434,7 @@ def handle_error(): # differentiate between an invalid ndarray-ndarray comparison # and an invalid type comparison - if (isinstance(values, np.ndarray) - and is_list_like(other, strict=False)): + if isinstance(values, np.ndarray) and is_list_like(other): raise ValueError( 'Invalid broadcasting comparison [{other!r}] with ' 'block values'.format(other=other)) @@ -1651,7 +1648,7 @@ def _nanpercentile(values, q, axis, **kw): from pandas import Float64Index is_empty = values.shape[axis] == 0 - if is_list_like(qs, strict=False): + if is_list_like(qs): ax = Float64Index(qs) if is_empty: @@ -2487,8 +2484,8 @@ def should_store(self, value): def replace(self, to_replace, value, inplace=False, filter=None, regex=False, convert=True, mgr=None): - to_rep_is_list = is_list_like(to_replace, strict=False) - value_is_list = is_list_like(value, strict=False) + to_rep_is_list = is_list_like(to_replace) + value_is_list = is_list_like(value) both_lists = to_rep_is_list and value_is_list either_list = to_rep_is_list or value_is_list @@ -3476,7 +3473,7 @@ def _putmask_smart(v, m, n): # that numpy does when numeric are mixed with strings # n should be the length of the mask or a scalar here - if not is_list_like(n, strict=False): + if not is_list_like(n): n = np.repeat(n, len(m)) elif isinstance(n, np.ndarray) and n.ndim == 0: # numpy scalar n = np.repeat(np.array(n, ndmin=1), len(m)) @@ -3508,7 +3505,7 @@ def _putmask_smart(v, m, n): is_integer_dtype(nn_at.dtype))): comp = (nn == nn_at) - if is_list_like(comp, strict=False) and comp.all(): + if is_list_like(comp) and comp.all(): nv = v.copy() nv[m] = nn_at return nv diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 2967b38abda53..20559bca9caed 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -1613,8 +1613,7 @@ def wrapper(self, other): else: # scalars, list, tuple, np.array is_other_int_dtype = is_integer_dtype(np.asarray(other)) - if (is_list_like(other, strict=False) - and not isinstance(other, np.ndarray)): + if is_list_like(other) and not isinstance(other, np.ndarray): # TODO: Can we do this before the is_integer_dtype check? # could the is_integer_dtype check be checking the wrong # thing? e.g. other = [[0, 1], [2, 3], [4, 5]]? @@ -1830,7 +1829,7 @@ def to_series(right): raise ValueError('Unable to coerce to Series/DataFrame, dim ' 'must be <= 2: {dim}'.format(dim=right.shape)) - elif (is_list_like(right, strict=False) and + elif (is_list_like(right) and not isinstance(right, (ABCSeries, ABCDataFrame))): # GH17901 right = to_series(right) diff --git a/pandas/core/panel.py b/pandas/core/panel.py index b8b672d8d6009..1e2d4000413bb 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -286,7 +286,7 @@ def __getitem__(self, key): if isinstance(self._info_axis, MultiIndex): return self._getitem_multilevel(key) - if not (is_list_like(key, strict=False) or isinstance(key, slice)): + if not (is_list_like(key) or isinstance(key, slice)): return super(Panel, self).__getitem__(key) return self.loc[key] @@ -884,7 +884,7 @@ def _ixs(self, i, axis=0): # if we have a multi-index and a single tuple, then its a reduction # (GH 7516) if not (isinstance(ax, MultiIndex) and isinstance(key, tuple)): - if is_list_like(key, strict=False): + if is_list_like(key): indexer = {self._get_axis_name(axis): key} return self.reindex(**indexer) diff --git a/pandas/core/reshape/melt.py b/pandas/core/reshape/melt.py index 6b1172a1fb17b..26221143c0cdf 100644 --- a/pandas/core/reshape/melt.py +++ b/pandas/core/reshape/melt.py @@ -26,7 +26,7 @@ def melt(frame, id_vars=None, value_vars=None, var_name=None, value_name='value', col_level=None): # TODO: what about the existing index? if id_vars is not None: - if not is_list_like(id_vars, strict=False): + if not is_list_like(id_vars): id_vars = [id_vars] elif (isinstance(frame.columns, ABCMultiIndex) and not isinstance(id_vars, list)): @@ -38,7 +38,7 @@ def melt(frame, id_vars=None, value_vars=None, var_name=None, id_vars = [] if value_vars is not None: - if not is_list_like(value_vars, strict=False): + if not is_list_like(value_vars): value_vars = [value_vars] elif (isinstance(frame.columns, ABCMultiIndex) and not isinstance(value_vars, list)): @@ -409,7 +409,7 @@ def melt_stub(df, stub, i, j, value_vars, sep): return newdf.set_index(i + [j]) - if not is_list_like(stubnames, strict=False): + if not is_list_like(stubnames): stubnames = [stubnames] else: stubnames = list(stubnames) @@ -417,7 +417,7 @@ def melt_stub(df, stub, i, j, value_vars, sep): if any(col in stubnames for col in df.columns): raise ValueError("stubname can't be identical to a column name") - if not is_list_like(i, strict=False): + if not is_list_like(i): i = [i] else: i = list(i) diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py index ddf51f3154ad8..d0c7b66978661 100644 --- a/pandas/core/reshape/merge.py +++ b/pandas/core/reshape/merge.py @@ -1296,9 +1296,9 @@ def _validate_specification(self): # add 'by' to our key-list so we can have it in the # output as a key if self.left_by is not None: - if not is_list_like(self.left_by, strict=False): + if not is_list_like(self.left_by): self.left_by = [self.left_by] - if not is_list_like(self.right_by, strict=False): + if not is_list_like(self.right_by): self.right_by = [self.right_by] if len(self.left_by) != len(self.right_by): diff --git a/pandas/core/reshape/pivot.py b/pandas/core/reshape/pivot.py index 7fe900e339648..b525dddeb1ba5 100644 --- a/pandas/core/reshape/pivot.py +++ b/pandas/core/reshape/pivot.py @@ -48,7 +48,7 @@ def pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', values_passed = values is not None if values_passed: - if is_list_like(values, strict=False): + if is_list_like(values): values_multi = True values = list(values) else: @@ -383,8 +383,7 @@ def pivot(data, index=None, columns=None, values=None): index = data[index] index = MultiIndex.from_arrays([index, data[columns]]) - if (is_list_like(values, strict=False) - and not isinstance(values, tuple)): + if is_list_like(values) and not isinstance(values, tuple): # Exclude tuple because it is seen as a single column name indexed = data._constructor(data[values].values, index=index, columns=values) diff --git a/pandas/core/reshape/reshape.py b/pandas/core/reshape/reshape.py index e306f4626a7ed..50f6e310705d7 100644 --- a/pandas/core/reshape/reshape.py +++ b/pandas/core/reshape/reshape.py @@ -743,7 +743,7 @@ def check_len(item, name): len_msg = ("Length of '{name}' ({len_item}) did not match the " "length of the columns being encoded ({len_enc}).") - if is_list_like(item, strict=False): + if is_list_like(item): if not len(item) == data_to_encode.shape[1]: len_msg = \ len_msg.format(name=name, len_item=len(item), diff --git a/pandas/core/reshape/util.py b/pandas/core/reshape/util.py index af99172b2c65a..1c2033d90cd8a 100644 --- a/pandas/core/reshape/util.py +++ b/pandas/core/reshape/util.py @@ -32,10 +32,10 @@ def cartesian_product(X): pandas.compat.product : An alias for itertools.product. """ msg = "Input must be a list-like of list-likes" - if not is_list_like(X, strict=False): + if not is_list_like(X): raise TypeError(msg) for x in X: - if not is_list_like(x, strict=False): + if not is_list_like(x): raise TypeError(msg) if len(X) == 0: diff --git a/pandas/core/series.py b/pandas/core/series.py index 4cd1745baf990..a613b22ea9046 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -249,10 +249,10 @@ def __init__(self, data=None, index=None, dtype=None, name=None, data = data.to_dense() if index is None: - if not is_list_like(data, strict=False): + if not is_list_like(data): data = [data] index = ibase.default_index(len(data)) - elif is_list_like(data, strict=False): + elif is_list_like(data): # a scalar numpy array is list-like but doesn't # have a proper length @@ -775,8 +775,8 @@ def __getitem__(self, key): result = self.index.get_value(self, key) if not is_scalar(result): - if (is_list_like(result, strict=False) - and not isinstance(result, Series)): + if is_list_like(result) and not isinstance(result, Series): + # we need to box if loc of the key isn't scalar here # otherwise have inline ndarray/lists try: @@ -1895,7 +1895,7 @@ def quantile(self, q=0.5, interpolation='linear'): result = self._data.quantile(qs=q, interpolation=interpolation) - if is_list_like(q, strict=False): + if is_list_like(q): return self._constructor(result, index=Float64Index(q), name=self.name) @@ -2561,7 +2561,7 @@ def _try_kind_sort(arr): argsorted = _try_kind_sort(arr[good]) - if is_list_like(ascending, strict=False): + if is_list_like(ascending): if len(ascending) != 1: raise ValueError('Length of ascending (%d) must be 1 ' 'for Series' % (len(ascending))) @@ -3494,8 +3494,8 @@ def rename(self, index=None, **kwargs): kwargs['inplace'] = validate_bool_kwarg(kwargs.get('inplace', False), 'inplace') - non_mapping = is_scalar(index) or (is_list_like(index, strict=False) - and not is_dict_like(index)) + non_mapping = is_scalar(index) or (is_list_like(index) and + not is_dict_like(index)) if non_mapping: return self._set_name(index, inplace=kwargs.get('inplace')) return super(Series, self).rename(index=index, **kwargs) @@ -4234,10 +4234,9 @@ def _try_cast(arr, take_fast_path): subarr = maybe_cast_to_datetime(arr, dtype) # Take care in creating object arrays (but iterators are not # supported): - if (is_object_dtype(dtype) - and (is_list_like(subarr, strict=False) - and not (is_iterator(subarr) - or isinstance(subarr, np.ndarray)))): + if is_object_dtype(dtype) and (is_list_like(subarr) and + not (is_iterator(subarr) or + isinstance(subarr, np.ndarray))): subarr = construct_1d_object_array_from_listlike(subarr) elif not is_extension_type(subarr): subarr = construct_1d_ndarray_preserving_na(subarr, dtype, diff --git a/pandas/core/sorting.py b/pandas/core/sorting.py index fe8bdb4d32c67..5aa9ea658482b 100644 --- a/pandas/core/sorting.py +++ b/pandas/core/sorting.py @@ -424,7 +424,7 @@ def safe_sort(values, labels=None, na_sentinel=-1, assume_unique=False): ValueError * If ``labels`` is not None and ``values`` contain duplicates. """ - if not is_list_like(values, strict=False): + if not is_list_like(values): raise TypeError("Only list-like objects are allowed to be passed to" "safe_sort as values") @@ -459,7 +459,7 @@ def sort_mixed(values): if labels is None: return ordered - if not is_list_like(labels, strict=False): + if not is_list_like(labels): raise TypeError("Only list-like objects or None are allowed to be" "passed to safe_sort as labels") labels = ensure_platform_int(np.asarray(labels)) diff --git a/pandas/core/sparse/array.py b/pandas/core/sparse/array.py index 47ffdd3539bd4..186a2490a5f2e 100644 --- a/pandas/core/sparse/array.py +++ b/pandas/core/sparse/array.py @@ -776,7 +776,7 @@ def _sanitize_values(arr): if isinstance(arr, np.ndarray): pass - elif is_list_like(arr, strict=False) and len(arr) > 0: + elif is_list_like(arr) and len(arr) > 0: arr = maybe_convert_platform(arr) else: diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 27bfc58bc5dc5..1f15e643302d4 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -1973,7 +1973,7 @@ def _wrap_result(self, result, use_codes=True, # not needed when inferred def cons_row(x): - if is_list_like(x, strict=False): + if is_list_like(x): return x else: return [x] @@ -2083,12 +2083,12 @@ def _get_series_list(self, others, ignore_index=False): elif isinstance(others, np.ndarray) and others.ndim == 2: others = DataFrame(others, index=idx) return ([others[x] for x in others], False) - elif is_list_like(others, strict=True): + elif is_ordered_list_like(others): others = list(others) # ensure iterators do not get read twice etc # in case of list-like `others`, all elements must be # either one-dimensional list-likes or scalars - if all(is_list_like(x, strict=True) for x in others): + if all(is_ordered_list_like(x) for x in others): los = [] join_warn = False depr_warn = False @@ -2116,7 +2116,7 @@ def _get_series_list(self, others, ignore_index=False): # nested list-likes are forbidden: # -> elements of nxt must not be list-like is_legal = ((no_deep and nxt.dtype == object) - or all(not is_list_like(x, strict=True) + or all(not is_ordered_list_like(x) for x in nxt)) # DataFrame is false positive of is_legal @@ -2135,7 +2135,7 @@ def _get_series_list(self, others, ignore_index=False): 'deprecated and will be removed in a future ' 'version.', FutureWarning, stacklevel=3) return (los, join_warn) - elif all(not is_list_like(x, strict=True) for x in others): + elif all(not is_ordered_list_like(x) for x in others): return ([Series(others, index=idx)], False) raise TypeError(err_msg) diff --git a/pandas/core/tools/datetimes.py b/pandas/core/tools/datetimes.py index ac851b8ee3cdf..eb8d2b0b6c809 100644 --- a/pandas/core/tools/datetimes.py +++ b/pandas/core/tools/datetimes.py @@ -375,7 +375,7 @@ def _adjust_to_origin(arg, origin, unit): offset = offset // tslibs.Timedelta(1, unit=unit) # scalars & ndarray-like can handle the addition - if is_list_like(arg, strict=False) and not isinstance( + if is_list_like(arg) and not isinstance( arg, (ABCSeries, ABCIndexClass, np.ndarray)): arg = np.asarray(arg) arg = arg + offset @@ -580,7 +580,7 @@ def to_datetime(arg, errors='raise', dayfirst=False, yearfirst=False, else: convert_listlike = partial(convert_listlike, name=arg.name) result = convert_listlike(arg, box, format) - elif is_list_like(arg, strict=False): + elif is_list_like(arg): cache_array = _maybe_cache(arg, format, cache, convert_listlike) if not cache_array.empty: result = _convert_and_box_cache(arg, cache_array, box, errors) @@ -868,7 +868,7 @@ def _convert_listlike(arg, format): return Series(values, index=arg.index, name=arg.name) elif isinstance(arg, ABCIndexClass): return _convert_listlike(arg, format) - elif is_list_like(arg, strict=False): + elif is_list_like(arg): return _convert_listlike(arg, format) return _convert_listlike(np.array([arg]), format)[0] diff --git a/pandas/core/tools/timedeltas.py b/pandas/core/tools/timedeltas.py index f1e64dc04af3b..4dc4fcb00d84d 100644 --- a/pandas/core/tools/timedeltas.py +++ b/pandas/core/tools/timedeltas.py @@ -88,7 +88,7 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'): elif isinstance(arg, np.ndarray) and arg.ndim == 0: # extract array scalar and process below arg = arg.item() - elif is_list_like(arg, strict=False) and getattr(arg, 'ndim', 1) == 1: + elif is_list_like(arg) and getattr(arg, 'ndim', 1) == 1: return _convert_listlike(arg, unit=unit, box=box, errors=errors) elif getattr(arg, 'ndim', 1) > 1: raise TypeError('arg must be a string, timedelta, list, tuple, ' diff --git a/pandas/core/util/hashing.py b/pandas/core/util/hashing.py index a873d49af1e9c..e62d70847437c 100644 --- a/pandas/core/util/hashing.py +++ b/pandas/core/util/hashing.py @@ -140,7 +140,7 @@ def hash_tuples(vals, encoding='utf8', hash_key=None): if isinstance(vals, tuple): vals = [vals] is_tuple = True - elif not is_list_like(vals, strict=False): + elif not is_list_like(vals): raise TypeError("must be convertible to a list-of-tuples") from pandas import Categorical, MultiIndex diff --git a/pandas/core/window.py b/pandas/core/window.py index 650860195b76f..ea0ec79d655fb 100644 --- a/pandas/core/window.py +++ b/pandas/core/window.py @@ -149,8 +149,7 @@ def _gotitem(self, key, ndim, subset=None): self = self._shallow_copy(subset) self._reset_cache() if subset.ndim == 2: - if (is_scalar(key) and key in subset - or is_list_like(key, strict=False)): + if is_scalar(key) and key in subset or is_list_like(key): self._selection = key return self diff --git a/pandas/io/excel.py b/pandas/io/excel.py index a864bd1da89f9..00b4c704c681b 100644 --- a/pandas/io/excel.py +++ b/pandas/io/excel.py @@ -639,13 +639,13 @@ def _parse_cell(cell_contents, cell_typ): output[asheetname] = DataFrame() continue - if is_list_like(header, strict=False) and len(header) == 1: + 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: - if is_list_like(header, strict=False): + if is_list_like(header): header_names = [] control_row = [True] * len(data[0]) for row in header: @@ -660,9 +660,9 @@ def _parse_cell(cell_contents, cell_typ): else: data[header] = _trim_excel_header(data[header]) - if is_list_like(index_col, strict=False): + if is_list_like(index_col): # forward fill values for MultiIndex index - if not is_list_like(header, strict=False): + if not is_list_like(header): offset = 1 + header else: offset = 1 + max(header) @@ -675,8 +675,7 @@ def _parse_cell(cell_contents, cell_typ): else: last = data[row][col] - has_index_names = (is_list_like(header, strict=False) - and len(header) > 1) + has_index_names = is_list_like(header) and len(header) > 1 # GH 12292 : error when read one empty column from excel file try: @@ -795,8 +794,7 @@ def _pop_header_name(row, index_col): return none_fill(row[0]), row[1:] else: # pop out header name and fill w/ blank - i = (index_col if not is_list_like(index_col, strict=False) - else max(index_col)) + i = index_col if not is_list_like(index_col) else max(index_col) return none_fill(row[i]), row[:i] + [''] + row[i + 1:] diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index a74559a6966a1..db86409adc2b0 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -517,7 +517,7 @@ def _to_str_columns(self): str_index = self._get_formatted_index(frame) - if not is_list_like(self.header, strict=False) and not self.header: + if not is_list_like(self.header) and not self.header: stringified = [] for i, c in enumerate(frame): fmt_values = self._format_col(i) @@ -526,7 +526,7 @@ def _to_str_columns(self): adj=self.adj) stringified.append(fmt_values) else: - if is_list_like(self.header, strict=False): + if is_list_like(self.header): if len(self.header) != len(self.columns): raise ValueError(('Writing {ncols} cols but got {nalias} ' 'aliases' diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 7d9bcbb456fa8..f4bb53ba4f218 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -1104,7 +1104,7 @@ def bar(self, subset=None, axis=0, color='#d65f5f', width=100, if align not in ('left', 'zero', 'mid'): raise ValueError("`align` must be one of {'left', 'zero',' mid'}") - if not (is_list_like(color, strict=False)): + if not (is_list_like(color)): color = [color, color] elif len(color) == 1: color = [color[0], color[0]] diff --git a/pandas/io/html.py b/pandas/io/html.py index ec29957b53913..04534ff591a2c 100644 --- a/pandas/io/html.py +++ b/pandas/io/html.py @@ -105,8 +105,7 @@ def _get_skiprows(skiprows): """ if isinstance(skiprows, slice): return lrange(skiprows.start or 0, skiprows.stop, skiprows.step or 1) - elif (isinstance(skiprows, numbers.Integral) - or is_list_like(skiprows, strict=False)): + elif isinstance(skiprows, numbers.Integral) or is_list_like(skiprows): return skiprows elif skiprows is None: return 0 diff --git a/pandas/io/parsers.py b/pandas/io/parsers.py index 7b5a5591f2d6d..2def3b81c9518 100755 --- a/pandas/io/parsers.py +++ b/pandas/io/parsers.py @@ -1225,7 +1225,7 @@ def _validate_usecols_arg(usecols): if callable(usecols): return usecols, None # GH20529, ensure is iterable container but not string. - elif not is_list_like(usecols, strict=False): + elif not is_list_like(usecols): raise ValueError(msg) else: usecols_dtype = lib.infer_dtype(usecols) @@ -3157,7 +3157,7 @@ def _clean_na_values(na_values, keep_default_na=True): # where we append the default NaN values, provided # that `keep_default_na=True`. for k, v in compat.iteritems(old_na_values): - if not is_list_like(v, strict=False): + if not is_list_like(v): v = [v] if keep_default_na: @@ -3166,7 +3166,7 @@ def _clean_na_values(na_values, keep_default_na=True): na_values[k] = v na_fvalues = {k: _floatify_na_values(v) for k, v in na_values.items()} else: - if not is_list_like(na_values, strict=False): + if not is_list_like(na_values): na_values = [na_values] na_values = _stringify_na_values(na_values) if keep_default_na: diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index 3a03f020e9fe5..ff37036533b4f 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -4758,7 +4758,7 @@ def __init__(self, table, where=None, start=None, stop=None, **kwargs): self.terms = None self.coordinates = None - if is_list_like(where, strict=False): + if is_list_like(where): # see if we have a passed coordinate like try: diff --git a/pandas/io/sql.py b/pandas/io/sql.py index a0d3413e6d858..882fa0092b2cf 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -748,7 +748,7 @@ def _create_table_setup(self): for name, typ, is_index in column_names_and_types] if self.keys is not None: - if not is_list_like(self.keys, strict=False): + if not is_list_like(self.keys): keys = [self.keys] else: keys = self.keys @@ -1289,7 +1289,7 @@ def _create_table_setup(self): for cname, ctype, _ in column_names_and_types] if self.keys is not None and len(self.keys): - if not is_list_like(self.keys, strict=False): + if not is_list_like(self.keys): keys = [self.keys] else: keys = self.keys diff --git a/pandas/tests/computation/test_eval.py b/pandas/tests/computation/test_eval.py index 68c852d50c596..eef8646e4d6d2 100644 --- a/pandas/tests/computation/test_eval.py +++ b/pandas/tests/computation/test_eval.py @@ -268,7 +268,7 @@ def check_operands(left, right, cmp_op): def check_simple_cmp_op(self, lhs, cmp1, rhs): ex = 'lhs {0} rhs'.format(cmp1) - if cmp1 in ('in', 'not in') and not is_list_like(rhs, strict=False): + 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}) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 7b866ff357ad9..e4ec5a61b5052 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -6,7 +6,7 @@ """ from warnings import catch_warnings, simplefilter -import collections +from collections import namedtuple, OrderedDict import re from datetime import datetime, date, timedelta, time from decimal import Decimal @@ -21,7 +21,7 @@ Panel, Period, Categorical, isna, Interval, DateOffset) from pandas import compat -from pandas.compat import u, PY2, StringIO, lrange +from pandas.compat import u, PY2, PY36, StringIO, lrange from pandas.core.dtypes import inference from pandas.core.dtypes.common import ( is_timedelta64_dtype, @@ -66,25 +66,40 @@ def __getitem__(self): @pytest.mark.parametrize( "ll", [ - [], [1], (1, ), (1, 2), {'a': 1}, - Series([1]), - Series([]), Series(['a']).str, - np.array([2]) + [], [1], tuple(), (1, ), (1, 2), {'a': 1}, {1, 'a'}, np.array([2]), + Series([1]), Series([]), Series(['a']).str, Index([]), Index([1]), + DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), + np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) ]) def test_is_list_like_passes(ll): - assert inference.is_list_like(ll, strict=False) + assert inference.is_list_like(ll) -def test_is_list_like_strict(): - ll = {1, 'a'} - assert inference.is_list_like(ll, strict=False) - assert not inference.is_list_like(ll, strict=True) +@pytest.mark.parametrize("ll", [1, '2', object(), str, np.array(2)]) +def test_is_list_like_fails(ll): + assert not inference.is_list_like(ll) @pytest.mark.parametrize( - "ll", [1, '2', object(), str, np.array(2)]) -def test_is_list_like_fails(ll): - assert not inference.is_list_like(ll, strict=False) + "ll", + [ + [], [1], tuple(), (1, ), (1, 2), np.array([2]), OrderedDict({'a': 1}), + Series([1]), Series([]), Series(['a']).str, Index([]), Index([1]), + DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), + np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) + ]) +def test_is_ordered_list_like_passes(ll): + assert inference.is_list_like(ll) + + +@pytest.mark.parametrize("ll", [1, '2', object(), str, np.array(2), + {1, 'a'}, frozenset({1, 'a'}), {1, 'a'}]) +def test_is_ordered_like_fails(ll): + # GH 23061 + if PY36 and isinstance(ll, dict): + assert inference.is_ordered_list_like(ll) + else: + assert not inference.is_ordered_list_like(ll) def test_is_array_like(): @@ -113,7 +128,7 @@ class DtypeList(list): ]) def test_is_nested_list_like_passes(inner, outer): result = outer([inner for _ in range(5)]) - assert inference.is_list_like(result, strict=False) + assert inference.is_list_like(result) @pytest.mark.parametrize('obj', [ @@ -179,7 +194,7 @@ class MockFile(object): @pytest.mark.parametrize( - "ll", [collections.namedtuple('Test', list('abc'))(1, 2, 3)]) + "ll", [namedtuple('Test', list('abc'))(1, 2, 3)]) def test_is_names_tuple_passes(ll): assert inference.is_named_tuple(ll) diff --git a/pandas/tests/extension/decimal/array.py b/pandas/tests/extension/decimal/array.py index 31a320b272ec0..a1ee3a4fefef2 100644 --- a/pandas/tests/extension/decimal/array.py +++ b/pandas/tests/extension/decimal/array.py @@ -104,7 +104,7 @@ def astype(self, dtype, copy=True): return super(DecimalArray, self).astype(dtype, copy) def __setitem__(self, key, value): - if pd.api.types.is_list_like(value, strict=False): + if pd.api.types.is_list_like(value): value = [decimal.Decimal(v) for v in value] else: value = decimal.Decimal(value) diff --git a/pandas/tests/plotting/common.py b/pandas/tests/plotting/common.py index b48f65e7a89d3..5c88926828fa6 100644 --- a/pandas/tests/plotting/common.py +++ b/pandas/tests/plotting/common.py @@ -163,8 +163,8 @@ def _check_visible(self, collections, visible=True): expected visibility """ from matplotlib.collections import Collection - if (not isinstance(collections, Collection) - and not is_list_like(collections, strict=False)): + if not isinstance(collections, + Collection) and not is_list_like(collections): collections = [collections] for patch in collections: @@ -251,7 +251,7 @@ def _check_text_labels(self, texts, expected): expected : str or list-like which has the same length as texts expected text label, or its list """ - if not is_list_like(texts, strict=False): + if not is_list_like(texts): assert texts.get_text() == expected else: labels = [t.get_text() for t in texts] diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index acd3070c9077d..cd297c356d60e 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -2644,7 +2644,7 @@ def _check_errorbar_color(containers, expected, has_err='has_xerr'): errs = [c.lines for c in ax.containers if getattr(c, has_err, False)][0] for el in errs: - if is_list_like(el, strict=False): + if is_list_like(el): lines.extend(el) else: lines.append(el) From 1941376b5e47e885b55810ee1fa85da4f4bcefd8 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 10 Oct 2018 23:36:01 +0200 Subject: [PATCH 05/17] abc-compat --- pandas/compat/__init__.py | 2 ++ pandas/core/dtypes/inference.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/compat/__init__.py b/pandas/compat/__init__.py index 1453725225e7d..5108e23c53b5a 100644 --- a/pandas/compat/__init__.py +++ b/pandas/compat/__init__.py @@ -141,6 +141,7 @@ def lfilter(*args, **kwargs): Mapping = collections.abc.Mapping Sequence = collections.abc.Sequence Sized = collections.abc.Sized + Set = collections.abc.Set else: # Python 2 @@ -201,6 +202,7 @@ def get_range_parameters(data): Mapping = collections.Mapping Sequence = collections.Sequence Sized = collections.Sized + Set = collections.Set if PY2: def iteritems(obj, **kw): diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 9df7a78c1c3b2..60771d964770d 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -5,9 +5,9 @@ from numbers import Number from pandas import compat from pandas.compat import (PY2, PY36, string_types, text_type, - string_and_binary_types, re_type) + string_and_binary_types, re_type, Set) from pandas._libs import lib -from collections import abc, OrderedDict +from collections import OrderedDict is_bool = lib.is_bool @@ -314,7 +314,7 @@ def is_ordered_list_like(obj): list_like = is_list_like(obj) unordered_dict = not PY36 and (isinstance(obj, dict) and not isinstance(obj, OrderedDict)) - return list_like and not unordered_dict and not isinstance(obj, abc.Set) + return list_like and not unordered_dict and not isinstance(obj, Set) def is_array_like(obj): """ From 787139717616a2e03b6f392d237805a542581d58 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 10 Oct 2018 23:42:44 +0200 Subject: [PATCH 06/17] Add import in pandas.core.dtypes.common --- pandas/core/dtypes/common.py | 8 ++++---- pandas/core/strings.py | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index a9fc9d13d4ab3..e7c2a1cc48c44 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -17,10 +17,10 @@ ABCSparseArray, ABCSparseSeries, ABCCategoricalIndex, ABCIndexClass, ABCDateOffset) from pandas.core.dtypes.inference import ( # noqa:F401 - is_bool, is_integer, is_hashable, is_iterator, is_float, - is_dict_like, is_scalar, is_string_like, is_list_like, is_number, - is_file_like, is_re, is_re_compilable, is_sequence, is_nested_list_like, - is_named_tuple, is_array_like, is_decimal, is_complex, is_interval) + is_bool, is_integer, is_float, is_number, is_decimal, is_complex, is_re, + is_re_compilable, is_dict_like, is_string_like, is_file_like, is_list_like, + is_ordered_list_like, is_nested_list_like, is_sequence, is_named_tuple, + is_array_like, is_hashable, is_iterator, is_scalar, is_interval) _POSSIBLY_CAST_DTYPES = {np.dtype(t).name diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 1f15e643302d4..6ddf9135b5f5e 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -10,6 +10,7 @@ is_object_dtype, is_string_like, is_list_like, + is_ordered_list_like, is_scalar, is_integer, is_re) From 8efee57c4503ba42cb3e0ae1f70e6b3958964deb Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Thu, 11 Oct 2018 00:22:45 +0200 Subject: [PATCH 07/17] Review (jorisvandenbossche) --- pandas/core/dtypes/common.py | 8 +++--- pandas/core/dtypes/inference.py | 37 ++++++--------------------- pandas/core/strings.py | 9 +++---- pandas/tests/dtypes/test_inference.py | 15 +++++------ 4 files changed, 22 insertions(+), 47 deletions(-) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index e7c2a1cc48c44..6bbfff9914a0d 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -17,10 +17,10 @@ ABCSparseArray, ABCSparseSeries, ABCCategoricalIndex, ABCIndexClass, ABCDateOffset) from pandas.core.dtypes.inference import ( # noqa:F401 - is_bool, is_integer, is_float, is_number, is_decimal, is_complex, is_re, - is_re_compilable, is_dict_like, is_string_like, is_file_like, is_list_like, - is_ordered_list_like, is_nested_list_like, is_sequence, is_named_tuple, - is_array_like, is_hashable, is_iterator, is_scalar, is_interval) + is_bool, is_integer, is_float, is_number, is_decimal, is_complex, + is_re, is_re_compilable, is_dict_like, is_string_like, is_file_like, + is_list_like, is_nested_list_like, is_sequence, is_named_tuple, + is_hashable, is_iterator, is_array_like, is_scalar, is_interval) _POSSIBLY_CAST_DTYPES = {np.dtype(t).name diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 60771d964770d..f087682fc50d0 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -248,7 +248,7 @@ def is_re_compilable(obj): return True -def is_list_like(obj): +def is_list_like(obj, strict=False): """ Check if the object is list-like. @@ -260,6 +260,8 @@ def is_list_like(obj): Parameters ---------- obj : The object to check. + strict : boolean, default False + If this parameter is True, sets will not be considered list-like Returns ------- @@ -284,37 +286,14 @@ def is_list_like(obj): False """ - return (isinstance(obj, compat.Iterable) and + return (isinstance(obj, compat.Iterable) # we do not count strings/unicode/bytes as list-like - not isinstance(obj, string_and_binary_types) and + and not isinstance(obj, string_and_binary_types) # exclude zero-dimensional numpy arrays, effectively scalars - not (isinstance(obj, np.ndarray) and obj.ndim == 0)) - - -def is_ordered_list_like(obj): - """ - Check if the object is list-like and has a defined order + and not (isinstance(obj, np.ndarray) and obj.ndim == 0) + # exclude sets if ordered_only + and not (strict and isinstance(obj, Set))) - Works like :meth:`is_list_like` but excludes sets (as well as unordered - `dict` before Python 3.6) - - Note that iterators can not be inspected for order - this check will return - True but it is up to the user to make sure that their iterators are - generated in an ordered way. - - Parameters - ---------- - obj : The object to check. - - Returns - ------- - is_ordered_list_like : bool - Whether `obj` is an ordered list-like - """ - list_like = is_list_like(obj) - unordered_dict = not PY36 and (isinstance(obj, dict) - and not isinstance(obj, OrderedDict)) - return list_like and not unordered_dict and not isinstance(obj, Set) def is_array_like(obj): """ diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 6ddf9135b5f5e..d3f64b0100e40 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -10,7 +10,6 @@ is_object_dtype, is_string_like, is_list_like, - is_ordered_list_like, is_scalar, is_integer, is_re) @@ -2084,12 +2083,12 @@ def _get_series_list(self, others, ignore_index=False): elif isinstance(others, np.ndarray) and others.ndim == 2: others = DataFrame(others, index=idx) return ([others[x] for x in others], False) - elif is_ordered_list_like(others): + elif is_list_like(others, strict=True): others = list(others) # ensure iterators do not get read twice etc # in case of list-like `others`, all elements must be # either one-dimensional list-likes or scalars - if all(is_ordered_list_like(x) for x in others): + if all(is_list_like(x, strict=True) for x in others): los = [] join_warn = False depr_warn = False @@ -2117,7 +2116,7 @@ def _get_series_list(self, others, ignore_index=False): # nested list-likes are forbidden: # -> elements of nxt must not be list-like is_legal = ((no_deep and nxt.dtype == object) - or all(not is_ordered_list_like(x) + or all(not is_list_like(x, strict=True) for x in nxt)) # DataFrame is false positive of is_legal @@ -2136,7 +2135,7 @@ def _get_series_list(self, others, ignore_index=False): 'deprecated and will be removed in a future ' 'version.', FutureWarning, stacklevel=3) return (los, join_warn) - elif all(not is_ordered_list_like(x) for x in others): + elif all(not is_list_like(x, strict=True) for x in others): return ([Series(others, index=idx)], False) raise TypeError(err_msg) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index e4ec5a61b5052..5aa2d2b27defe 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -83,23 +83,20 @@ def test_is_list_like_fails(ll): @pytest.mark.parametrize( "ll", [ - [], [1], tuple(), (1, ), (1, 2), np.array([2]), OrderedDict({'a': 1}), + [], [1], tuple(), (1, ), (1, 2), {'a': 1}, np.array([2]), Series([1]), Series([]), Series(['a']).str, Index([]), Index([1]), DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) ]) -def test_is_ordered_list_like_passes(ll): - assert inference.is_list_like(ll) +def test_is_list_like_strict_passes(ll): + assert inference.is_list_like(ll, strict=True) @pytest.mark.parametrize("ll", [1, '2', object(), str, np.array(2), - {1, 'a'}, frozenset({1, 'a'}), {1, 'a'}]) -def test_is_ordered_like_fails(ll): + {1, 'a'}, frozenset({1, 'a'})]) +def test_is_list_like_strict_fails(ll): # GH 23061 - if PY36 and isinstance(ll, dict): - assert inference.is_ordered_list_like(ll) - else: - assert not inference.is_ordered_list_like(ll) + assert not inference.is_list_like(ll, strict=True) def test_is_array_like(): From 0826f34db2b9617919ac0a3ca833524b462d17da Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Thu, 11 Oct 2018 00:39:52 +0200 Subject: [PATCH 08/17] Lint --- pandas/core/dtypes/inference.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index f087682fc50d0..12eff1130d8a3 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -4,10 +4,9 @@ import numpy as np from numbers import Number from pandas import compat -from pandas.compat import (PY2, PY36, string_types, text_type, +from pandas.compat import (PY2, string_types, text_type, string_and_binary_types, re_type, Set) from pandas._libs import lib -from collections import OrderedDict is_bool = lib.is_bool From 5686c7701347a6703fb7b98376648fd68b5b4524 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Thu, 11 Oct 2018 00:42:02 +0200 Subject: [PATCH 09/17] More lint --- pandas/tests/dtypes/test_inference.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 5aa2d2b27defe..66a403a69f731 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -6,7 +6,7 @@ """ from warnings import catch_warnings, simplefilter -from collections import namedtuple, OrderedDict +import collections import re from datetime import datetime, date, timedelta, time from decimal import Decimal @@ -21,7 +21,7 @@ Panel, Period, Categorical, isna, Interval, DateOffset) from pandas import compat -from pandas.compat import u, PY2, PY36, StringIO, lrange +from pandas.compat import u, PY2, StringIO, lrange from pandas.core.dtypes import inference from pandas.core.dtypes.common import ( is_timedelta64_dtype, @@ -191,7 +191,7 @@ class MockFile(object): @pytest.mark.parametrize( - "ll", [namedtuple('Test', list('abc'))(1, 2, 3)]) + "ll", [collections.namedtuple('Test', list('abc'))(1, 2, 3)]) def test_is_names_tuple_passes(ll): assert inference.is_named_tuple(ll) From 3796080a463f718d89ca13a5953afcdda1ae1c5e Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Mon, 15 Oct 2018 20:23:20 +0200 Subject: [PATCH 10/17] Back to is_ordered_list_like --- pandas/core/dtypes/common.py | 6 +++--- pandas/core/dtypes/inference.py | 29 +++++++++++++++++++++------ pandas/core/strings.py | 9 +++++---- pandas/tests/dtypes/test_inference.py | 8 ++++---- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index bf22025761426..5f8c7a807bc50 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -18,9 +18,9 @@ ABCSparseArray, ABCSparseSeries, ABCCategoricalIndex, ABCIndexClass, ABCDateOffset) from pandas.core.dtypes.inference import ( # noqa:F401 - is_bool, is_integer, is_float, is_number, is_decimal, is_complex, - is_re, is_re_compilable, is_dict_like, is_string_like, is_file_like, - is_list_like, is_nested_list_like, is_sequence, is_named_tuple, + is_bool, is_integer, is_float, is_number, is_decimal, is_complex, is_re, + is_re_compilable, is_dict_like, is_string_like, is_file_like, is_list_like, + is_ordered_list_like, is_nested_list_like, is_sequence, is_named_tuple, is_hashable, is_iterator, is_array_like, is_scalar, is_interval) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 12eff1130d8a3..c80eb6edba606 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -247,7 +247,7 @@ def is_re_compilable(obj): return True -def is_list_like(obj, strict=False): +def is_list_like(obj): """ Check if the object is list-like. @@ -259,8 +259,6 @@ def is_list_like(obj, strict=False): Parameters ---------- obj : The object to check. - strict : boolean, default False - If this parameter is True, sets will not be considered list-like Returns ------- @@ -289,9 +287,28 @@ def is_list_like(obj, strict=False): # we do not count strings/unicode/bytes as list-like and not isinstance(obj, string_and_binary_types) # exclude zero-dimensional numpy arrays, effectively scalars - and not (isinstance(obj, np.ndarray) and obj.ndim == 0) - # exclude sets if ordered_only - and not (strict and isinstance(obj, Set))) + and not (isinstance(obj, np.ndarray) and obj.ndim == 0)) + + +def is_ordered_list_like(obj): + """ + Check if the object is list-like and has a defined order + + Works like :meth:`is_list_like` but excludes sets. Note that iterators can + not be inspected for order - this check will return True but it is up to + the user to make sure that their iterators are generated in an ordered way. + + Parameters + ---------- + obj : The object to check. + + Returns + ------- + is_ordered_list_like : bool + Whether `obj` is an ordered list-like + """ + list_like = is_list_like(obj) + return list_like and not isinstance(obj, Set) def is_array_like(obj): diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 9ab18b0577b6c..3fb7795a89185 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -11,6 +11,7 @@ is_object_dtype, is_string_like, is_list_like, + is_ordered_list_like, is_scalar, is_integer, is_re) @@ -1996,12 +1997,12 @@ def _get_series_list(self, others, ignore_index=False): elif isinstance(others, np.ndarray) and others.ndim == 2: others = DataFrame(others, index=idx) return ([others[x] for x in others], False) - elif is_list_like(others, strict=True): + elif is_ordered_list_like(others): others = list(others) # ensure iterators do not get read twice etc # in case of list-like `others`, all elements must be # either one-dimensional list-likes or scalars - if all(is_list_like(x, strict=True) for x in others): + if all(is_ordered_list_like(x) for x in others): los = [] join_warn = False depr_warn = False @@ -2029,7 +2030,7 @@ def _get_series_list(self, others, ignore_index=False): # nested list-likes are forbidden: # -> elements of nxt must not be list-like is_legal = ((no_deep and nxt.dtype == object) - or all(not is_list_like(x, strict=True) + or all(not is_ordered_list_like(x) for x in nxt)) # DataFrame is false positive of is_legal @@ -2048,7 +2049,7 @@ def _get_series_list(self, others, ignore_index=False): 'deprecated and will be removed in a future ' 'version.', FutureWarning, stacklevel=3) return (los, join_warn) - elif all(not is_list_like(x, strict=True) for x in others): + elif all(not is_ordered_list_like(x) for x in others): return ([Series(others, index=idx)], False) raise TypeError(err_msg) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 66a403a69f731..3791fe9f81b26 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -88,15 +88,15 @@ def test_is_list_like_fails(ll): DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) ]) -def test_is_list_like_strict_passes(ll): - assert inference.is_list_like(ll, strict=True) +def test_is_ordered_list_like_strict_passes(ll): + assert inference.is_ordered_list_like(ll) @pytest.mark.parametrize("ll", [1, '2', object(), str, np.array(2), {1, 'a'}, frozenset({1, 'a'})]) -def test_is_list_like_strict_fails(ll): +def test_is_ordered_list_like_fails(ll): # GH 23061 - assert not inference.is_list_like(ll, strict=True) + assert not inference.is_ordered_list_like(ll) def test_is_array_like(): From 3647bdd68b66c16fd5ec95cb78c758fd3ed2e523 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Tue, 16 Oct 2018 07:41:06 +0200 Subject: [PATCH 11/17] Split off changes in strings.py --- pandas/core/strings.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 3fb7795a89185..10bd616929c8f 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -11,7 +11,6 @@ is_object_dtype, is_string_like, is_list_like, - is_ordered_list_like, is_scalar, is_integer, is_re) @@ -1997,12 +1996,12 @@ def _get_series_list(self, others, ignore_index=False): elif isinstance(others, np.ndarray) and others.ndim == 2: others = DataFrame(others, index=idx) return ([others[x] for x in others], False) - elif is_ordered_list_like(others): + elif is_list_like(others): others = list(others) # ensure iterators do not get read twice etc # in case of list-like `others`, all elements must be # either one-dimensional list-likes or scalars - if all(is_ordered_list_like(x) for x in others): + if all(is_list_like(x) for x in others): los = [] join_warn = False depr_warn = False @@ -2030,7 +2029,7 @@ def _get_series_list(self, others, ignore_index=False): # nested list-likes are forbidden: # -> elements of nxt must not be list-like is_legal = ((no_deep and nxt.dtype == object) - or all(not is_ordered_list_like(x) + or all(not is_list_like(x) for x in nxt)) # DataFrame is false positive of is_legal @@ -2049,7 +2048,7 @@ def _get_series_list(self, others, ignore_index=False): 'deprecated and will be removed in a future ' 'version.', FutureWarning, stacklevel=3) return (los, join_warn) - elif all(not is_ordered_list_like(x) for x in others): + elif all(not is_list_like(x) for x in others): return ([Series(others, index=idx)], False) raise TypeError(err_msg) From d5ef14fb853218220c4d534135295d09d01981d5 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Tue, 16 Oct 2018 08:51:43 +0200 Subject: [PATCH 12/17] Tiny fix --- pandas/core/strings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 10bd616929c8f..4086021bc61a6 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -2029,8 +2029,7 @@ def _get_series_list(self, others, ignore_index=False): # nested list-likes are forbidden: # -> elements of nxt must not be list-like is_legal = ((no_deep and nxt.dtype == object) - or all(not is_list_like(x) - for x in nxt)) + or all(not is_list_like(x) for x in nxt)) # DataFrame is false positive of is_legal # because "x in df" returns column names From 4b91d2e5558d725eaa65f74c2bead41e432be61f Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 17 Oct 2018 00:10:04 +0200 Subject: [PATCH 13/17] Revert "Back to is_ordered_list_like" This reverts commit 3796080a463f718d89ca13a5953afcdda1ae1c5e. --- pandas/core/dtypes/common.py | 6 +++--- pandas/core/dtypes/inference.py | 29 ++++++--------------------- pandas/core/strings.py | 1 - pandas/tests/dtypes/test_inference.py | 8 ++++---- 4 files changed, 13 insertions(+), 31 deletions(-) diff --git a/pandas/core/dtypes/common.py b/pandas/core/dtypes/common.py index 5f8c7a807bc50..bf22025761426 100644 --- a/pandas/core/dtypes/common.py +++ b/pandas/core/dtypes/common.py @@ -18,9 +18,9 @@ ABCSparseArray, ABCSparseSeries, ABCCategoricalIndex, ABCIndexClass, ABCDateOffset) from pandas.core.dtypes.inference import ( # noqa:F401 - is_bool, is_integer, is_float, is_number, is_decimal, is_complex, is_re, - is_re_compilable, is_dict_like, is_string_like, is_file_like, is_list_like, - is_ordered_list_like, is_nested_list_like, is_sequence, is_named_tuple, + is_bool, is_integer, is_float, is_number, is_decimal, is_complex, + is_re, is_re_compilable, is_dict_like, is_string_like, is_file_like, + is_list_like, is_nested_list_like, is_sequence, is_named_tuple, is_hashable, is_iterator, is_array_like, is_scalar, is_interval) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index c80eb6edba606..12eff1130d8a3 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -247,7 +247,7 @@ def is_re_compilable(obj): return True -def is_list_like(obj): +def is_list_like(obj, strict=False): """ Check if the object is list-like. @@ -259,6 +259,8 @@ def is_list_like(obj): Parameters ---------- obj : The object to check. + strict : boolean, default False + If this parameter is True, sets will not be considered list-like Returns ------- @@ -287,28 +289,9 @@ def is_list_like(obj): # we do not count strings/unicode/bytes as list-like and not isinstance(obj, string_and_binary_types) # exclude zero-dimensional numpy arrays, effectively scalars - and not (isinstance(obj, np.ndarray) and obj.ndim == 0)) - - -def is_ordered_list_like(obj): - """ - Check if the object is list-like and has a defined order - - Works like :meth:`is_list_like` but excludes sets. Note that iterators can - not be inspected for order - this check will return True but it is up to - the user to make sure that their iterators are generated in an ordered way. - - Parameters - ---------- - obj : The object to check. - - Returns - ------- - is_ordered_list_like : bool - Whether `obj` is an ordered list-like - """ - list_like = is_list_like(obj) - return list_like and not isinstance(obj, Set) + and not (isinstance(obj, np.ndarray) and obj.ndim == 0) + # exclude sets if ordered_only + and not (strict and isinstance(obj, Set))) def is_array_like(obj): diff --git a/pandas/core/strings.py b/pandas/core/strings.py index 4086021bc61a6..e9eaa110eec1b 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -2030,7 +2030,6 @@ def _get_series_list(self, others, ignore_index=False): # -> elements of nxt must not be list-like is_legal = ((no_deep and nxt.dtype == object) or all(not is_list_like(x) for x in nxt)) - # DataFrame is false positive of is_legal # because "x in df" returns column names if not is_legal or isinstance(nxt, DataFrame): diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 3791fe9f81b26..66a403a69f731 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -88,15 +88,15 @@ def test_is_list_like_fails(ll): DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) ]) -def test_is_ordered_list_like_strict_passes(ll): - assert inference.is_ordered_list_like(ll) +def test_is_list_like_strict_passes(ll): + assert inference.is_list_like(ll, strict=True) @pytest.mark.parametrize("ll", [1, '2', object(), str, np.array(2), {1, 'a'}, frozenset({1, 'a'})]) -def test_is_ordered_list_like_fails(ll): +def test_is_list_like_strict_fails(ll): # GH 23061 - assert not inference.is_ordered_list_like(ll) + assert not inference.is_list_like(ll, strict=True) def test_is_array_like(): From d1ff6ab9637fb3cb360a314a604d9ed3d6bc1d58 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 17 Oct 2018 00:14:47 +0200 Subject: [PATCH 14/17] Change kwarg to "allow_sets" --- pandas/core/dtypes/inference.py | 10 +++++----- pandas/tests/dtypes/test_inference.py | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 12eff1130d8a3..2eac42ccc6b68 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -247,7 +247,7 @@ def is_re_compilable(obj): return True -def is_list_like(obj, strict=False): +def is_list_like(obj, allow_sets=True): """ Check if the object is list-like. @@ -259,8 +259,8 @@ def is_list_like(obj, strict=False): Parameters ---------- obj : The object to check. - strict : boolean, default False - If this parameter is True, sets will not be considered list-like + allow_sets : boolean, default True + If this parameter is False, sets will not be considered list-like Returns ------- @@ -290,8 +290,8 @@ def is_list_like(obj, strict=False): and not isinstance(obj, string_and_binary_types) # exclude zero-dimensional numpy arrays, effectively scalars and not (isinstance(obj, np.ndarray) and obj.ndim == 0) - # exclude sets if ordered_only - and not (strict and isinstance(obj, Set))) + # exclude sets if allow_sets is False + and not (allow_sets is False and isinstance(obj, Set))) def is_array_like(obj): diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 66a403a69f731..de9f9257f8fbd 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -88,15 +88,15 @@ def test_is_list_like_fails(ll): DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) ]) -def test_is_list_like_strict_passes(ll): - assert inference.is_list_like(ll, strict=True) +def test_is_list_like_disallow_sets_passes(ll): + assert inference.is_list_like(ll, allow_sets=False) @pytest.mark.parametrize("ll", [1, '2', object(), str, np.array(2), {1, 'a'}, frozenset({1, 'a'})]) -def test_is_list_like_strict_fails(ll): +def test_is_list_like_disallow_sets_fails(ll): # GH 23061 - assert not inference.is_list_like(ll, strict=True) + assert not inference.is_list_like(ll, allow_sets=False) def test_is_array_like(): From ece9deb63b02250d2b00d55593bf478f327fa8c8 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 17 Oct 2018 00:18:13 +0200 Subject: [PATCH 15/17] Tiny fix --- pandas/core/strings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/core/strings.py b/pandas/core/strings.py index e9eaa110eec1b..4086021bc61a6 100644 --- a/pandas/core/strings.py +++ b/pandas/core/strings.py @@ -2030,6 +2030,7 @@ def _get_series_list(self, others, ignore_index=False): # -> elements of nxt must not be list-like is_legal = ((no_deep and nxt.dtype == object) or all(not is_list_like(x) for x in nxt)) + # DataFrame is false positive of is_legal # because "x in df" returns column names if not is_legal or isinstance(nxt, DataFrame): From 13e098375b4907032e55ec11ebfc9da538732b00 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 17 Oct 2018 08:06:04 +0200 Subject: [PATCH 16/17] Review (jreback) --- doc/source/whatsnew/v0.24.0.txt | 2 ++ pandas/core/dtypes/inference.py | 4 +++ pandas/tests/dtypes/test_inference.py | 35 ++++++++++----------------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 3053625721560..b46d3d7522bc5 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -198,6 +198,8 @@ Other Enhancements - :meth:`round`, :meth:`ceil`, and meth:`floor` for :class:`DatetimeIndex` and :class:`Timestamp` now support an ``ambiguous`` argument for handling datetimes that are rounded to ambiguous times (:issue:`18946`) - :class:`Resampler` now is iterable like :class:`GroupBy` (:issue:`15314`). - :meth:`Series.resample` and :meth:`DataFrame.resample` have gained the :meth:`Resampler.quantile` (:issue:`15023`). +- :meth:`pandas.core.dtypes.is_list_like` has gained a keyword ``allow_sets`` which is ``True`` by default; if ``False``, + all instances of ``set`` will not be considered "list-like" anymore (:issue:`23061`) - :meth:`Index.to_frame` now supports overriding column name(s) (:issue:`22580`). - New attribute :attr:`__git_version__` will return git commit sha of current build (:issue:`21295`). - Compatibility with Matplotlib 3.0 (:issue:`22790`). diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 2eac42ccc6b68..7470497383064 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -262,6 +262,8 @@ def is_list_like(obj, allow_sets=True): allow_sets : boolean, default True If this parameter is False, sets will not be considered list-like + .. versionadded:: 0.24.0 + Returns ------- is_list_like : bool @@ -288,8 +290,10 @@ def is_list_like(obj, allow_sets=True): return (isinstance(obj, compat.Iterable) # we do not count strings/unicode/bytes as list-like and not isinstance(obj, string_and_binary_types) + # exclude zero-dimensional numpy arrays, effectively scalars and not (isinstance(obj, np.ndarray) and obj.ndim == 0) + # exclude sets if allow_sets is False and not (allow_sets is False and isinstance(obj, Set))) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index de9f9257f8fbd..fa2b5d6eaf685 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -64,39 +64,30 @@ def __getitem__(self): @pytest.mark.parametrize( - "ll", - [ + "obj, expected", + list(zip([ [], [1], tuple(), (1, ), (1, 2), {'a': 1}, {1, 'a'}, np.array([2]), Series([1]), Series([]), Series(['a']).str, Index([]), Index([1]), DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) - ]) -def test_is_list_like_passes(ll): - assert inference.is_list_like(ll) - - -@pytest.mark.parametrize("ll", [1, '2', object(), str, np.array(2)]) -def test_is_list_like_fails(ll): - assert not inference.is_list_like(ll) + ], [True] * 30)) + + list(zip([1, '2', object(), str, np.array(2)], [False] * 10))) +def test_is_list_like(obj, expected): + assert inference.is_list_like(obj) == expected @pytest.mark.parametrize( - "ll", - [ + "obj, expected", + list(zip([ [], [1], tuple(), (1, ), (1, 2), {'a': 1}, np.array([2]), Series([1]), Series([]), Series(['a']).str, Index([]), Index([1]), DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) - ]) -def test_is_list_like_disallow_sets_passes(ll): - assert inference.is_list_like(ll, allow_sets=False) - - -@pytest.mark.parametrize("ll", [1, '2', object(), str, np.array(2), - {1, 'a'}, frozenset({1, 'a'})]) -def test_is_list_like_disallow_sets_fails(ll): - # GH 23061 - assert not inference.is_list_like(ll, allow_sets=False) + ], [True] * 30)) + + list(zip([1, '2', object(), str, np.array(2), + {1, 'a'}, frozenset({1, 'a'})], [False] * 10))) +def test_is_list_like_disallow_sets(obj, expected): + assert inference.is_list_like(obj, allow_sets=False) == expected def test_is_array_like(): From 2f5e927bdaea3e0abb2d06c4cbbbc806fffade24 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 17 Oct 2018 18:18:03 +0200 Subject: [PATCH 17/17] Review (TomAugspurger & jreback) --- pandas/tests/dtypes/test_inference.py | 91 +++++++++++++++++++-------- 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index fa2b5d6eaf685..d0dd03d6eb8df 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -47,6 +47,70 @@ def coerce(request): return request.param +# collect all objects to be tested for list-like-ness; use tuples of objects, +# whether they are list-like or not (special casing for sets), and their ID +ll_params = [ + ([1], True, 'list'), # noqa: E241 + ([], True, 'list-empty'), # noqa: E241 + ((1, ), True, 'tuple'), # noqa: E241 + (tuple(), True, 'tuple-empty'), # noqa: E241 + ({'a': 1}, True, 'dict'), # noqa: E241 + (dict(), True, 'dict-empty'), # noqa: E241 + ({'a', 1}, 'set', 'set'), # noqa: E241 + (set(), 'set', 'set-empty'), # noqa: E241 + (frozenset({'a', 1}), 'set', 'frozenset'), # noqa: E241 + (frozenset([]), 'set', 'frozenset-empty'), # noqa: E241 + (iter([1, 2]), True, 'iterator'), # noqa: E241 + (iter([]), True, 'iterator-empty'), # noqa: E241 + ((x for x in [1, 2]), True, 'generator'), # noqa: E241 + ((x for x in []), True, 'generator-empty'), # noqa: E241 + (Series([1]), True, 'Series'), # noqa: E241 + (Series([]), True, 'Series-empty'), # noqa: E241 + (Series(['a']).str, True, 'StringMethods'), # noqa: E241 + (Series([], dtype='O').str, True, 'StringMethods-empty'), # noqa: E241 + (Index([1]), True, 'Index'), # noqa: E241 + (Index([]), True, 'Index-empty'), # noqa: E241 + (DataFrame([[1]]), True, 'DataFrame'), # noqa: E241 + (DataFrame(), True, 'DataFrame-empty'), # noqa: E241 + (np.ndarray((2,) * 1), True, 'ndarray-1d'), # noqa: E241 + (np.array([]), True, 'ndarray-1d-empty'), # noqa: E241 + (np.ndarray((2,) * 2), True, 'ndarray-2d'), # noqa: E241 + (np.array([[]]), True, 'ndarray-2d-empty'), # noqa: E241 + (np.ndarray((2,) * 3), True, 'ndarray-3d'), # noqa: E241 + (np.array([[[]]]), True, 'ndarray-3d-empty'), # noqa: E241 + (np.ndarray((2,) * 4), True, 'ndarray-4d'), # noqa: E241 + (np.array([[[[]]]]), True, 'ndarray-4d-empty'), # noqa: E241 + (np.array(2), False, 'ndarray-0d'), # noqa: E241 + (1, False, 'int'), # noqa: E241 + (b'123', False, 'bytes'), # noqa: E241 + (b'', False, 'bytes-empty'), # noqa: E241 + ('123', False, 'string'), # noqa: E241 + ('', False, 'string-empty'), # noqa: E241 + (str, False, 'string-type'), # noqa: E241 + (object(), False, 'object'), # noqa: E241 + (np.nan, False, 'NaN'), # noqa: E241 + (None, False, 'None') # noqa: E241 +] +objs, expected, ids = zip(*ll_params) + + +@pytest.fixture(params=zip(objs, expected), ids=ids) +def maybe_list_like(request): + return request.param + + +def test_is_list_like(maybe_list_like): + obj, expected = maybe_list_like + expected = True if expected == 'set' else expected + assert inference.is_list_like(obj) == expected + + +def test_is_list_like_disallow_sets(maybe_list_like): + obj, expected = maybe_list_like + expected = False if expected == 'set' else expected + assert inference.is_list_like(obj, allow_sets=False) == expected + + def test_is_sequence(): is_seq = inference.is_sequence assert (is_seq((1, 2))) @@ -63,33 +127,6 @@ def __getitem__(self): assert (not is_seq(A())) -@pytest.mark.parametrize( - "obj, expected", - list(zip([ - [], [1], tuple(), (1, ), (1, 2), {'a': 1}, {1, 'a'}, np.array([2]), - Series([1]), Series([]), Series(['a']).str, Index([]), Index([1]), - DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), - np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) - ], [True] * 30)) - + list(zip([1, '2', object(), str, np.array(2)], [False] * 10))) -def test_is_list_like(obj, expected): - assert inference.is_list_like(obj) == expected - - -@pytest.mark.parametrize( - "obj, expected", - list(zip([ - [], [1], tuple(), (1, ), (1, 2), {'a': 1}, np.array([2]), - Series([1]), Series([]), Series(['a']).str, Index([]), Index([1]), - DataFrame(), DataFrame([[1]]), iter([1, 2]), (x for x in [1, 2]), - np.ndarray((2,) * 2), np.ndarray((2,) * 3), np.ndarray((2,) * 4) - ], [True] * 30)) - + list(zip([1, '2', object(), str, np.array(2), - {1, 'a'}, frozenset({1, 'a'})], [False] * 10))) -def test_is_list_like_disallow_sets(obj, expected): - assert inference.is_list_like(obj, allow_sets=False) == expected - - def test_is_array_like(): assert inference.is_array_like(Series([])) assert inference.is_array_like(Series([1, 2]))