summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/reference/arrays.datetime.rst121
-rw-r--r--numpy/add_newdocs.py173
-rw-r--r--numpy/core/_internal.py7
-rw-r--r--numpy/core/src/multiarray/datetime_busday.c16
-rw-r--r--numpy/core/src/multiarray/datetime_busdaycal.c123
-rw-r--r--numpy/core/tests/test_datetime.py28
-rw-r--r--numpy/lib/_iotools.py33
-rw-r--r--numpy/lib/function_base.py6
-rw-r--r--numpy/lib/tests/test_function_base.py5
-rw-r--r--numpy/lib/tests/test_io.py9
-rw-r--r--numpy/lib/tests/test_twodim_base.py5
-rw-r--r--numpy/ma/core.py4
-rw-r--r--pavement.py22
-rw-r--r--release.sh19
14 files changed, 384 insertions, 187 deletions
diff --git a/doc/source/reference/arrays.datetime.rst b/doc/source/reference/arrays.datetime.rst
index 2fd6dfcdd..d0f037bcb 100644
--- a/doc/source/reference/arrays.datetime.rst
+++ b/doc/source/reference/arrays.datetime.rst
@@ -23,7 +23,7 @@ be either a :ref:`date unit <arrays.dtypes.dateunits>` or a
:ref:`time unit <arrays.dtypes.timeunits>`. The date units are years ('Y'),
months ('M'), weeks ('W'), and days ('D'), while the time units are
hours ('h'), minutes ('m'), seconds ('s'), milliseconds ('ms'), and
-more SI-prefix seconds-based units.
+some additional SI-prefix seconds-based units.
.. admonition:: Example
@@ -139,7 +139,7 @@ simple datetime calculations.
>>> np.timedelta64(1,'W') / np.timedelta64(1,'D')
7.0
-There are two Timedelta units, years and months, which are treated
+There are two Timedelta units ('Y', years and 'M', months) which are treated
specially, because how much time they represent changes depending
on when they are used. While a timedelta day unit is equivalent to
24 hours, there is no way to convert a month unit into days, because
@@ -167,7 +167,13 @@ other units based on input data.
Datetimes are always stored based on POSIX time (though having a TAI
mode which allows for accounting of leap-seconds is proposed), with
a epoch of 1970-01-01T00:00Z. This means the supported dates are
-always a symmetric interval around 1970.
+always a symmetric interval around the epoch, called "time span" in the
+table below.
+
+The length of the span is the range of a 64-bit integer times the length
+of the date or unit. For example, the time span for 'W' (week) is exactly
+7 times longer than the time span for 'D' (day), and the time span for
+'D' (day) is exactly 24 times longer than the time span for 'h' (hour).
Here are the date units:
@@ -176,10 +182,10 @@ Here are the date units:
======== ================ ======================= ==========================
Code Meaning Time span (relative) Time span (absolute)
======== ================ ======================= ==========================
- Y year +- 9.2e18 years [9.2e18 BC, 9.2e18 AD]
- M month +- 7.6e17 years [7.6e17 BC, 7.6e17 AD]
- W week +- 1.7e17 years [1.7e17 BC, 1.7e17 AD]
- D day +- 2.5e16 years [2.5e16 BC, 2.5e16 AD]
+ Y year +/- 9.2e18 years [9.2e18 BC, 9.2e18 AD]
+ M month +/- 7.6e17 years [7.6e17 BC, 7.6e17 AD]
+ W week +/- 1.7e17 years [1.7e17 BC, 1.7e17 AD]
+ D day +/- 2.5e16 years [2.5e16 BC, 2.5e16 AD]
======== ================ ======================= ==========================
And here are the time units:
@@ -189,28 +195,34 @@ And here are the time units:
======== ================ ======================= ==========================
Code Meaning Time span (relative) Time span (absolute)
======== ================ ======================= ==========================
- h hour +- 1.0e15 years [1.0e15 BC, 1.0e15 AD]
- m minute +- 1.7e13 years [1.7e13 BC, 1.7e13 AD]
- s second +- 2.9e12 years [ 2.9e9 BC, 2.9e9 AD]
- ms millisecond +- 2.9e9 years [ 2.9e6 BC, 2.9e6 AD]
- us microsecond +- 2.9e6 years [290301 BC, 294241 AD]
- ns nanosecond +- 292 years [ 1678 AD, 2262 AD]
- ps picosecond +- 106 days [ 1969 AD, 1970 AD]
- fs femtosecond +- 2.6 hours [ 1969 AD, 1970 AD]
- as attosecond +- 9.2 seconds [ 1969 AD, 1970 AD]
+ h hour +/- 1.0e15 years [1.0e15 BC, 1.0e15 AD]
+ m minute +/- 1.7e13 years [1.7e13 BC, 1.7e13 AD]
+ s second +/- 2.9e12 years [ 2.9e9 BC, 2.9e9 AD]
+ ms millisecond +/- 2.9e9 years [ 2.9e6 BC, 2.9e6 AD]
+ us microsecond +/- 2.9e6 years [290301 BC, 294241 AD]
+ ns nanosecond +/- 292 years [ 1678 AD, 2262 AD]
+ ps picosecond +/- 106 days [ 1969 AD, 1970 AD]
+ fs femtosecond +/- 2.6 hours [ 1969 AD, 1970 AD]
+ as attosecond +/- 9.2 seconds [ 1969 AD, 1970 AD]
======== ================ ======================= ==========================
Business Day Functionality
==========================
-To allow the datetime to be used in contexts where accounting for weekends
-and holidays is important, NumPy includes a set of functions for
-working with business days.
+To allow the datetime to be used in contexts where only certain days of
+the week are valid, NumPy includes a set of "busday" (business day)
+functions.
+
+The default for busday functions is that the only valid days are Monday
+through Friday (the usual business days). The implementation is based on
+a "weekmask" containing 7 Boolean flags to indicate valid days; custom
+weekmasks are possible that specify other sets of valid days.
+
+The "busday" functions can additionally check a list of "holiday" dates,
+specific dates that are not valid days.
The function :func:`busday_offset` allows you to apply offsets
-specified in business days to datetimes with a unit of 'day'. By default,
-a business date is defined to be any date which falls on Monday through
-Friday, but this can be customized with a weekmask and a list of holidays.
+specified in business days to datetimes with a unit of 'D' (day).
.. admonition:: Example
@@ -266,7 +278,7 @@ is necessary to get a desired answer.
The function is also useful for computing some kinds of days
like holidays. In Canada and the U.S., Mother's day is on
-the second Sunday in May, which can be computed with a special
+the second Sunday in May, which can be computed with a custom
weekmask.
.. admonition:: Example
@@ -274,11 +286,66 @@ weekmask.
>>> np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun')
numpy.datetime64('2012-05-13','D')
-When performance is important for manipulating many business date
+When performance is important for manipulating many business dates
with one particular choice of weekmask and holidays, there is
an object :class:`busdaycalendar` which stores the data necessary
in an optimized form.
-The other two functions for business days are :func:`is_busday`
-and :func:`busday_count`, which are more straightforward and
-not explained here.
+np.is_busday():
+```````````````
+To test a datetime64 value to see if it is a valid day, use :func:`is_busday`.
+
+.. admonition:: Example
+
+ >>> np.is_busday(np.datetime64('2011-07-15')) # a Friday
+ True
+ >>> np.is_busday(np.datetime64('2011-07-16')) # a Saturday
+ False
+ >>> np.is_busday(np.datetime64('2011-07-16'), weekmask="Sat Sun")
+ True
+ >>> a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
+ >>> np.is_busday(a)
+ array([ True, True, True, True, True, False, False], dtype='bool')
+
+np.busday_count():
+``````````````````
+To find how many valid days there are in a specified range of datetime64
+dates, use :func:`busday_count`:
+
+.. admonition:: Example
+
+ >>> np.busday_count(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
+ 5
+ >>> np.busday_count(np.datetime64('2011-07-18'), np.datetime64('2011-07-11'))
+ -5
+
+If you have an array of datetime64 day values, and you want a count of
+how many of them are valid dates, you can do this:
+
+.. admonition:: Example
+
+ >>> a = np.arange(np.datetime64('2011-07-11'), np.datetime64('2011-07-18'))
+ >>> np.count_nonzero(np.is_busday(a))
+ 5
+
+
+
+Custom Weekmasks
+----------------
+
+Here are several examples of custom weekmask values. These examples
+specify the "busday" default of Monday through Friday being valid days.
+
+Some examples::
+
+ # Positional sequences; positions are Monday through Sunday.
+ # Length of the sequence must be exactly 7.
+ weekmask = [1, 1, 1, 1, 1, 0, 0]
+ # list or other sequence; 0 == invalid day, 1 == valid day
+ weekmask = "1111100"
+ # string '0' == invalid day, '1' == valid day
+
+ # string abbreviations from this list: Mon Tue Wed Thu Fri Sat Sun
+ weekmask = "Mon Tue Wed Thu Fri"
+ # any amount of whitespace is allowed; abbreviations are case-sensitive.
+ weekmask = "MonTue Wed Thu\tFri"
diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py
index 1e1d237a4..334bd8c4b 100644
--- a/numpy/add_newdocs.py
+++ b/numpy/add_newdocs.py
@@ -5993,44 +5993,52 @@ add_newdoc('numpy.core.multiarray', 'busdaycalendar',
"""
busdaycalendar(weekmask='1111100', holidays=None)
- A business day calendar object that efficiently stores
- information defining business days for the business
- day-related functions.
+ A business day calendar object that efficiently stores information
+ defining valid days for the busday family of functions.
+
+ The default valid days are Monday through Friday ("business days").
+ A busdaycalendar object can be specified with any set of weekly
+ valid days, plus an optional "holiday" dates that always will be invalid.
+
+ Once a busdaycalendar object is created, the weekmask and holidays
+ cannot be modified.
.. versionadded:: 1.7.0
Parameters
----------
weekmask : str or array_like of bool, optional
- A seven-element array indicating which of Monday through Sunday may
- be valid business days. May be specified as a list or array, like
- [1,1,1,1,1,0,0], a length-seven string like '1111100', or a string
- of three-letter weekday names, like 'MonTueWedThuFri'. The latter
- string representation is most useful when only one day of the
- week is important, like 'Mon' if you want to calculate the date
- of Easter.
+ A seven-element array indicating which of Monday through Sunday are
+ valid days. May be specified as a length-seven list or array, like
+ [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
+ like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
+ weekdays, optionally separated by white space. Valid abbreviations
+ are: Mon Tue Wed Thu Fri Sat Sun
holidays : array_like of datetime64[D], optional
- An array of dates which should be blacked out from being considered
- as business days. They may be specified in any order, and NaT
- (not-a-time) dates are ignored. Internally, this list is normalized
- into a form suited for fast business day calculations.
+ An array of dates to consider as invalid dates, no matter which
+ weekday they fall upon. Holiday dates may be specified in any
+ order, and NaT (not-a-time) dates are ignored. This list is
+ saved in a normalized form that is suited for fast calculations
+ of valid days.
Returns
-------
out : busdaycalendar
A business day calendar object containing the specified
- weekmask and holidays.
+ weekmask and holidays values.
See Also
--------
- is_busday : Returns a boolean array indicating valid business days.
- busday_offset : Applies an offset counted in business days.
- busday_count : Counts how many business days are in a half-open date range.
+ is_busday : Returns a boolean array indicating valid days.
+ busday_offset : Applies an offset counted in valid days.
+ busday_count : Counts how many valid days are in a half-open date range.
Attributes
----------
- weekmask : seven-element array of bool
- holidays : sorted array of datetime64[D]
+ Note: once a busdaycalendar object is created, you cannot modify the
+ weekmask or holidays. The attributes return copies of internal data.
+ weekmask : (copy) seven-element array of bool
+ holidays : (copy) sorted array of datetime64[D]
Examples
--------
@@ -6046,17 +6054,16 @@ add_newdoc('numpy.core.multiarray', 'busdaycalendar',
""")
add_newdoc('numpy.core.multiarray', 'busdaycalendar', ('weekmask',
- """A copy of the seven-element boolean mask indicating valid business days."""))
+ """A copy of the seven-element boolean mask indicating valid days."""))
add_newdoc('numpy.core.multiarray', 'busdaycalendar', ('holidays',
- """A copy of the holiday array indicating blacked out business days."""))
+ """A copy of the holiday array indicating additional invalid days."""))
add_newdoc('numpy.core.multiarray', 'is_busday',
"""
is_busday(dates, weekmask='1111100', holidays=None, busdaycal=None, out=None)
- Calculates which of the given dates are valid business days, and
- which are not.
+ Calculates which of the given dates are valid days, and which are not.
.. versionadded:: 1.7.0
@@ -6065,20 +6072,19 @@ add_newdoc('numpy.core.multiarray', 'is_busday',
dates : array_like of datetime64[D]
The array of dates to process.
weekmask : str or array_like of bool, optional
- A seven-element array indicating which of Monday through Sunday may
- be valid business days. May be specified as a list or array, like
- [1,1,1,1,1,0,0], a length-seven string like '1111100', or a string
- of three-letter weekday names, like 'MonTueWedThuFri'. The latter
- string representation is most useful when only one day of the
- week is important, like 'Mon' if you want to calculate the date
- of Easter.
+ A seven-element array indicating which of Monday through Sunday are
+ valid days. May be specified as a length-seven list or array, like
+ [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
+ like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
+ weekdays, optionally separated by white space. Valid abbreviations
+ are: Mon Tue Wed Thu Fri Sat Sun
holidays : array_like of datetime64[D], optional
- An array of dates which should be blacked out from being considered
- as business days. They may be specified in any order, and NaT
- (not-a-time) dates are ignored. Internally, this list is normalized
- into a form suited for fast business day calculations.
+ An array of dates to consider as invalid dates. They may be
+ specified in any order, and NaT (not-a-time) dates are ignored.
+ This list is saved in a normalized form that is suited for
+ fast calculations of valid days.
busdaycal : busdaycalendar, optional
- A `busdaycalendar` object which specifies the business days. If this
+ A `busdaycalendar` object which specifies the valid days. If this
parameter is provided, neither weekmask nor holidays may be
provided.
out : array of bool, optional
@@ -6088,13 +6094,13 @@ add_newdoc('numpy.core.multiarray', 'is_busday',
-------
out : array of bool
An array with the same shape as ``dates``, containing True for
- each valid business day, and False for the others.
+ each valid day, and False for each invalid day.
See Also
--------
- busdaycalendar: An object for efficiently specifying which are business days.
- busday_offset : Applies an offset counted in business days.
- busday_count : Counts how many business days are in a half-open date range.
+ busdaycalendar: An object that specifies a custom set of valid days.
+ busday_offset : Applies an offset counted in valid days.
+ busday_count : Counts how many valid days are in a half-open date range.
Examples
--------
@@ -6108,9 +6114,9 @@ add_newdoc('numpy.core.multiarray', 'busday_offset',
"""
busday_offset(dates, offsets, roll='raise', weekmask='1111100', holidays=None, busdaycal=None, out=None)
- First adjusts the date to fall on a business day according to
+ First adjusts the date to fall on a valid day according to
the ``roll`` rule, then applies offsets to the given dates
- counted in business days.
+ counted in valid days.
.. versionadded:: 1.7.0
@@ -6121,36 +6127,35 @@ add_newdoc('numpy.core.multiarray', 'busday_offset',
offsets : array_like of int
The array of offsets, which is broadcast with ``dates``.
roll : {'raise', 'nat', 'forward', 'following', 'backward', 'preceding', 'modifiedfollowing', 'modifiedpreceding'}, optional
- How to treat dates that do not fall on a business day. The default
+ How to treat dates that do not fall on a valid day. The default
is 'raise'.
- * 'raise' means to raise an exception for invalid business days.
- * 'nat' means to return a NaT (not-a-time) for invalid business days.
- * 'forward' and 'following' mean to take the first business day
+ * 'raise' means to raise an exception for an invalid day.
+ * 'nat' means to return a NaT (not-a-time) for an invalid day.
+ * 'forward' and 'following' mean to take the first valid day
later in time.
- * 'backward' and 'preceding' mean to take the first business day
+ * 'backward' and 'preceding' mean to take the first valid day
earlier in time.
- * 'modifiedfollowing' means to take the first business day
+ * 'modifiedfollowing' means to take the first valid day
later in time unless it is across a Month boundary, in which
- case to take the first business day earlier in time.
- * 'modifiedpreceding' means to take the first business day
+ case to take the first valid day earlier in time.
+ * 'modifiedpreceding' means to take the first valid day
earlier in time unless it is across a Month boundary, in which
- case to take the first business day later in time.
+ case to take the first valid day later in time.
weekmask : str or array_like of bool, optional
- A seven-element array indicating which of Monday through Sunday may
- be valid business days. May be specified as a list or array, like
- [1,1,1,1,1,0,0], a length-seven string like '1111100', or a string
- of three-letter weekday names, like 'MonTueWedThuFri'. The latter
- string representation is most useful when only one day of the
- week is important, like 'Mon' if you want to calculate the date
- of Easter.
+ A seven-element array indicating which of Monday through Sunday are
+ valid days. May be specified as a length-seven list or array, like
+ [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
+ like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
+ weekdays, optionally separated by white space. Valid abbreviations
+ are: Mon Tue Wed Thu Fri Sat Sun
holidays : array_like of datetime64[D], optional
- An array of dates which should be blacked out from being considered
- as business days. They may be specified in any order, and NaT
- (not-a-time) dates are ignored. Internally, this list is normalized
- into a form suited for fast business day calculations.
+ An array of dates to consider as invalid dates. They may be
+ specified in any order, and NaT (not-a-time) dates are ignored.
+ This list is saved in a normalized form that is suited for
+ fast calculations of valid days.
busdaycal : busdaycalendar, optional
- A `busdaycalendar` object which specifies the business days. If this
+ A `busdaycalendar` object which specifies the valid days. If this
parameter is provided, neither weekmask nor holidays may be
provided.
out : array of datetime64[D], optional
@@ -6164,9 +6169,9 @@ add_newdoc('numpy.core.multiarray', 'busday_offset',
See Also
--------
- busdaycalendar: An object for efficiently specifying which are business days.
- is_busday : Returns a boolean array indicating valid business days.
- busday_count : Counts how many business days are in a half-open date range.
+ busdaycalendar: An object that specifies a custom set of valid days.
+ is_busday : Returns a boolean array indicating valid days.
+ busday_count : Counts how many valid days are in a half-open date range.
Examples
--------
@@ -6199,9 +6204,12 @@ add_newdoc('numpy.core.multiarray', 'busday_count',
"""
busday_count(begindates, enddates, weekmask='1111100', holidays=[], busdaycal=None, out=None)
- Counts the number of business days between `begindates` and
+ Counts the number of valid days between `begindates` and
`enddates`, not including the day of `enddates`.
+ If ``enddates`` specifies a date value that is earlier than the
+ corresponding ``begindates`` date value, the count will be negative.
+
.. versionadded:: 1.7.0
Parameters
@@ -6212,20 +6220,19 @@ add_newdoc('numpy.core.multiarray', 'busday_count',
The array of the end dates for counting, which are excluded
from the count themselves.
weekmask : str or array_like of bool, optional
- A seven-element array indicating which of Monday through Sunday may
- be valid business days. May be specified as a list or array, like
- [1,1,1,1,1,0,0], a length-seven string like '1111100', or a string
- of three-letter weekday names, like 'MonTueWedThuFri'. The latter
- string representation is most useful when only one day of the
- week is important, like 'Mon' if you want to calculate the date
- of Easter.
+ A seven-element array indicating which of Monday through Sunday are
+ valid days. May be specified as a length-seven list or array, like
+ [1,1,1,1,1,0,0]; a length-seven string, like '1111100'; or a string
+ like "Mon Tue Wed Thu Fri", made up of 3-character abbreviations for
+ weekdays, optionally separated by white space. Valid abbreviations
+ are: Mon Tue Wed Thu Fri Sat Sun
holidays : array_like of datetime64[D], optional
- An array of dates which should be blacked out from being considered
- as business days. They may be specified in any order, and NaT
- (not-a-time) dates are ignored. Internally, this list is normalized
- into a form suited for fast business day calculations.
+ An array of dates to consider as invalid dates. They may be
+ specified in any order, and NaT (not-a-time) dates are ignored.
+ This list is saved in a normalized form that is suited for
+ fast calculations of valid days.
busdaycal : busdaycalendar, optional
- A `busdaycalendar` object which specifies the business days. If this
+ A `busdaycalendar` object which specifies the valid days. If this
parameter is provided, neither weekmask nor holidays may be
provided.
out : array of int, optional
@@ -6235,14 +6242,14 @@ add_newdoc('numpy.core.multiarray', 'busday_count',
-------
out : array of int
An array with a shape from broadcasting ``begindates`` and ``enddates``
- together, containing the number of business days between
+ together, containing the number of valid days between
the begin and end dates.
See Also
--------
- busdaycalendar: An object for efficiently specifying which are business days.
- is_busday : Returns a boolean array indicating valid business days.
- busday_offset : Applies an offset counted in business days.
+ busdaycalendar: An object that specifies a custom set of valid days.
+ is_busday : Returns a boolean array indicating valid days.
+ busday_offset : Applies an offset counted in valid days.
Examples
--------
diff --git a/numpy/core/_internal.py b/numpy/core/_internal.py
index 713687199..3d6702095 100644
--- a/numpy/core/_internal.py
+++ b/numpy/core/_internal.py
@@ -90,8 +90,11 @@ def _array_descr(descriptor):
else:
new = descriptor.metadata.copy()
# Eliminate any key related to internal implementation
- _ = new.pop(METADATA_DTSTR, None)
- return (descriptor.str, new)
+ new.pop(METADATA_DTSTR, None)
+ if new:
+ return (descriptor.str, new)
+ else:
+ return descriptor.str
else:
return (_array_descr(subdtype[0]), subdtype[1])
diff --git a/numpy/core/src/multiarray/datetime_busday.c b/numpy/core/src/multiarray/datetime_busday.c
index 7ad033248..5a0078b76 100644
--- a/numpy/core/src/multiarray/datetime_busday.c
+++ b/numpy/core/src/multiarray/datetime_busday.c
@@ -365,6 +365,7 @@ apply_business_day_count(npy_datetime date_begin, npy_datetime date_end,
{
npy_int64 count, whole_weeks;
int day_of_week = 0;
+ int swapped = 0;
/* If we get a NaT, raise an error */
if (date_begin == NPY_DATETIME_NAT || date_end == NPY_DATETIME_NAT) {
@@ -375,10 +376,16 @@ apply_business_day_count(npy_datetime date_begin, npy_datetime date_end,
}
/* Trivial empty date range */
- if (date_begin >= date_end) {
+ if (date_begin == date_end) {
*out = 0;
return 0;
}
+ else if (date_begin > date_end) {
+ npy_datetime tmp = date_begin;
+ date_begin = date_end;
+ date_end = tmp;
+ swapped = 1;
+ }
/* Remove any earlier holidays */
holidays_begin = find_earliest_holiday_on_or_after(date_begin,
@@ -411,6 +418,10 @@ apply_business_day_count(npy_datetime date_begin, npy_datetime date_end,
}
}
+ if (swapped) {
+ count = -count;
+ }
+
*out = count;
return 0;
}
@@ -563,6 +574,9 @@ finish:
* the end date. This is the low-level function which requires already
* cleaned input data.
*
+ * If dates_begin is before dates_end, the result is positive. If
+ * dates_begin is after dates_end, it is negative.
+ *
* dates_begin: An array of dates with 'datetime64[D]' data type.
* dates_end: An array of dates with 'datetime64[D]' data type.
* out: Either NULL, or an array with 'int64' data type
diff --git a/numpy/core/src/multiarray/datetime_busdaycal.c b/numpy/core/src/multiarray/datetime_busdaycal.c
index c406fac7b..1d047a547 100644
--- a/numpy/core/src/multiarray/datetime_busdaycal.c
+++ b/numpy/core/src/multiarray/datetime_busdaycal.c
@@ -46,6 +46,7 @@ PyArray_WeekMaskConverter(PyObject *weekmask_in, npy_bool *weekmask)
if (PyBytes_Check(obj)) {
char *str;
Py_ssize_t len;
+ int i;
if (PyBytes_AsStringAndSize(obj, &str, &len) < 0) {
Py_DECREF(obj);
@@ -54,7 +55,6 @@ PyArray_WeekMaskConverter(PyObject *weekmask_in, npy_bool *weekmask)
/* Length 7 is a string like "1111100" */
if (len == 7) {
- int i;
for (i = 0; i < 7; ++i) {
switch(str[i]) {
case '0':
@@ -64,70 +64,81 @@ PyArray_WeekMaskConverter(PyObject *weekmask_in, npy_bool *weekmask)
weekmask[i] = 1;
break;
default:
- goto invalid_weekmask_string;
+ goto general_weekmask_string;
}
}
goto finish;
}
- /* Length divisible by 3 is a string like "Mon" or "MonWedFri" */
- else if (len % 3 == 0) {
- int i;
- memset(weekmask, 0, 7);
- for (i = 0; i < len; i += 3) {
- switch (str[i]) {
- case 'M':
- if (str[i+1] == 'o' && str[i+2] == 'n') {
- weekmask[0] = 1;
- }
- else {
- goto invalid_weekmask_string;
- }
- break;
- case 'T':
- if (str[i+1] == 'u' && str[i+2] == 'e') {
- weekmask[1] = 1;
- }
- else if (str[i+1] == 'h' && str[i+2] == 'u') {
- weekmask[3] = 1;
- }
- else {
- goto invalid_weekmask_string;
- }
- break;
- case 'W':
- if (str[i+1] == 'e' && str[i+2] == 'd') {
- weekmask[2] = 1;
- }
- else {
- goto invalid_weekmask_string;
- }
- break;
- case 'F':
- if (str[i+1] == 'r' && str[i+2] == 'i') {
- weekmask[4] = 1;
- }
- else {
- goto invalid_weekmask_string;
- }
- break;
- case 'S':
- if (str[i+1] == 'a' && str[i+2] == 't') {
- weekmask[5] = 1;
- }
- else if (str[i+1] == 'u' && str[i+2] == 'n') {
- weekmask[6] = 1;
- }
- else {
- goto invalid_weekmask_string;
- }
- break;
- }
+
+general_weekmask_string:
+ /* a string like "SatSun" or "Mon Tue Wed" */
+ memset(weekmask, 0, 7);
+ for (i = 0; i < len; i += 3) {
+ while (isspace(str[i]))
+ ++i;
+
+ if (i == len) {
+ goto finish;
+ }
+ else if (i + 2 >= len) {
+ goto invalid_weekmask_string;
}
- goto finish;
+ switch (str[i]) {
+ case 'M':
+ if (str[i+1] == 'o' && str[i+2] == 'n') {
+ weekmask[0] = 1;
+ }
+ else {
+ goto invalid_weekmask_string;
+ }
+ break;
+ case 'T':
+ if (str[i+1] == 'u' && str[i+2] == 'e') {
+ weekmask[1] = 1;
+ }
+ else if (str[i+1] == 'h' && str[i+2] == 'u') {
+ weekmask[3] = 1;
+ }
+ else {
+ goto invalid_weekmask_string;
+ }
+ break;
+ case 'W':
+ if (str[i+1] == 'e' && str[i+2] == 'd') {
+ weekmask[2] = 1;
+ }
+ else {
+ goto invalid_weekmask_string;
+ }
+ break;
+ case 'F':
+ if (str[i+1] == 'r' && str[i+2] == 'i') {
+ weekmask[4] = 1;
+ }
+ else {
+ goto invalid_weekmask_string;
+ }
+ break;
+ case 'S':
+ if (str[i+1] == 'a' && str[i+2] == 't') {
+ weekmask[5] = 1;
+ }
+ else if (str[i+1] == 'u' && str[i+2] == 'n') {
+ weekmask[6] = 1;
+ }
+ else {
+ goto invalid_weekmask_string;
+ }
+ break;
+ default:
+ goto invalid_weekmask_string;
+ }
}
+ goto finish;
+
invalid_weekmask_string:
PyErr_Format(PyExc_ValueError,
"Invalid business day weekmask string \"%s\"",
diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py
index 82577c0fc..a624768b6 100644
--- a/numpy/core/tests/test_datetime.py
+++ b/numpy/core/tests/test_datetime.py
@@ -1440,8 +1440,28 @@ class TestDateTime(TestCase):
# Default M-F weekmask
assert_equal(bdd.weekmask, np.array([1,1,1,1,1,0,0], dtype='?'))
+ # Check string weekmask with varying whitespace.
+ bdd = np.busdaycalendar(weekmask="Sun TueWed Thu\tFri")
+ assert_equal(bdd.weekmask, np.array([0,1,1,1,1,0,1], dtype='?'))
+
+ # Check length 7 0/1 string
+ bdd = np.busdaycalendar(weekmask="0011001")
+ assert_equal(bdd.weekmask, np.array([0,0,1,1,0,0,1], dtype='?'))
+
+ # Check length 7 string weekmask.
+ bdd = np.busdaycalendar(weekmask="Mon Tue")
+ assert_equal(bdd.weekmask, np.array([1,1,0,0,0,0,0], dtype='?'))
+
# All-zeros weekmask should raise
assert_raises(ValueError, np.busdaycalendar, weekmask=[0,0,0,0,0,0,0])
+ # weekday names must be correct case
+ assert_raises(ValueError, np.busdaycalendar, weekmask="satsun")
+ # All-zeros weekmask should raise
+ assert_raises(ValueError, np.busdaycalendar, weekmask="")
+ # Invalid weekday name codes should raise
+ assert_raises(ValueError, np.busdaycalendar, weekmask="Mon Tue We")
+ assert_raises(ValueError, np.busdaycalendar, weekmask="Max")
+ assert_raises(ValueError, np.busdaycalendar, weekmask="Monday Tue")
def test_datetime_busday_holidays_offset(self):
# With exactly one holiday
@@ -1624,11 +1644,17 @@ class TestDateTime(TestCase):
roll='forward', busdaycal=bdd)
assert_equal(np.busday_count('2011-01-01', dates, busdaycal=bdd),
np.arange(366))
+ # Returns negative value when reversed
+ assert_equal(np.busday_count(dates, '2011-01-01', busdaycal=bdd),
+ -np.arange(366))
dates = np.busday_offset('2011-12-31', -np.arange(366),
roll='forward', busdaycal=bdd)
assert_equal(np.busday_count(dates, '2011-12-31', busdaycal=bdd),
np.arange(366))
+ # Returns negative value when reversed
+ assert_equal(np.busday_count('2011-12-31', dates, busdaycal=bdd),
+ -np.arange(366))
# Can't supply both a weekmask/holidays and busdaycal
assert_raises(ValueError, np.busday_offset, '2012-01-03', '2012-02-03',
@@ -1638,6 +1664,8 @@ class TestDateTime(TestCase):
# Number of Mondays in March 2011
assert_equal(np.busday_count('2011-03', '2011-04', weekmask='Mon'), 4)
+ # Returns negative value when reversed
+ assert_equal(np.busday_count('2011-04', '2011-03', weekmask='Mon'), -4)
def test_datetime_is_busday(self):
holidays=['2011-01-01', '2011-10-10', '2011-11-11', '2011-11-24',
diff --git a/numpy/lib/_iotools.py b/numpy/lib/_iotools.py
index 27c1e76db..7921b4116 100644
--- a/numpy/lib/_iotools.py
+++ b/numpy/lib/_iotools.py
@@ -503,10 +503,25 @@ class StringConverter(object):
(_defaulttype, _defaultfunc, _defaultfill) = zip(*_mapper)
#
@classmethod
+ def _getdtype(cls, val):
+ """Returns the dtype of the input variable."""
+ return np.array(val).dtype
+ #
+ @classmethod
def _getsubdtype(cls, val):
"""Returns the type of the dtype of the input variable."""
return np.array(val).dtype.type
#
+ # This is a bit annoying. We want to return the "general" type in most cases
+ # (ie. "string" rather than "S10"), but we want to return the specific type
+ # for datetime64 (ie. "datetime64[us]" rather than "datetime64").
+ @classmethod
+ def _dtypeortype(cls, dtype):
+ """Returns dtype for datetime64 and type of dtype otherwise."""
+ if dtype.type == np.datetime64:
+ return dtype
+ return dtype.type
+ #
@classmethod
def upgrade_mapper(cls, func, default=None):
"""
@@ -561,12 +576,12 @@ class StringConverter(object):
self.func = str2bool
self._status = 0
self.default = default or False
- ttype = np.bool
+ dtype = np.dtype('bool')
else:
# Is the input a np.dtype ?
try:
self.func = None
- ttype = np.dtype(dtype_or_func).type
+ dtype = np.dtype(dtype_or_func)
except TypeError:
# dtype_or_func must be a function, then
if not hasattr(dtype_or_func, '__call__'):
@@ -581,11 +596,11 @@ class StringConverter(object):
default = self.func(asbytes('0'))
except ValueError:
default = None
- ttype = self._getsubdtype(default)
+ dtype = self._getdtype(default)
# Set the status according to the dtype
_status = -1
for (i, (deftype, func, default_def)) in enumerate(self._mapper):
- if np.issubdtype(ttype, deftype):
+ if np.issubdtype(dtype.type, deftype):
_status = i
if default is None:
self.default = default_def
@@ -603,9 +618,9 @@ class StringConverter(object):
# If the status is 1 (int), change the function to
# something more robust.
if self.func == self._mapper[1][1]:
- if issubclass(ttype, np.uint64):
+ if issubclass(dtype.type, np.uint64):
self.func = np.uint64
- elif issubclass(ttype, np.int64):
+ elif issubclass(dtype.type, np.int64):
self.func = np.int64
else:
self.func = lambda x : int(float(x))
@@ -618,7 +633,7 @@ class StringConverter(object):
self.missing_values = set(list(missing_values) + [asbytes('')])
#
self._callingfunction = self._strict_call
- self.type = ttype
+ self.type = self._dtypeortype(dtype)
self._checked = False
self._initial_default = default
#
@@ -747,13 +762,13 @@ class StringConverter(object):
# Don't reset the default to None if we can avoid it
if default is not None:
self.default = default
- self.type = self._getsubdtype(default)
+ self.type = self._dtypeortype(self._getdtype(default))
else:
try:
tester = func(testing_value or asbytes('1'))
except (TypeError, ValueError):
tester = None
- self.type = self._getsubdtype(tester)
+ self.type = self._dtypeortype(self._getdtype(tester))
# Add the missing values to the existing set
if missing_values is not None:
if _is_bytes_like(missing_values):
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index d20304a1b..8a0c0b37d 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -335,11 +335,11 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None):
Found bin edge of size <= 0. Did you specify `bins` with
non-monotonic sequence?""")
+ nbin = asarray(nbin)
+
# Handle empty input.
if N == 0:
- return np.zeros(D), edges
-
- nbin = asarray(nbin)
+ return np.zeros(nbin-2), edges
# Compute the bin number each sample falls into.
Ncount = {}
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index 9df1a916f..105389d6d 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -749,7 +749,10 @@ class TestHistogramdd(TestCase):
def test_empty(self):
a, b = histogramdd([[], []], bins=([0,1], [0,1]))
- assert_array_max_ulp(a, array([ 0., 0.]))
+ assert_array_max_ulp(a, array([[ 0.]]))
+ a, b = np.histogramdd([[], [], []], bins=2)
+ assert_array_max_ulp(a, np.zeros((2, 2, 2)))
+
def test_bins_errors(self):
"""There are two ways to specify bins. Check for the right errors when
diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py
index e83c82ecd..f9da258dc 100644
--- a/numpy/lib/tests/test_io.py
+++ b/numpy/lib/tests/test_io.py
@@ -776,6 +776,15 @@ M 33 21.99
dtype=[('date', np.object_), ('stid', float)])
assert_equal(test, control)
+ def test_converters_cornercases2(self):
+ "Test the conversion to datetime64."
+ converter = {'date': lambda s: np.datetime64(strptime(s, '%Y-%m-%d %H:%M:%SZ'))}
+ data = StringIO('2009-02-03 12:00:00Z, 72214.0')
+ test = np.ndfromtxt(data, delimiter=',', dtype=None,
+ names=['date', 'stid'], converters=converter)
+ control = np.array((datetime(2009, 02, 03), 72214.),
+ dtype=[('date', 'datetime64[us]'), ('stid', float)])
+ assert_equal(test, control)
def test_unused_converter(self):
"Test whether unused converters are forgotten"
diff --git a/numpy/lib/tests/test_twodim_base.py b/numpy/lib/tests/test_twodim_base.py
index a7c0e85c8..37165f672 100644
--- a/numpy/lib/tests/test_twodim_base.py
+++ b/numpy/lib/tests/test_twodim_base.py
@@ -224,7 +224,10 @@ class TestHistogram2d(TestCase):
def test_empty(self):
a, edge1, edge2 = histogram2d([],[], bins=([0,1],[0,1]))
- assert_array_max_ulp(a, array([ 0., 0.]))
+ assert_array_max_ulp(a, array([[ 0.]]))
+
+ a, edge1, edge2 = histogram2d([], [], bins=4)
+ assert_array_max_ulp(a, np.zeros((4, 4)))
class TestTri(TestCase):
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index 4c40a7c30..e2e954a97 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -143,6 +143,8 @@ default_filler = {'b': True,
'u' : 999999,
'V' : '???',
'U' : 'N/A',
+ 'M8[D]' : np.datetime64('NaT', 'D'),
+ 'M8[us]' : np.datetime64('NaT', 'us')
}
max_filler = ntypes._minvals
max_filler.update([(k, -np.inf) for k in [np.float32, np.float64]])
@@ -198,6 +200,8 @@ def default_fill_value(obj):
elif isinstance(obj, np.dtype):
if obj.subdtype:
defval = default_filler.get(obj.subdtype[0].kind, '?')
+ elif obj.kind == 'M':
+ defval = default_filler.get(obj.str[1:], '?')
else:
defval = default_filler.get(obj.kind, '?')
elif isinstance(obj, float):
diff --git a/pavement.py b/pavement.py
index c5a52b455..afb177279 100644
--- a/pavement.py
+++ b/pavement.py
@@ -101,7 +101,7 @@ finally:
RELEASE_NOTES = 'doc/release/2.0.0-notes.rst'
# Start/end of the log (from git)
-LOG_START = 'svn/tags/1.5.0'
+LOG_START = 'v1.6.0'
LOG_END = 'master'
@@ -398,8 +398,22 @@ def pdf():
#------------------
# Mac OS X targets
#------------------
-def dmg_name(fullversion, pyver):
- return "numpy-%s-py%s-python.org.dmg" % (fullversion, pyver)
+def dmg_name(fullversion, pyver, osxver=None):
+ """Return name for dmg installer.
+
+ Notes
+ -----
+ Python 2.7 has two binaries, one for 10.3 (ppc, i386) and one for 10.6
+ (i386, x86_64). All other Python versions at python.org at the moment
+ have binaries for 10.3 only. The "macosx%s" part of the dmg name should
+ correspond to the python.org naming scheme.
+ """
+ # assume that for the py2.7/osx10.6 build the deployment target is set
+ # (should be done in the release script).
+ if not osxver:
+ osxver = os.environ.get('MACOSX_DEPLOYMENT_TARGET', '10.3')
+ return "numpy-%s-py%s-python.org-macosx%s.dmg" % (fullversion, pyver,
+ osxver)
def macosx_version():
if not sys.platform == 'darwin':
@@ -580,7 +594,7 @@ Checksums
def write_log_task(options, filename='Changelog'):
st = subprocess.Popen(
- ['git', 'svn', 'log', '%s..%s' % (LOG_START, LOG_END)],
+ ['git', 'log', '%s..%s' % (LOG_START, LOG_END)],
stdout=subprocess.PIPE)
out = st.communicate()[0]
diff --git a/release.sh b/release.sh
index bb5a375db..5f1f31ebb 100644
--- a/release.sh
+++ b/release.sh
@@ -5,6 +5,17 @@
# downloads, i.e. two versions for Python 2.7. The Intel 32/64-bit version is
# for OS X 10.6+, the other dmg installers are for 10.3+ and are built on 10.5
+# Check we're using the correct g++/c++ for the 32-bit 2.6 version we build for
+# the docs and the 64-bit 2.7 dmg installer.
+# We do this because for Python 2.6 we use a symlink on the PATH to select
+# /usr/bin/g++-4.0, while for Python 2.7 we need the default 4.2 version.
+export PATH=~/Code/tmp/gpp40temp/:$PATH
+gpp="$(g++ --version | grep "4.0")"
+if [ -z "$gpp" ]; then
+ echo "Wrong g++ version, we need 4.0 to compile scipy with Python 2.6"
+ exit 1
+fi
+
# bootstrap needed to ensure we build the docs from the right scipy version
paver bootstrap
source bootstrap/bin/activate
@@ -19,6 +30,14 @@ paver pdf
paver sdist
export MACOSX_DEPLOYMENT_TARGET=10.6
+# Use GCC 4.2 for 64-bit OS X installer for Python 2.7
+export PATH=~/Code/tmp/gpp42temp/:$PATH
+gpp="$(g++ --version | grep "4.2")"
+if [ -z "$gpp" ]; then
+ echo "Wrong g++ version, we need 4.2 for 64-bit binary for Python 2.7"
+ exit 1
+fi
+
paver dmg -p 2.7 # 32/64-bit version
paver bdist_superpack -p 3.2