# Notable differences with numpy

xtensor and numpy are very different libraries in their internal semantics. While xtensor is a lazy expression system, numpy manipulates in-memory containers, however, similarities in APIs are obvious. See e.g. the numpy to xtensor cheat sheet.

And this page tracks the subtle differences of behavior between NumPy and xtensor.

## Zero-dimensional arrays

With NumPy, 0-D arrays are nearly indistinguishable from scalars. This led to some issues w.r.t. universal functions returning scalars with 0-D array inputs instead of actual arrays…

In xtensor, 0-D expressions are not implicitly convertible to scalar values. Values held by 0-D
expressions can be accessed in the same way as values of higher dimensional arrays, that is with
`operator[]`

, `operator()`

and `element`

.

Accumulators (`cumsum`

, `cumprod`

) throw an exception if an axis argument is passed and the
array argument is a 0-D argument:

```
#include <xtensor/xarray.hpp>
#include <xtensor/xio.hpp>
xt::xarray<double> x = 1;
std::cout << xt::cumsum(x, 0) << std::endl;
// Outputs:
// Standard Exception: Axis larger than expression dimension in accumulator.
std::cout << xt::cumsum(x) << std::endl;
//Outputs:
// 1
```

## Meshgrid

`numpy.meshgrid`

supports two modes: the ‘xy’ indexing and the ‘ij’ indexing.

The following code

```
import numpy as np
x1, x2, x3, x4 = [1], [10, 20], [100, 200, 300], [1000, 2000, 3000, 4000]
ij = np.meshgrid(x1, x2, x3, x4, indexing='ij')
xy = np.meshgrid(x1, x2, x3, x4, indexing='xy')
print 'ij:', [m.shape for m in ij]
print 'xy:', [m.shape for m in xy]
```

would return

```
ij: [(1, 2, 3, 4), (1, 2, 3, 4), (1, 2, 3, 4), (1, 2, 3, 4)]
xy: [(2, 1, 3, 4), (2, 1, 3, 4), (2, 1, 3, 4), (2, 1, 3, 4)]
```

In other words, the ‘xy’ indexing, which is the default only reverses the first two dimensions compared to the ‘ij’ indexing.

xtensor’s version of meshgrid corresponds to the ‘ij’ indexing.

## The random module

Like most functions of xtensor, functions of the random module return expressions that don’t hold any value.

Every time an element is accessed, a new random value is generated. To fix the values of a generator, it should be assigned to a container such as xarray or xtensor.

## Missing values

Support of missing values in NumPy can be emulated with the masked array module, which provides a means to handle arrays that have missing or invalid data.

Support of missing values in xtensor is done through a notion of optional values, implemented in `xoptional<T, B>`

, which serves both as a value type for container and as a reference proxy for optimized storage types. See the section of the documentation on Missing values.

## Strides

Strided containers of xtensor and NumPy having the same exact memory layout may have different strides when accessing them through the `strides`

attribute.
The reason is an optimization in xtensor, which is to set the strides to `0`

in dimensions of length `1`

, which simplifies the implementation of broadcasting of universal functions.

Tip

Use the free function `xt::strides`

to switch between representations.

```
xt::strides(a); // strides of ``a`` corresponding to storage
xt::strides(a, xt::stride_type::normal); // same
xt::strides(a, xt::stride_type::internal); // ``== a.strides()``
xt::strides(a, xt::stride_type::bytes) // strides in bytes, as in NumPy
```

## Array indices

Array indices are in xtensor stored as a `std::vector`

of array indices, whereby each entry corresponds to the array indices of one item. This results in a slightly different usage of `xt::ravel_indices`

than of `numpy.ravel_multi_index`

.