Skip to content

Commit cf6656c

Browse files
committed
Merge branch 'master' of https://github.com/pandas-dev/pandas into tslibs-offsets-inits
2 parents 7eb906d + fbe15d0 commit cf6656c

File tree

17 files changed

+530
-481
lines changed

17 files changed

+530
-481
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,6 @@ before_install:
102102
- uname -a
103103
- git --version
104104
- git tag
105-
- ci/before_install_travis.sh
106-
- export DISPLAY=":99.0"
107105

108106
install:
109107
- echo "install start"
@@ -114,6 +112,8 @@ install:
114112

115113
before_script:
116114
- ci/install_db_travis.sh
115+
- export DISPLAY=":99.0"
116+
- ci/before_script_travis.sh
117117

118118
script:
119119
- echo "script start"

ci/before_install_travis.sh renamed to ci/before_script_travis.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ echo "inside $0"
44

55
if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
66
sh -e /etc/init.d/xvfb start
7+
sleep 3
78
fi
89

910
# Never fail because bad things happened here.

doc/source/whatsnew/v0.21.1.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Conversion
7373
Indexing
7474
^^^^^^^^
7575

76-
-
76+
- Bug where a ``MultiIndex`` with more than a million records was not raising ``AttributeError`` when trying to access a missing attribute (:issue:`18165`)
7777
-
7878
-
7979

doc/source/whatsnew/v0.22.0.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Deprecations
6161
Removal of prior version deprecations/changes
6262
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6363

64-
-
64+
- Warnings against the obsolete usage ``Categorical(codes, categories)``, which were emitted for instance when the first two arguments to ``Categorical()`` had different dtypes, and recommended the use of ``Categorical.from_codes``, have now been removed (:issue:`8074`)
6565
-
6666
-
6767

@@ -72,6 +72,7 @@ Performance Improvements
7272

7373
- Indexers on ``Series`` or ``DataFrame`` no longer create a reference cycle (:issue:`17956`)
7474
- Added a keyword argument, ``cache``, to :func:`to_datetime` that improved the performance of converting duplicate datetime arguments (:issue:`11665`)
75+
- :class`DateOffset` arithmetic performance is improved (:issue:`18218`)
7576
-
7677

7778
.. _whatsnew_0220.docs:

pandas/_libs/tslibs/offsets.pyx

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
cimport cython
55

66
import time
7-
from cpython.datetime cimport timedelta, time as dt_time
7+
from cpython.datetime cimport datetime, timedelta, time as dt_time
88

99
from dateutil.relativedelta import relativedelta
1010

@@ -13,9 +13,9 @@ cimport numpy as np
1313
np.import_array()
1414

1515

16-
from util cimport is_string_object
16+
from util cimport is_string_object, is_integer_object
1717

18-
from pandas._libs.tslib import pydt_to_i8
18+
from pandas._libs.tslib import pydt_to_i8, monthrange
1919

2020
from frequencies cimport get_freq_code
2121
from conversion cimport tz_convert_single
@@ -375,3 +375,56 @@ class BaseOffset(_BaseOffset):
375375
# i.e. isinstance(other, (ABCDatetimeIndex, ABCSeries))
376376
return other - self
377377
return -self + other
378+
379+
380+
# ----------------------------------------------------------------------
381+
# RelativeDelta Arithmetic
382+
383+
384+
cpdef datetime shift_month(datetime stamp, int months, object day_opt=None):
385+
"""
386+
Given a datetime (or Timestamp) `stamp`, an integer `months` and an
387+
option `day_opt`, return a new datetimelike that many months later,
388+
with day determined by `day_opt` using relativedelta semantics.
389+
390+
Scalar analogue of tslib.shift_months
391+
392+
Parameters
393+
----------
394+
stamp : datetime or Timestamp
395+
months : int
396+
day_opt : None, 'start', 'end', or an integer
397+
None: returned datetimelike has the same day as the input, or the
398+
last day of the month if the new month is too short
399+
'start': returned datetimelike has day=1
400+
'end': returned datetimelike has day on the last day of the month
401+
int: returned datetimelike has day equal to day_opt
402+
403+
Returns
404+
-------
405+
shifted : datetime or Timestamp (same as input `stamp`)
406+
"""
407+
cdef:
408+
int year, month, day
409+
int dim, dy
410+
411+
dy = (stamp.month + months) // 12
412+
month = (stamp.month + months) % 12
413+
414+
if month == 0:
415+
month = 12
416+
dy -= 1
417+
year = stamp.year + dy
418+
419+
dim = monthrange(year, month)[1]
420+
if day_opt is None:
421+
day = min(stamp.day, dim)
422+
elif day_opt == 'start':
423+
day = 1
424+
elif day_opt == 'end':
425+
day = dim
426+
elif is_integer_object(day_opt):
427+
day = min(day_opt, dim)
428+
else:
429+
raise ValueError(day_opt)
430+
return stamp.replace(year=year, month=month, day=day)

pandas/core/api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
from pandas.core.panel import Panel, WidePanel
2525
from pandas.core.panel4d import Panel4D
2626
from pandas.core.reshape.reshape import (
27-
pivot_simple as pivot, get_dummies,
28-
lreshape, wide_to_long)
27+
pivot_simple as pivot, get_dummies)
28+
from pandas.core.reshape.melt import lreshape, wide_to_long
2929

3030
from pandas.core.indexing import IndexSlice
3131
from pandas.core.tools.numeric import to_numeric

pandas/core/categorical.py

Lines changed: 18 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
is_timedelta64_dtype,
2626
is_categorical,
2727
is_categorical_dtype,
28-
is_integer_dtype,
2928
is_list_like, is_sequence,
3029
is_scalar,
3130
is_dict_like)
@@ -261,6 +260,7 @@ def __init__(self, values, categories=None, ordered=None, dtype=None,
261260
# c.) infer from values
262261

263262
if dtype is not None:
263+
# The dtype argument takes precedence over values.dtype (if any)
264264
if isinstance(dtype, compat.string_types):
265265
if dtype == 'category':
266266
dtype = CategoricalDtype(categories, ordered)
@@ -275,9 +275,12 @@ def __init__(self, values, categories=None, ordered=None, dtype=None,
275275
ordered = dtype.ordered
276276

277277
elif is_categorical(values):
278+
# If no "dtype" was passed, use the one from "values", but honor
279+
# the "ordered" and "categories" arguments
278280
dtype = values.dtype._from_categorical_dtype(values.dtype,
279281
categories, ordered)
280282
else:
283+
# If dtype=None and values is not categorical, create a new dtype
281284
dtype = CategoricalDtype(categories, ordered)
282285

283286
# At this point, dtype is always a CategoricalDtype
@@ -294,28 +297,12 @@ def __init__(self, values, categories=None, ordered=None, dtype=None,
294297

295298
# sanitize input
296299
if is_categorical_dtype(values):
300+
if dtype.categories is None:
301+
dtype = CategoricalDtype(values.categories, dtype.ordered)
297302

298-
# we are either a Series or a CategoricalIndex
299-
if isinstance(values, (ABCSeries, ABCCategoricalIndex)):
300-
values = values._values
301-
302-
if ordered is None:
303-
ordered = values.ordered
304-
if categories is None:
305-
categories = values.categories
306-
values = values.get_values()
307-
308-
elif isinstance(values, (ABCIndexClass, ABCSeries)):
309-
# we'll do inference later
310-
pass
311-
312-
else:
313-
314-
# on numpy < 1.6 datetimelike get inferred to all i8 by
315-
# _sanitize_array which is fine, but since factorize does this
316-
# correctly no need here this is an issue because _sanitize_array
317-
# also coerces np.nan to a string under certain versions of numpy
318-
# as well
303+
elif not isinstance(values, (ABCIndexClass, ABCSeries)):
304+
# _sanitize_array coerces np.nan to a string under certain versions
305+
# of numpy
319306
values = maybe_infer_to_datetimelike(values, convert_dates=True)
320307
if not isinstance(values, np.ndarray):
321308
values = _convert_to_list_like(values)
@@ -335,7 +322,7 @@ def __init__(self, values, categories=None, ordered=None, dtype=None,
335322
codes, categories = factorize(values, sort=True)
336323
except TypeError:
337324
codes, categories = factorize(values, sort=False)
338-
if ordered:
325+
if dtype.ordered:
339326
# raise, as we don't have a sortable data structure and so
340327
# the user should give us one by specifying categories
341328
raise TypeError("'values' is not ordered, please "
@@ -347,34 +334,18 @@ def __init__(self, values, categories=None, ordered=None, dtype=None,
347334
raise NotImplementedError("> 1 ndim Categorical are not "
348335
"supported at this time")
349336

350-
if dtype.categories is None:
351-
# we're inferring from values
352-
dtype = CategoricalDtype(categories, ordered)
337+
# we're inferring from values
338+
dtype = CategoricalDtype(categories, dtype.ordered)
353339

354-
else:
355-
# there were two ways if categories are present
356-
# - the old one, where each value is a int pointer to the levels
357-
# array -> not anymore possible, but code outside of pandas could
358-
# call us like that, so make some checks
359-
# - the new one, where each value is also in the categories array
360-
# (or np.nan)
340+
elif is_categorical_dtype(values):
341+
old_codes = (values.cat.codes if isinstance(values, ABCSeries)
342+
else values.codes)
343+
codes = _recode_for_categories(old_codes, values.dtype.categories,
344+
dtype.categories)
361345

346+
else:
362347
codes = _get_codes_for_values(values, dtype.categories)
363348

364-
# TODO: check for old style usage. These warnings should be removes
365-
# after 0.18/ in 2016
366-
if (is_integer_dtype(values) and
367-
not is_integer_dtype(dtype.categories)):
368-
warn("Values and categories have different dtypes. Did you "
369-
"mean to use\n'Categorical.from_codes(codes, "
370-
"categories)'?", RuntimeWarning, stacklevel=2)
371-
372-
if (len(values) and is_integer_dtype(values) and
373-
(codes == -1).all()):
374-
warn("None of the categories were found in values. Did you "
375-
"mean to use\n'Categorical.from_codes(codes, "
376-
"categories)'?", RuntimeWarning, stacklevel=2)
377-
378349
if null_mask.any():
379350
# Reinsert -1 placeholders for previously removed missing values
380351
full_codes = - np.ones(null_mask.shape, dtype=codes.dtype)

pandas/core/frame.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4637,7 +4637,7 @@ def unstack(self, level=-1, fill_value=None):
46374637
other='melt'))
46384638
def melt(self, id_vars=None, value_vars=None, var_name=None,
46394639
value_name='value', col_level=None):
4640-
from pandas.core.reshape.reshape import melt
4640+
from pandas.core.reshape.melt import melt
46414641
return melt(self, id_vars=id_vars, value_vars=value_vars,
46424642
var_name=var_name, value_name=value_name,
46434643
col_level=col_level)

pandas/core/indexes/multi.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,17 @@ def _shallow_copy_with_infer(self, values=None, **kwargs):
446446
**kwargs)
447447
return self._shallow_copy(values, **kwargs)
448448

449+
@Appender(_index_shared_docs['__contains__'] % _index_doc_kwargs)
450+
def __contains__(self, key):
451+
hash(key)
452+
try:
453+
self.get_loc(key)
454+
return True
455+
except (LookupError, TypeError):
456+
return False
457+
458+
contains = __contains__
459+
449460
@Appender(_index_shared_docs['_shallow_copy'])
450461
def _shallow_copy(self, values=None, **kwargs):
451462
if values is not None:
@@ -1370,17 +1381,6 @@ def nlevels(self):
13701381
def levshape(self):
13711382
return tuple(len(x) for x in self.levels)
13721383

1373-
@Appender(_index_shared_docs['__contains__'] % _index_doc_kwargs)
1374-
def __contains__(self, key):
1375-
hash(key)
1376-
try:
1377-
self.get_loc(key)
1378-
return True
1379-
except LookupError:
1380-
return False
1381-
1382-
contains = __contains__
1383-
13841384
def __reduce__(self):
13851385
"""Necessary for making this object picklable"""
13861386
d = dict(levels=[lev for lev in self.levels],

pandas/core/reshape/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# flake8: noqa
22

33
from pandas.core.reshape.concat import concat
4-
from pandas.core.reshape.reshape import melt
4+
from pandas.core.reshape.melt import melt
55
from pandas.core.reshape.merge import (
66
merge, ordered_merge, merge_ordered, merge_asof)
77
from pandas.core.reshape.pivot import pivot_table, crosstab

0 commit comments

Comments
 (0)