NumCpp  1.0
A C++ implementation of the Python Numpy library
nanpercentile.hpp
Go to the documentation of this file.
1 #pragma once
30 
31 #include "NumCpp/NdArray.hpp"
32 #include "NumCpp/Core/Shape.hpp"
33 #include "NumCpp/Core/Types.hpp"
41 
42 #include <algorithm>
43 #include <cmath>
44 #include <string>
45 #include <vector>
46 
47 namespace nc
48 {
49  //============================================================================
50  // Method Description:
62  template<typename dtype>
63  NdArray<dtype> nanpercentile(const NdArray<dtype>& inArray, double inPercentile,
64  Axis inAxis = Axis::NONE, const std::string& inInterpMethod = "linear")
65  {
66  STATIC_ASSERT_FLOAT(dtype);
67 
68  if (inPercentile < 0.0 || inPercentile > 100.0)
69  {
70  THROW_INVALID_ARGUMENT_ERROR("input percentile value must be of the range [0, 100].");
71  }
72 
73  if (inInterpMethod.compare("linear") != 0 &&
74  inInterpMethod.compare("lower") != 0 &&
75  inInterpMethod.compare("higher") != 0 &&
76  inInterpMethod.compare("nearest") != 0 &&
77  inInterpMethod.compare("midpoint") != 0)
78  {
79  std::string errStr = "input interpolation method is not a vaid option.\n";
80  errStr += "\tValid options are 'linear', 'lower', 'higher', 'nearest', 'midpoint'.";
82  }
83 
84  switch (inAxis)
85  {
86  case Axis::NONE:
87  {
88  if (utils::essentiallyEqual(inPercentile, 0.0))
89  {
90  for (auto value : inArray)
91  {
92  if (!isnan(value))
93  {
94  NdArray<dtype> returnArray = { value };
95  return returnArray;
96  }
97  }
98  return NdArray<dtype>(0);
99  }
100  else if (utils::essentiallyEqual(inPercentile, 100.0))
101  {
102  for (int32 i = static_cast<int32>(inArray.size()) - 1; i > -1; --i)
103  {
104  if (!isnan(inArray[i]))
105  {
106  NdArray<dtype> returnArray = { inArray[i] };
107  return returnArray;
108  }
109  }
110  return NdArray<dtype>(0);
111  }
112 
113  std::vector<double> arrayCopy;
114  uint32 numNonNan = 0;
115  for (auto value : inArray)
116  {
117  if (!isnan(value))
118  {
119  arrayCopy.push_back(value);
120  ++numNonNan;
121  }
122  }
123 
124  if (arrayCopy.size() < 2)
125  {
126  return NdArray<dtype>(0);
127  }
128 
129  const int32 i = static_cast<int32>(std::floor(static_cast<double>(numNonNan - 1) * inPercentile / 100.0));
130  const uint32 indexLower = static_cast<uint32>(clip<uint32>(i, 0, numNonNan - 2));
131 
132  stl_algorithms::sort(arrayCopy.begin(), arrayCopy.end());
133 
134  if (inInterpMethod.compare("linear") == 0)
135  {
136  const double percentI = static_cast<double>(indexLower) / static_cast<double>(numNonNan - 1);
137  const double fraction = (inPercentile / 100.0 - percentI) /
138  (static_cast<double>(indexLower + 1) / static_cast<double>(numNonNan - 1) - percentI);
139 
140  const double returnValue = arrayCopy[indexLower] + (arrayCopy[indexLower + 1] - arrayCopy[indexLower]) * fraction;
141  NdArray<dtype> returnArray = { returnValue };
142  return returnArray;
143  }
144  else if (inInterpMethod.compare("lower") == 0)
145  {
146  NdArray<dtype> returnArray = { arrayCopy[indexLower] };
147  return returnArray;
148  }
149  else if (inInterpMethod.compare("higher") == 0)
150  {
151  NdArray<dtype> returnArray = { arrayCopy[indexLower + 1] };
152  return returnArray;
153  }
154  else if (inInterpMethod.compare("nearest") == 0)
155  {
156  const double percent = inPercentile / 100.0;
157  const double percent1 = static_cast<double>(indexLower) / static_cast<double>(numNonNan - 1);
158  const double percent2 = static_cast<double>(indexLower + 1) / static_cast<double>(numNonNan - 1);
159  const double diff1 = percent - percent1;
160  const double diff2 = percent2 - percent;
161 
162  switch (argmin<double>({ diff1, diff2 }).item())
163  {
164  case 0:
165  {
166  NdArray<dtype> returnArray = { arrayCopy[indexLower] };
167  return returnArray;
168  }
169  case 1:
170  {
171  NdArray<dtype> returnArray = { arrayCopy[indexLower + 1] };
172  return returnArray;
173  }
174  }
175  }
176  else if (inInterpMethod.compare("midpoint") == 0)
177  {
178  NdArray<dtype> returnArray = { (arrayCopy[indexLower] + arrayCopy[indexLower + 1]) / 2.0 };
179  return returnArray;
180  }
181  else
182  {
183  THROW_INVALID_ARGUMENT_ERROR("intperpolation method has not been implemented: " + inInterpMethod);
184  }
185 
186  // this isn't actually possible, just putting this here to get rid
187  // of the compiler warning.
188  return NdArray<dtype>(0);
189  }
190  case Axis::COL:
191  {
192  const Shape inShape = inArray.shape();
193 
194  NdArray<dtype> returnArray(1, inShape.rows);
195  for (uint32 row = 0; row < inShape.rows; ++row)
196  {
197  NdArray<dtype> outValue = nanpercentile(NdArray<dtype>(inArray.cbegin(row), inArray.cend(row)),
198  inPercentile, Axis::NONE, inInterpMethod);
199 
200  if (outValue.size() == 1)
201  {
202  returnArray[row] = outValue.item();
203  }
204  else
205  {
206  returnArray[row] = constants::nan;
207  }
208  }
209 
210  return returnArray;
211  }
212  case Axis::ROW:
213  {
214  NdArray<dtype> arrayTrans = inArray.transpose();
215  const Shape inShape = arrayTrans.shape();
216 
217  NdArray<dtype> returnArray(1, inShape.rows);
218  for (uint32 row = 0; row < inShape.rows; ++row)
219  {
220  NdArray<dtype> outValue = nanpercentile(NdArray<dtype>(arrayTrans.cbegin(row), arrayTrans.cend(row)),
221  inPercentile, Axis::NONE, inInterpMethod);
222 
223  if (outValue.size() == 1)
224  {
225  returnArray[row] = outValue.item();
226  }
227  else
228  {
229  returnArray[row] = constants::nan;
230  }
231  }
232 
233  return returnArray;
234  }
235  default:
236  {
237  // this isn't actually possible, just putting this here to get rid
238  // of the compiler warning.
239  return NdArray<dtype>(0);
240  }
241  }
242  }
243 }
StaticAsserts.hpp
nc::NdArray::item
value_type item() const
Definition: NdArrayCore.hpp:2950
nc::NdArray::shape
Shape shape() const noexcept
Definition: NdArrayCore.hpp:4296
nc::Axis::NONE
@ NONE
nc::int32
std::int32_t int32
Definition: Types.hpp:37
Error.hpp
nc::Axis::ROW
@ ROW
argmin.hpp
nc::nanpercentile
NdArray< dtype > nanpercentile(const NdArray< dtype > &inArray, double inPercentile, Axis inAxis=Axis::NONE, const std::string &inInterpMethod="linear")
Definition: nanpercentile.hpp:63
nc::utils::essentiallyEqual
bool essentiallyEqual(dtype inValue1, dtype inValue2) noexcept
Definition: essentiallyEqual.hpp:53
clip.hpp
nc::NdArray::transpose
NdArray< dtype > transpose() const
Definition: NdArrayCore.hpp:4591
nc::NdArray< dtype >
nc::uint32
std::uint32_t uint32
Definition: Types.hpp:41
nc::floor
dtype floor(dtype inValue) noexcept
Definition: floor.hpp:49
NdArray.hpp
nc::Shape
A Shape Class for NdArrays.
Definition: Core/Shape.hpp:41
STATIC_ASSERT_FLOAT
#define STATIC_ASSERT_FLOAT(dtype)
Definition: StaticAsserts.hpp:44
isnan.hpp
nc::NdArray::size
size_type size() const noexcept
Definition: NdArrayCore.hpp:4310
nc::NdArray::cend
const_iterator cend() const noexcept
Definition: NdArrayCore.hpp:1491
nc::Axis
Axis
Enum To describe an axis.
Definition: Types.hpp:47
nc::constants::nan
const double nan
NaN.
Definition: Constants.hpp:45
Shape.hpp
nc
Definition: Coordinate.hpp:45
nc::Shape::rows
uint32 rows
Definition: Core/Shape.hpp:45
essentiallyEqual.hpp
THROW_INVALID_ARGUMENT_ERROR
#define THROW_INVALID_ARGUMENT_ERROR(msg)
Definition: Error.hpp:37
nc::NdArray::cbegin
const_iterator cbegin() const noexcept
Definition: NdArrayCore.hpp:1147
nc::stl_algorithms::sort
void sort(RandomIt first, RandomIt last) noexcept
Definition: StlAlgorithms.hpp:630
StlAlgorithms.hpp
Types.hpp
nc::Axis::COL
@ COL
nc::isnan
bool isnan(dtype inValue) noexcept
Definition: isnan.hpp:52