NumCpp  2.10.1
A Templatized Header Only C++ Implementation of the Python NumPy Library
insert.hpp
Go to the documentation of this file.
1 #pragma once
29 
30 #include <cmath>
31 #include <utility>
32 #include <vector>
33 
36 #include "NumCpp/Core/Slice.hpp"
37 #include "NumCpp/Core/Types.hpp"
39 #include "NumCpp/NdArray.hpp"
40 
41 namespace nc
42 {
43  //============================================================================
44  // Method Description:
54  template<typename dtype>
55  NdArray<dtype> insert(const NdArray<dtype>& arr, int32 index, const dtype& value)
56  {
57  const NdArray<dtype> values = { value };
58  return insert(arr, index, values);
59  }
60 
61  //============================================================================
62  // Method Description:
72  template<typename dtype>
73  NdArray<dtype> insert(const NdArray<dtype>& arr, int32 index, const NdArray<dtype>& values)
74  {
75  if (index < 0)
76  {
77  index += arr.size();
78  // still
79  if (index < 0)
80  {
81  THROW_INVALID_ARGUMENT_ERROR("index out of range");
82  }
83  }
84  else if (index > static_cast<int32>(arr.size()))
85  {
86  THROW_INVALID_ARGUMENT_ERROR("index out of range");
87  }
88 
89  const auto valuesSlice = Slice(index, index + values.size());
90  auto result = NdArray<dtype>(1, arr.size() + values.size());
91  result.put(valuesSlice, values);
92 
93  NdArray<bool> mask(result.shape());
94  mask.fill(true);
95  mask.put(valuesSlice, false);
96  result.putMask(mask, arr.flatten());
97 
98  return result;
99  }
100 
101  //============================================================================
102  // Method Description:
113  template<typename dtype>
114  NdArray<dtype> insert(const NdArray<dtype>& arr, int32 index, const dtype& value, Axis axis)
115  {
116  const NdArray<dtype> values = { value };
117  return insert(arr, index, values, axis);
118  }
119 
120  //============================================================================
121  // Method Description:
132  template<typename dtype>
133  NdArray<dtype> insert(const NdArray<dtype>& arr, int32 index, const NdArray<dtype>& values, Axis axis)
134  {
135  switch (axis)
136  {
137  case Axis::NONE:
138  {
139  return insert(arr, index, values);
140  }
141  case Axis::ROW:
142  {
143  if (!(values.size() == arr.numCols() || values.isscalar() || values.numCols() == arr.numCols()))
144  {
145  THROW_INVALID_ARGUMENT_ERROR("input values shape cannot be broadcast to input array dimensions");
146  }
147 
148  if (index < 0)
149  {
150  index += arr.numRows();
151  // still
152  if (index < 0)
153  {
154  THROW_INVALID_ARGUMENT_ERROR("index out of range");
155  }
156  }
157  else if (index > static_cast<int32>(arr.numRows()))
158  {
159  THROW_INVALID_ARGUMENT_ERROR("index out of range");
160  }
161 
162  auto result = NdArray<dtype>();
163  int32 valuesSize{};
164  if (values.size() == arr.numCols() || values.isscalar())
165  {
166  result.resizeFast(arr.numRows() + 1, arr.numCols());
167  valuesSize = 1;
168  }
169  else if (values.numCols() == arr.numCols())
170  {
171  result.resizeFast(arr.numRows() + values.numRows(), arr.numCols());
172  valuesSize = values.numRows();
173  }
174 
175  auto mask = ones_like<bool>(result);
176  mask.put(Slice(index, index + valuesSize), mask.cSlice(), false);
177 
178  result.putMask(mask, arr);
179  result.putMask(!mask, values);
180 
181  return result;
182  }
183  case Axis::COL:
184  {
185  if (!(values.size() == arr.numRows() || values.isscalar() || values.numRows() == arr.numRows()))
186  {
187  THROW_INVALID_ARGUMENT_ERROR("input values shape cannot be broadcast to input array dimensions");
188  }
189 
190  if (index < 0)
191  {
192  index += arr.numCols();
193  // still
194  if (index < 0)
195  {
196  THROW_INVALID_ARGUMENT_ERROR("index out of range");
197  }
198  }
199  else if (index > static_cast<int32>(arr.numCols()))
200  {
201  THROW_INVALID_ARGUMENT_ERROR("index out of range");
202  }
203 
204  auto result = NdArray<dtype>();
205  int32 valuesSize{};
206  if (values.size() == arr.numRows() || values.isscalar())
207  {
208  result.resizeFast(arr.numRows(), arr.numCols() + 1);
209  valuesSize = 1;
210  }
211  else if (values.numRows() == arr.numRows())
212  {
213  result.resizeFast(arr.numRows(), arr.numCols() + values.numCols());
214  valuesSize = values.numCols();
215  }
216 
217  auto mask = ones_like<bool>(result);
218  mask.put(mask.rSlice(), Slice(index, index + valuesSize), false);
219 
220  result.putMask(mask, arr);
221  result.putMask(!mask, values);
222 
223  return result;
224  }
225  default:
226  {
227  // get rid of compiler warning
228  return {};
229  }
230  }
231  }
232 
233  //============================================================================
234  // Method Description:
245  template<typename dtype, typename Indices, type_traits::ndarray_int_concept<Indices> = 0>
246  NdArray<dtype> insert(const NdArray<dtype>& arr, const Indices& indices, const dtype& value, Axis axis = Axis::NONE)
247  {
248  const NdArray<dtype> values = { value };
249  return insert(arr, indices, values, axis);
250  }
251 
252  //============================================================================
253  // Method Description:
264  template<typename dtype>
265  NdArray<dtype> insert(const NdArray<dtype>& arr, Slice slice, const dtype& value, Axis axis = Axis::NONE)
266  {
267  auto sliceIndices = slice.toIndices(arr.dimSize(axis));
268  return insert(arr, NdArray<uint32>(sliceIndices.data(), sliceIndices.size(), false), value, axis);
269  }
270 
271  //============================================================================
272  // Method Description:
283  template<typename dtype, typename Indices, type_traits::ndarray_int_concept<Indices> = 0>
284  NdArray<dtype>
285  insert(const NdArray<dtype>& arr, const Indices& indices, const NdArray<dtype>& values, Axis axis = Axis::NONE)
286  {
287  const auto isScalarValue = values.isscalar();
288 
289  switch (axis)
290  {
291  case Axis::NONE:
292  {
293  if (!isScalarValue && indices.size() != values.size())
294  {
295  THROW_INVALID_ARGUMENT_ERROR("could not broadcast values into indices");
296  }
297 
298  const auto arrSize = static_cast<int32>(arr.size());
299 
300  std::vector<std::pair<int32, dtype>> indexValues(indices.size());
301  if (isScalarValue)
302  {
303  const auto value = values.front();
304  stl_algorithms::transform(indices.begin(),
305  indices.end(),
306  indexValues.begin(),
307  [arrSize, value](auto index) -> std::pair<int32, dtype>
308  {
309  if constexpr (type_traits::is_ndarray_signed_int_v<Indices>)
310  {
311  if (index < 0)
312  {
313  index += arrSize;
314  }
315  // still
316  if (index < 0)
317  {
318  THROW_INVALID_ARGUMENT_ERROR("index out of range");
319  }
320  }
321  if (static_cast<int32>(index) > arrSize)
322  {
323  THROW_INVALID_ARGUMENT_ERROR("index out of range");
324  }
325 
326  return std::make_pair(static_cast<int32>(index), value);
327  });
328  }
329  else
330  {
331  stl_algorithms::transform(indices.begin(),
332  indices.end(),
333  values.begin(),
334  indexValues.begin(),
335  [arrSize](auto index, const auto& value) -> std::pair<int32, dtype>
336  {
337  if constexpr (type_traits::is_ndarray_signed_int_v<Indices>)
338  {
339  if (index < 0)
340  {
341  index += arrSize;
342  }
343  // still
344  if (index < 0)
345  {
346  THROW_INVALID_ARGUMENT_ERROR("index out of range");
347  }
348  }
349  if (static_cast<int32>(index) > arrSize)
350  {
351  THROW_INVALID_ARGUMENT_ERROR("index out of range");
352  }
353 
354  return std::make_pair(static_cast<int32>(index), value);
355  });
356  }
357 
358  stl_algorithms::sort(indexValues.begin(),
359  indexValues.end(),
360  [](const auto& indexValue1, const auto& indexValue2) noexcept -> bool
361  { return indexValue1.first < indexValue2.first; });
362  auto indexValuesUnique = std::vector<typename decltype(indexValues)::value_type>{};
363  std::unique_copy(indexValues.begin(),
364  indexValues.end(),
365  std::back_inserter(indexValuesUnique),
366  [](const auto& indexValue1, const auto& indexValue2) noexcept -> bool
367  { return indexValue1.first == indexValue2.first; });
368 
369  auto result = NdArray<dtype>(1, arr.size() + indexValuesUnique.size());
370 
371  auto mask = ones_like<bool>(result);
372  int32 counter = 0;
373  std::for_each(indexValuesUnique.begin(),
374  indexValuesUnique.end(),
375  [&counter, &mask](auto& indexValue) noexcept -> void
376  { mask[indexValue.first + counter++] = false; });
377 
378  result.putMask(mask, arr);
379 
380  auto valuesSorted = [&indexValuesUnique]
381  {
382  std::vector<dtype> values_;
383  values_.reserve(indexValuesUnique.size());
384  std::transform(indexValuesUnique.begin(),
385  indexValuesUnique.end(),
386  std::back_inserter(values_),
387  [](const auto& indexValue) { return indexValue.second; });
388  return values_;
389  }();
390 
391  result.putMask(!mask, NdArray<dtype>(valuesSorted.data(), valuesSorted.size(), false));
392 
393  return result;
394  }
395  case Axis::ROW:
396  {
397  const auto arrNumRows = static_cast<int32>(arr.numRows());
398 
399  std::vector<std::pair<int32, NdArray<dtype>>> indexValues(indices.size());
400  if (values.isscalar())
401  {
402  const auto value = values.front();
403  auto valueRow = NdArray<dtype>(1, arr.numCols());
404  valueRow.fill(value);
405  stl_algorithms::transform(indices.begin(),
406  indices.end(),
407  indexValues.begin(),
408  [arrNumRows, &valueRow](auto index) -> std::pair<int32, NdArray<dtype>>
409  {
410  if constexpr (type_traits::is_ndarray_signed_int_v<Indices>)
411  {
412  if (index < 0)
413  {
414  index += arrNumRows;
415  }
416  // still
417  if (index < 0)
418  {
419  THROW_INVALID_ARGUMENT_ERROR("index out of range");
420  }
421  }
422  if (static_cast<int32>(index) > arrNumRows)
423  {
424  THROW_INVALID_ARGUMENT_ERROR("index out of range");
425  }
426 
427  return std::make_pair(static_cast<int32>(index), valueRow);
428  });
429  }
430  else if (values.size() == arr.numCols())
431  {
432  stl_algorithms::transform(indices.begin(),
433  indices.end(),
434  indexValues.begin(),
435  [arrNumRows, &values](auto index) -> std::pair<int32, NdArray<dtype>>
436  {
437  if constexpr (type_traits::is_ndarray_signed_int_v<Indices>)
438  {
439  if (index < 0)
440  {
441  index += arrNumRows;
442  }
443  // still
444  if (index < 0)
445  {
446  THROW_INVALID_ARGUMENT_ERROR("index out of range");
447  }
448  }
449  if (static_cast<int32>(index) > arrNumRows)
450  {
451  THROW_INVALID_ARGUMENT_ERROR("index out of range");
452  }
453 
454  return std::make_pair(static_cast<int32>(index), values);
455  });
456  }
457  else if (values.numCols() == arr.numCols() && values.numRows() == indices.size())
458  {
459  int32 counter = 0;
460  std::transform(indices.begin(),
461  indices.end(),
462  indexValues.begin(),
463  [arrNumRows, &values, &counter](auto index) -> std::pair<int32, NdArray<dtype>>
464  {
465  if constexpr (type_traits::is_ndarray_signed_int_v<Indices>)
466  {
467  if (index < 0)
468  {
469  index += arrNumRows;
470  }
471  // still
472  if (index < 0)
473  {
474  THROW_INVALID_ARGUMENT_ERROR("index out of range");
475  }
476  }
477  if (static_cast<int32>(index) > arrNumRows)
478  {
479  THROW_INVALID_ARGUMENT_ERROR("index out of range");
480  }
481 
482  return std::make_pair(static_cast<int32>(index),
483  values(counter++, values.cSlice()));
484  });
485  }
486  else
487  {
488  THROW_INVALID_ARGUMENT_ERROR("input values shape cannot be broadcast to input array dimensions");
489  }
490 
491  stl_algorithms::sort(indexValues.begin(),
492  indexValues.end(),
493  [](const auto& indexValue1, const auto& indexValue2) noexcept -> bool
494  { return indexValue1.first < indexValue2.first; });
495  auto indexValuesUnique = std::vector<typename decltype(indexValues)::value_type>{};
496  std::unique_copy(indexValues.begin(),
497  indexValues.end(),
498  std::back_inserter(indexValuesUnique),
499  [](const auto& indexValue1, const auto& indexValue2) noexcept -> bool
500  { return indexValue1.first == indexValue2.first; });
501 
502  auto result = NdArray<dtype>(arrNumRows + indexValuesUnique.size(), arr.numCols());
503 
504  auto mask = ones_like<bool>(result);
505  int32 counter = 0;
506  std::for_each(indexValuesUnique.begin(),
507  indexValuesUnique.end(),
508  [&counter, &mask](auto& indexValue) noexcept -> void
509  { mask.put(indexValue.first + counter++, mask.cSlice(), false); });
510 
511  result.putMask(mask, arr);
512 
513  counter = 0;
514  for (const auto& [index, values_] : indexValuesUnique)
515  {
516  result.put(index + counter++, result.cSlice(), values_);
517  }
518 
519  return result;
520  }
521  case Axis::COL:
522  {
523  return insert(arr.transpose(), indices, values.transpose(), Axis::ROW).transpose();
524  }
525  default:
526  {
527  // get rid of compiler warning
528  return {};
529  }
530  }
531  }
532 
533  //============================================================================
534  // Method Description:
545  template<typename dtype>
546  NdArray<dtype> insert(const NdArray<dtype>& arr, Slice slice, const NdArray<dtype>& values, Axis axis = Axis::NONE)
547  {
548  auto sliceIndices = slice.toIndices(arr.dimSize(axis));
549  return insert(arr, NdArray<uint32>(sliceIndices.data(), sliceIndices.size(), false), values, axis);
550  }
551 } // namespace nc
#define THROW_INVALID_ARGUMENT_ERROR(msg)
Definition: Error.hpp:37
Holds 1D and 2D arrays, the main work horse of the NumCpp library.
Definition: NdArrayCore.hpp:138
size_type dimSize(Axis inAxis) const noexcept
Definition: NdArrayCore.hpp:2607
self_type & fill(value_type inFillValue) noexcept
Definition: NdArrayCore.hpp:2731
size_type size() const noexcept
Definition: NdArrayCore.hpp:4415
self_type & put(index_type inIndex, const value_type &inValue)
Definition: NdArrayCore.hpp:3616
size_type numCols() const noexcept
Definition: NdArrayCore.hpp:3388
self_type flatten() const
Definition: NdArrayCore.hpp:2770
const_reference front() const noexcept
Definition: NdArrayCore.hpp:2783
size_type numRows() const noexcept
Definition: NdArrayCore.hpp:3400
iterator begin() noexcept
Definition: NdArrayCore.hpp:1258
bool isscalar() const noexcept
Definition: NdArrayCore.hpp:2879
self_type & putMask(const NdArray< bool > &inMask, const value_type &inValue)
Definition: NdArrayCore.hpp:4058
A Class for slicing into NdArrays.
Definition: Slice.hpp:45
std::vector< uint32 > toIndices(uint32 inArrayDimSize)
Definition: Slice.hpp:214
void sort(RandomIt first, RandomIt last) noexcept
Definition: StlAlgorithms.hpp:696
OutputIt transform(InputIt first, InputIt last, OutputIt destination, UnaryOperation unaryFunction)
Definition: StlAlgorithms.hpp:775
void for_each(InputIt first, InputIt last, UnaryFunction f)
Definition: StlAlgorithms.hpp:225
constexpr OutputIt unique_copy(InputIt first, InputIt last, OutputIt destination) noexcept
Definition: StlAlgorithms.hpp:823
Definition: Coordinate.hpp:45
Axis
Enum To describe an axis.
Definition: Types.hpp:47
NdArray< dtype > insert(const NdArray< dtype > &arr, int32 index, const dtype &value)
Definition: insert.hpp:55
std::int32_t int32
Definition: Types.hpp:36
NdArray< dtype > insert(const NdArray< dtype > &arr, Slice slice, const NdArray< dtype > &values, Axis axis=Axis::NONE)
Definition: insert.hpp:546