NumCpp  2.1.0
A C++ implementation of the Python Numpy library
nanpercentile.hpp
Go to the documentation of this file.
1 #pragma once
30 
34 #include "NumCpp/Core/Shape.hpp"
35 #include "NumCpp/Core/Types.hpp"
39 #include "NumCpp/NdArray.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 != "linear" &&
74  inInterpMethod != "lower" &&
75  inInterpMethod != "higher" &&
76  inInterpMethod != "nearest" &&
77  inInterpMethod != "midpoint")
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 
101  if (utils::essentiallyEqual(inPercentile, 100.0))
102  {
103  for (int32 i = static_cast<int32>(inArray.size()) - 1; i > -1; --i)
104  {
105  if (!isnan(inArray[i]))
106  {
107  NdArray<dtype> returnArray = { inArray[i] };
108  return returnArray;
109  }
110  }
111  return NdArray<dtype>(0);
112  }
113 
114  std::vector<double> arrayCopy;
115  uint32 numNonNan = 0;
116  for (auto value : inArray)
117  {
118  if (!isnan(value))
119  {
120  arrayCopy.push_back(value);
121  ++numNonNan;
122  }
123  }
124 
125  if (arrayCopy.size() < 2)
126  {
127  return NdArray<dtype>(0);
128  }
129 
130  const auto i = static_cast<int32>(std::floor(static_cast<double>(numNonNan - 1) * inPercentile / 100.0));
131  const auto indexLower = static_cast<uint32>(clip<uint32>(i, 0, numNonNan - 2));
132 
133  stl_algorithms::sort(arrayCopy.begin(), arrayCopy.end());
134 
135  if (inInterpMethod == "linear")
136  {
137  const double percentI = static_cast<double>(indexLower) / static_cast<double>(numNonNan - 1);
138  const double fraction = (inPercentile / 100.0 - percentI) /
139  (static_cast<double>(indexLower + 1) / static_cast<double>(numNonNan - 1) - percentI);
140 
141  const double returnValue = arrayCopy[indexLower] + (arrayCopy[indexLower + 1] - arrayCopy[indexLower]) * fraction;
142  NdArray<dtype> returnArray = { returnValue };
143  return returnArray;
144  }
145 
146  if (inInterpMethod == "lower")
147  {
148  NdArray<dtype> returnArray = { arrayCopy[indexLower] };
149  return returnArray;
150  }
151 
152  if (inInterpMethod == "higher")
153  {
154  NdArray<dtype> returnArray = { arrayCopy[indexLower + 1] };
155  return returnArray;
156  }
157 
158  if (inInterpMethod == "nearest")
159  {
160  const double percent = inPercentile / 100.0;
161  const double percent1 = static_cast<double>(indexLower) / static_cast<double>(numNonNan - 1);
162  const double percent2 = static_cast<double>(indexLower + 1) / static_cast<double>(numNonNan - 1);
163  const double diff1 = percent - percent1;
164  const double diff2 = percent2 - percent;
165 
166  switch (argmin<double>({ diff1, diff2 }).item())
167  {
168  case 0:
169  {
170  NdArray<dtype> returnArray = { arrayCopy[indexLower] };
171  return returnArray;
172  }
173  case 1:
174  {
175  NdArray<dtype> returnArray = { arrayCopy[indexLower + 1] };
176  return returnArray;
177  }
178  }
179  }
180 
181  if (inInterpMethod == "midpoint")
182  {
183  NdArray<dtype> returnArray = { (arrayCopy[indexLower] + arrayCopy[indexLower + 1]) / 2.0 };
184  return returnArray;
185  }
186 
187  THROW_INVALID_ARGUMENT_ERROR("intperpolation method has not been implemented: " + inInterpMethod);
188 
189  // this isn't actually possible, just putting this here to get rid
190  // of the compiler warning.
191  return NdArray<dtype>(0);
192  }
193  case Axis::COL:
194  {
195  const Shape inShape = inArray.shape();
196 
197  NdArray<dtype> returnArray(1, inShape.rows);
198  for (uint32 row = 0; row < inShape.rows; ++row)
199  {
200  NdArray<dtype> outValue = nanpercentile(NdArray<dtype>(inArray.cbegin(row), inArray.cend(row)),
201  inPercentile, Axis::NONE, inInterpMethod);
202 
203  if (outValue.size() == 1)
204  {
205  returnArray[row] = outValue.item();
206  }
207  else
208  {
209  returnArray[row] = constants::nan;
210  }
211  }
212 
213  return returnArray;
214  }
215  case Axis::ROW:
216  {
217  NdArray<dtype> arrayTrans = inArray.transpose();
218  const Shape inShape = arrayTrans.shape();
219 
220  NdArray<dtype> returnArray(1, inShape.rows);
221  for (uint32 row = 0; row < inShape.rows; ++row)
222  {
223  NdArray<dtype> outValue = nanpercentile(NdArray<dtype>(arrayTrans.cbegin(row), arrayTrans.cend(row)),
224  inPercentile, Axis::NONE, inInterpMethod);
225 
226  if (outValue.size() == 1)
227  {
228  returnArray[row] = outValue.item();
229  }
230  else
231  {
232  returnArray[row] = constants::nan;
233  }
234  }
235 
236  return returnArray;
237  }
238  default:
239  {
240  THROW_INVALID_ARGUMENT_ERROR("Unimplemented axis type.");
241  return {}; // get rid of compiler warning
242  }
243  }
244  }
245 } // namespace nc
StaticAsserts.hpp
nc::NdArray::item
value_type item() const
Definition: NdArrayCore.hpp:2958
nc::NdArray::shape
Shape shape() const noexcept
Definition: NdArrayCore.hpp:4312
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:4608
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:4326
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