diff --git a/ci/azure-windows-36.yaml b/ci/azure-windows-36.yaml index 6230e9b6a1885..656a6a31d92b4 100644 --- a/ci/azure-windows-36.yaml +++ b/ci/azure-windows-36.yaml @@ -5,12 +5,14 @@ channels: dependencies: - blosc - bottleneck + - boost-cpp<1.67 - fastparquet - feather-format - matplotlib - numexpr - numpy=1.14* - openpyxl=2.5.5 + - parquet-cpp - pyarrow - pytables - python-dateutil diff --git a/pandas/_libs/src/headers/cmath b/pandas/_libs/src/headers/cmath index 2bccf9bb13d77..632e1fc2390d0 100644 --- a/pandas/_libs/src/headers/cmath +++ b/pandas/_libs/src/headers/cmath @@ -1,16 +1,36 @@ #ifndef _PANDAS_MATH_H_ #define _PANDAS_MATH_H_ +// MSVC 2017 has a bug where `x == x` can be true for NaNs. +// MSC_VER from https://stackoverflow.com/a/70630/1889400 +// Place upper bound on this check once a fixed MSVC is released. +#if defined(_MSC_VER) && (_MSC_VER < 1800) +#include // In older versions of Visual Studio there wasn't a std::signbit defined // This defines it using _copysign -#if defined(_MSC_VER) && (_MSC_VER < 1800) +namespace std { + __inline int isnan(double x) { return _isnan(x); } + __inline int signbit(double num) { return _copysign(1.0, num) < 0; } + __inline int notnan(double x) { return !isnan(x); } +} +#elif defined(_MSC_VER) && (_MSC_VER >= 1900) +#include +namespace std { + __inline int isnan(double x) { return _isnan(x); } + __inline int notnan(double x) { return !isnan(x); } +} +#elif defined(_MSC_VER) #include namespace std { __inline int isnan(double x) { return _isnan(x); } - __inline int signbit(double num) { return _copysign(1.0, num) < 0; } + __inline int notnan(double x) { return x == x; } } #else #include -#endif +namespace std { + __inline int notnan(double x) { return x == x; } +} + +#endif #endif diff --git a/pandas/_libs/window.pyx b/pandas/_libs/window.pyx index d4b61b8611b68..989dc4dd17a37 100644 --- a/pandas/_libs/window.pyx +++ b/pandas/_libs/window.pyx @@ -15,6 +15,7 @@ cnp.import_array() cdef extern from "src/headers/cmath" namespace "std": bint isnan(double) nogil + bint notnan(double) nogil int signbit(double) nogil double sqrt(double x) nogil @@ -381,7 +382,7 @@ def roll_count(ndarray[double_t] input, int64_t win, int64_t minp, count_x = 0.0 for j in range(s, e): val = input[j] - if val == val: + if notnan(val): count_x += 1.0 else: @@ -389,13 +390,13 @@ def roll_count(ndarray[double_t] input, int64_t win, int64_t minp, # calculate deletes for j in range(start[i - 1], s): val = input[j] - if val == val: + if notnan(val): count_x -= 1.0 # calculate adds for j in range(end[i - 1], e): val = input[j] - if val == val: + if notnan(val): count_x += 1.0 if count_x >= minp: @@ -424,7 +425,7 @@ cdef inline void add_sum(double val, int64_t *nobs, double *sum_x) nogil: """ add a value from the sum calc """ # Not NaN - if val == val: + if notnan(val): nobs[0] = nobs[0] + 1 sum_x[0] = sum_x[0] + val @@ -432,7 +433,7 @@ cdef inline void add_sum(double val, int64_t *nobs, double *sum_x) nogil: cdef inline void remove_sum(double val, int64_t *nobs, double *sum_x) nogil: """ remove a value from the sum calc """ - if val == val: + if notnan(val): nobs[0] = nobs[0] - 1 sum_x[0] = sum_x[0] - val @@ -538,7 +539,7 @@ cdef inline void add_mean(double val, Py_ssize_t *nobs, double *sum_x, """ add a value from the mean calc """ # Not NaN - if val == val: + if notnan(val): nobs[0] = nobs[0] + 1 sum_x[0] = sum_x[0] + val if signbit(val): @@ -549,7 +550,7 @@ cdef inline void remove_mean(double val, Py_ssize_t *nobs, double *sum_x, Py_ssize_t *neg_ct) nogil: """ remove a value from the mean calc """ - if val == val: + if notnan(val): nobs[0] = nobs[0] - 1 sum_x[0] = sum_x[0] - val if signbit(val): @@ -671,8 +672,7 @@ cdef inline void remove_var(double val, double *nobs, double *mean_x, """ remove a value from the var calc """ cdef double delta - # Not NaN - if val == val: + if notnan(val): nobs[0] = nobs[0] - 1 if nobs[0]: # a part of Welford's method for the online variance-calculation @@ -760,7 +760,7 @@ def roll_var(ndarray[double_t] input, int64_t win, int64_t minp, val = input[i] prev = input[i - win] - if val == val: + if notnan(val): if prev == prev: # Adding one observation and removing another one @@ -822,7 +822,7 @@ cdef inline void add_skew(double val, int64_t *nobs, double *x, double *xx, """ add a value from the skew calc """ # Not NaN - if val == val: + if notnan(val): nobs[0] = nobs[0] + 1 # seriously don't ask me why this is faster @@ -836,7 +836,7 @@ cdef inline void remove_skew(double val, int64_t *nobs, double *x, double *xx, """ remove a value from the skew calc """ # Not NaN - if val == val: + if notnan(val): nobs[0] = nobs[0] - 1 # seriously don't ask me why this is faster @@ -959,7 +959,7 @@ cdef inline void add_kurt(double val, int64_t *nobs, double *x, double *xx, """ add a value from the kurotic calc """ # Not NaN - if val == val: + if notnan(val): nobs[0] = nobs[0] + 1 # seriously don't ask me why this is faster @@ -974,7 +974,7 @@ cdef inline void remove_kurt(double val, int64_t *nobs, double *x, double *xx, """ remove a value from the kurotic calc """ # Not NaN - if val == val: + if notnan(val): nobs[0] = nobs[0] - 1 # seriously don't ask me why this is faster @@ -1089,7 +1089,7 @@ def roll_median_c(ndarray[float64_t] input, int64_t win, int64_t minp, # setup val = input[i] - if val == val: + if notnan(val): nobs += 1 err = skiplist_insert(sl, val) != 1 if err: @@ -1100,14 +1100,14 @@ def roll_median_c(ndarray[float64_t] input, int64_t win, int64_t minp, # calculate deletes for j in range(start[i - 1], s): val = input[j] - if val == val: + if notnan(val): skiplist_remove(sl, val) nobs -= 1 # calculate adds for j in range(end[i - 1], e): val = input[j] - if val == val: + if notnan(val): nobs += 1 err = skiplist_insert(sl, val) != 1 if err: @@ -1472,7 +1472,7 @@ def roll_quantile(ndarray[float64_t, cast=True] input, int64_t win, # setup val = input[i] - if val == val: + if notnan(val): nobs += 1 skiplist_insert(skiplist, val) @@ -1481,14 +1481,14 @@ def roll_quantile(ndarray[float64_t, cast=True] input, int64_t win, # calculate deletes for j in range(start[i - 1], s): val = input[j] - if val == val: + if notnan(val): skiplist_remove(skiplist, val) nobs -= 1 # calculate adds for j in range(end[i - 1], e): val = input[j] - if val == val: + if notnan(val): nobs += 1 skiplist_insert(skiplist, val)