Skip to content

Commit

Permalink
fix conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
charlesdong1991 committed Feb 24, 2019
2 parents a1a8891 + 85572de commit eb119d8
Show file tree
Hide file tree
Showing 23 changed files with 601 additions and 189 deletions.
19 changes: 19 additions & 0 deletions asv_bench/benchmarks/series_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,25 @@ def time_dropna(self, dtype):
self.s.dropna()


class SearchSorted(object):

goal_time = 0.2
params = ['int8', 'int16', 'int32', 'int64',
'uint8', 'uint16', 'uint32', 'uint64',
'float16', 'float32', 'float64',
'str']
param_names = ['dtype']

def setup(self, dtype):
N = 10**5
data = np.array([1] * N + [2] * N + [3] * N).astype(dtype)
self.s = Series(data)

def time_searchsorted(self, dtype):
key = '2' if dtype == 'str' else 2
self.s.searchsorted(key)


class Map(object):

params = ['dict', 'Series']
Expand Down
Binary file added doc/source/styled.xlsx
Binary file not shown.
10 changes: 10 additions & 0 deletions doc/source/user_guide/timeseries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,16 @@ We are stopping on the included end-point as it is part of the index:
dft2 = dft2.swaplevel(0, 1).sort_index()
dft2.loc[idx[:, '2013-01-05'], :]
.. versionadded:: 0.25.0

Slicing with string indexing also honors UTC offset.

.. ipython:: python
df = pd.DataFrame([0], index=pd.DatetimeIndex(['2019-01-01'], tz='US/Pacific'))
df
df['2019-01-01 12:00:00+04:00':'2019-01-01 13:00:00+04:00']
.. _timeseries.slice_vs_exact_match:

Slice vs. Exact Match
Expand Down
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v0.24.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Bug Fixes
**Other**

- Bug in :meth:`Series.is_unique` where single occurrences of ``NaN`` were not considered unique (:issue:`25180`)
-
- Bug in :func:`merge` when merging an empty ``DataFrame`` with an ``Int64`` column or a non-empty ``DataFrame`` with an ``Int64`` column that is all ``NaN`` (:issue:`25183`)
-

.. _whatsnew_0.242.contributors:
Expand Down
40 changes: 37 additions & 3 deletions doc/source/whatsnew/v0.25.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,46 @@ Other Enhancements
- :meth:`Timestamp.replace` now supports the ``fold`` argument to disambiguate DST transition times (:issue:`25017`)
- :meth:`DataFrame.at_time` and :meth:`Series.at_time` now support :meth:`datetime.time` objects with timezones (:issue:`24043`)
- ``Series.str`` has gained :meth:`Series.str.casefold` method to removes all case distinctions present in a string (:issue:`25405`)
- :meth:`DataFrame.set_index` now works for instances of ``abc.Iterator``, provided their output is of the same length as the calling frame (:issue:`22484`, :issue:`24984`)
- :meth:`DatetimeIndex.union` now supports the ``sort`` argument. The behaviour of the sort parameter matches that of :meth:`Index.union` (:issue:`24994`)
-

.. _whatsnew_0250.api_breaking:

Backwards incompatible API changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- :meth:`Timestamp.strptime` will now raise a NotImplementedError (:issue:`25016`)
.. _whatsnew_0250.api_breaking.utc_offset_indexing:

Indexing with date strings with UTC offsets
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Indexing a :class:`DataFrame` or :class:`Series` with a :class:`DatetimeIndex` with a
date string with a UTC offset would previously ignore the UTC offset. Now, the UTC offset
is respected in indexing. (:issue:`24076`, :issue:`16785`)

*Previous Behavior*:

.. code-block:: ipython
In [1]: df = pd.DataFrame([0], index=pd.DatetimeIndex(['2019-01-01'], tz='US/Pacific'))
In [2]: df
Out[2]:
0
2019-01-01 00:00:00-08:00 0
In [3]: df['2019-01-01 00:00:00+04:00':'2019-01-01 01:00:00+04:00']
Out[3]:
0
2019-01-01 00:00:00-08:00 0
*New Behavior*:

.. ipython:: ipython

df = pd.DataFrame([0], index=pd.DatetimeIndex(['2019-01-01'], tz='US/Pacific'))
df['2019-01-01 12:00:00+04:00':'2019-01-01 13:00:00+04:00']

.. _whatsnew_0250.api.other:

Expand All @@ -38,7 +71,7 @@ Other API Changes

- :class:`DatetimeTZDtype` will now standardize pytz timezones to a common timezone instance (:issue:`24713`)
- ``Timestamp`` and ``Timedelta`` scalars now implement the :meth:`to_numpy` method as aliases to :meth:`Timestamp.to_datetime64` and :meth:`Timedelta.to_timedelta64`, respectively. (:issue:`24653`)
-
- :meth:`Timestamp.strptime` will now rise a ``NotImplementedError`` (:issue:`25016`)
-

.. _whatsnew_0250.deprecations:
Expand All @@ -64,7 +97,8 @@ Performance Improvements

- Significant speedup in `SparseArray` initialization that benefits most operations, fixing performance regression introduced in v0.20.0 (:issue:`24985`)
- `DataFrame.to_stata()` is now faster when outputting data with any string or non-native endian columns (:issue:`25045`)
-
- Improved performance of :meth:`Series.searchsorted`. The speedup is especially large when the dtype is
int8/int16/int32 and the searched key is within the integer bounds for the dtype (:issue:`22034`)


.. _whatsnew_0250.bug_fixes:
Expand Down
2 changes: 2 additions & 0 deletions pandas/compat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def lfilter(*args, **kwargs):
reload = reload
Hashable = collections.abc.Hashable
Iterable = collections.abc.Iterable
Iterator = collections.abc.Iterator
Mapping = collections.abc.Mapping
MutableMapping = collections.abc.MutableMapping
Sequence = collections.abc.Sequence
Expand Down Expand Up @@ -199,6 +200,7 @@ def get_range_parameters(data):

Hashable = collections.Hashable
Iterable = collections.Iterable
Iterator = collections.Iterator
Mapping = collections.Mapping
MutableMapping = collections.MutableMapping
Sequence = collections.Sequence
Expand Down
85 changes: 84 additions & 1 deletion pandas/core/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
ensure_float64, ensure_int64, ensure_object, ensure_platform_int,
ensure_uint64, is_array_like, is_bool_dtype, is_categorical_dtype,
is_complex_dtype, is_datetime64_any_dtype, is_datetime64tz_dtype,
is_datetimelike, is_extension_array_dtype, is_float_dtype,
is_datetimelike, is_extension_array_dtype, is_float_dtype, is_integer,
is_integer_dtype, is_interval_dtype, is_list_like, is_numeric_dtype,
is_object_dtype, is_period_dtype, is_scalar, is_signed_integer_dtype,
is_sparse, is_timedelta64_dtype, is_unsigned_integer_dtype,
Expand Down Expand Up @@ -1724,6 +1724,89 @@ def func(arr, indexer, out, fill_value=np.nan):
return out


# ------------ #
# searchsorted #
# ------------ #

def searchsorted(arr, value, side="left", sorter=None):
"""
Find indices where elements should be inserted to maintain order.
.. versionadded:: 0.25.0
Find the indices into a sorted array `arr` (a) such that, if the
corresponding elements in `value` were inserted before the indices,
the order of `arr` would be preserved.
Assuming that `arr` is sorted:
====== ================================
`side` returned index `i` satisfies
====== ================================
left ``arr[i-1] < value <= self[i]``
right ``arr[i-1] <= value < self[i]``
====== ================================
Parameters
----------
arr: array-like
Input array. If `sorter` is None, then it must be sorted in
ascending order, otherwise `sorter` must be an array of indices
that sort it.
value : array_like
Values to insert into `arr`.
side : {'left', 'right'}, optional
If 'left', the index of the first suitable location found is given.
If 'right', return the last such index. If there is no suitable
index, return either 0 or N (where N is the length of `self`).
sorter : 1-D array_like, optional
Optional array of integer indices that sort array a into ascending
order. They are typically the result of argsort.
Returns
-------
array of ints
Array of insertion points with the same shape as `value`.
See Also
--------
numpy.searchsorted : Similar method from NumPy.
"""
if sorter is not None:
sorter = ensure_platform_int(sorter)

if isinstance(arr, np.ndarray) and is_integer_dtype(arr) and (
is_integer(value) or is_integer_dtype(value)):
from .arrays.array_ import array
# if `arr` and `value` have different dtypes, `arr` would be
# recast by numpy, causing a slow search.
# Before searching below, we therefore try to give `value` the
# same dtype as `arr`, while guarding against integer overflows.
iinfo = np.iinfo(arr.dtype.type)
value_arr = np.array([value]) if is_scalar(value) else np.array(value)
if (value_arr >= iinfo.min).all() and (value_arr <= iinfo.max).all():
# value within bounds, so no overflow, so can convert value dtype
# to dtype of arr
dtype = arr.dtype
else:
dtype = value_arr.dtype

if is_scalar(value):
value = dtype.type(value)
else:
value = array(value, dtype=dtype)
elif not (is_object_dtype(arr) or is_numeric_dtype(arr) or
is_categorical_dtype(arr)):
from pandas.core.series import Series
# E.g. if `arr` is an array with dtype='datetime64[ns]'
# and `value` is a pd.Timestamp, we may need to convert value
value_ser = Series(value)._values
value = value_ser[0] if is_scalar(value) else value_ser

result = arr.searchsorted(value, side=side, sorter=sorter)
return result


# ---- #
# diff #
# ---- #
Expand Down
18 changes: 9 additions & 9 deletions pandas/core/arrays/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,17 +555,17 @@ def searchsorted(self, value, side="left", sorter=None):
.. versionadded:: 0.24.0
Find the indices into a sorted array `self` (a) such that, if the
corresponding elements in `v` were inserted before the indices, the
order of `self` would be preserved.
corresponding elements in `value` were inserted before the indices,
the order of `self` would be preserved.
Assuming that `a` is sorted:
Assuming that `self` is sorted:
====== ============================
====== ================================
`side` returned index `i` satisfies
====== ============================
left ``self[i-1] < v <= self[i]``
right ``self[i-1] <= v < self[i]``
====== ============================
====== ================================
left ``self[i-1] < value <= self[i]``
right ``self[i-1] <= value < self[i]``
====== ================================
Parameters
----------
Expand All @@ -581,7 +581,7 @@ def searchsorted(self, value, side="left", sorter=None):
Returns
-------
indices : array of ints
array of ints
Array of insertion points with the same shape as `value`.
See Also
Expand Down
7 changes: 7 additions & 0 deletions pandas/core/arrays/numpy_.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from pandas._libs import lib
from pandas.compat.numpy import function as nv
from pandas.util._decorators import Appender
from pandas.util._validators import validate_fillna_kwargs

from pandas.core.dtypes.dtypes import ExtensionDtype
Expand All @@ -12,6 +13,7 @@

from pandas import compat
from pandas.core import nanops
from pandas.core.algorithms import searchsorted
from pandas.core.missing import backfill_1d, pad_1d

from .base import ExtensionArray, ExtensionOpsMixin
Expand Down Expand Up @@ -423,6 +425,11 @@ def to_numpy(self, dtype=None, copy=False):

return result

@Appender(ExtensionArray.searchsorted.__doc__)
def searchsorted(self, value, side='left', sorter=None):
return searchsorted(self.to_numpy(), value,
side=side, sorter=sorter)

# ------------------------------------------------------------------------
# Ops

Expand Down
6 changes: 3 additions & 3 deletions pandas/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1522,11 +1522,11 @@ def factorize(self, sort=False, na_sentinel=-1):
array([3])
""")

@Substitution(klass='IndexOpsMixin')
@Substitution(klass='Index')
@Appender(_shared_docs['searchsorted'])
def searchsorted(self, value, side='left', sorter=None):
# needs coercion on the key (DatetimeIndex does already)
return self._values.searchsorted(value, side=side, sorter=sorter)
return algorithms.searchsorted(self._values, value,
side=side, sorter=sorter)

def drop_duplicates(self, keep='first', inplace=False):
inplace = validate_bool_kwarg(inplace, 'inplace')
Expand Down
43 changes: 41 additions & 2 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from pandas import compat
from pandas.compat import (range, map, zip, lmap, lzip, StringIO, u,
PY36, raise_with_traceback,
PY36, raise_with_traceback, Iterator,
string_and_binary_types)
from pandas.compat.numpy import function as nv
from pandas.core.dtypes.cast import (
Expand Down Expand Up @@ -4025,7 +4025,8 @@ def set_index(self, keys, drop=True, append=False, inplace=False,
This parameter can be either a single column key, a single array of
the same length as the calling DataFrame, or a list containing an
arbitrary combination of column keys and arrays. Here, "array"
encompasses :class:`Series`, :class:`Index` and ``np.ndarray``.
encompasses :class:`Series`, :class:`Index`, ``np.ndarray``, and
instances of :class:`abc.Iterator`.
drop : bool, default True
Delete columns to be used as the new index.
append : bool, default False
Expand Down Expand Up @@ -4104,6 +4105,32 @@ def set_index(self, keys, drop=True, append=False, inplace=False,
if not isinstance(keys, list):
keys = [keys]

err_msg = ('The parameter "keys" may be a column key, one-dimensional '
'array, or a list containing only valid column keys and '
'one-dimensional arrays.')

missing = []
for col in keys:
if isinstance(col, (ABCIndexClass, ABCSeries, np.ndarray,
list, Iterator)):
# arrays are fine as long as they are one-dimensional
# iterators get converted to list below
if getattr(col, 'ndim', 1) != 1:
raise ValueError(err_msg)
else:
# everything else gets tried as a key; see GH 24969
try:
found = col in self.columns
except TypeError:
raise TypeError(err_msg + ' Received column of '
'type {}'.format(type(col)))
else:
if not found:
missing.append(col)

if missing:
raise KeyError('None of {} are in the columns'.format(missing))

if inplace:
frame = self
else:
Expand Down Expand Up @@ -4132,13 +4159,25 @@ def set_index(self, keys, drop=True, append=False, inplace=False,
elif isinstance(col, (list, np.ndarray)):
arrays.append(col)
names.append(None)
elif isinstance(col, Iterator):
arrays.append(list(col))
names.append(None)
# from here, col can only be a column label
else:
arrays.append(frame[col]._values)
names.append(col)
if drop:
to_remove.append(col)

if len(arrays[-1]) != len(self):
# check newest element against length of calling frame, since
# ensure_index_from_sequences would not raise for append=False.
raise ValueError('Length mismatch: Expected {len_self} rows, '
'received array of length {len_col}'.format(
len_self=len(self),
len_col=len(arrays[-1])
))

index = ensure_index_from_sequences(arrays, names)

if verify_integrity and not index.is_unique:
Expand Down
Loading

0 comments on commit eb119d8

Please sign in to comment.