Thread overview
2D matrix operation (subtraction)
Feb 21, 2020
Andre Pany
Feb 21, 2020
Ali Çehreli
Feb 21, 2020
Andre Pany
Feb 22, 2020
9il
Feb 22, 2020
Andre Pany
Feb 25, 2020
jmh530
Feb 21, 2020
jmh530
Feb 21, 2020
jmh530
Feb 22, 2020
septc
February 21, 2020
Hi,

I have a 2D double array and I want to subtract from the first column a value,
is this possible with matrix operation in D?

```
void main()
{
    double[][] data = [[0.0, 1.4], [1.0, 5.2], [2.0, 0.8]];

    // subtract -2.0 from the first column for every value

    // Expected output
    // data = [[-2.0, 1.4], [-1.0, 5.2], [0.0, 0.8]];

}
```

Kind regards
André


February 21, 2020
On 2/21/20 12:51 AM, Andre Pany wrote:
> Hi,
> 
> I have a 2D double array and I want to subtract from the first column a value,
> is this possible with matrix operation in D?
> 
> ```
> void main()
> {
>      double[][] data = [[0.0, 1.4], [1.0, 5.2], [2.0, 0.8]];
> 
>      // subtract -2.0 from the first column for every value
> 
>      // Expected output
>      // data = [[-2.0, 1.4], [-1.0, 5.2], [0.0, 0.8]];
> 
> }
> ```
> 
> Kind regards
> André
> 
> 


I don't have experience with it but mir's ndslice is designed for this:

  https://github.com/libmir/mir-algorithm

Although it feels like something similar is probably already in Phobos, I've come up with the following solution just now for fun:

import std.stdio;
import std.algorithm;
import std.range;

// At least something similar to this exists in Phobos?
struct ElementReference(R) {
  ElementType!(ElementType!R) * p;

  ref reference() {
    return *p;
  }

  alias reference this;
}

struct Column(R) {
  R range;
  size_t n;

  auto empty() {
    return range.empty;
  }

  auto front() {
    return ElementReference!R(&(range.front[n]));
  }

  auto popFront() {
    return range.popFront();
  }
}

auto byColumn(R)(R range, size_t n) {
  return Column!R(range, n);
}

void main() {
  double[][] data = [[0.0, 1.4], [1.0, 5.2], [2.0, 0.8]];

  data.byColumn(0).each!(a => a -= 2.0);
  writeln(data);
}

Ali

February 21, 2020
On Friday, 21 February 2020 at 11:53:02 UTC, Ali Çehreli wrote:
> On 2/21/20 12:51 AM, Andre Pany wrote:
>> Hi,
>> 
>> I have a 2D double array and I want to subtract from the first column a value,
>> is this possible with matrix operation in D?
>> 
>> ```
>> void main()
>> {
>>      double[][] data = [[0.0, 1.4], [1.0, 5.2], [2.0, 0.8]];
>> 
>>      // subtract -2.0 from the first column for every value
>> 
>>      // Expected output
>>      // data = [[-2.0, 1.4], [-1.0, 5.2], [0.0, 0.8]];
>> 
>> }
>> ```
>> 
>> Kind regards
>> André
>> 
>> 
>
>
> I don't have experience with it but mir's ndslice is designed for this:
>
>   https://github.com/libmir/mir-algorithm
>
> Although it feels like something similar is probably already in Phobos, I've come up with the following solution just now for fun:
>
> import std.stdio;
> import std.algorithm;
> import std.range;
>
> // At least something similar to this exists in Phobos?
> struct ElementReference(R) {
>   ElementType!(ElementType!R) * p;
>
>   ref reference() {
>     return *p;
>   }
>
>   alias reference this;
> }
>
> struct Column(R) {
>   R range;
>   size_t n;
>
>   auto empty() {
>     return range.empty;
>   }
>
>   auto front() {
>     return ElementReference!R(&(range.front[n]));
>   }
>
>   auto popFront() {
>     return range.popFront();
>   }
> }
>
> auto byColumn(R)(R range, size_t n) {
>   return Column!R(range, n);
> }
>
> void main() {
>   double[][] data = [[0.0, 1.4], [1.0, 5.2], [2.0, 0.8]];
>
>   data.byColumn(0).each!(a => a -= 2.0);
>   writeln(data);
> }
>
> Ali

Thanks a lot. Mir is great and actually I try to rewrite some Python
Pandas Dataframe index logic. For my current project any dependency less
is a little headache less, therefore I try to avoid Mir at the moment,
but will definitely will have a look whether I can use it.

Thanks for the example, I will use it.

Kind regards
André


February 21, 2020
On Friday, 21 February 2020 at 11:53:02 UTC, Ali Çehreli wrote:
> [snip]
> auto byColumn(R)(R range, size_t n) {
>   return Column!R(range, n);
> }

mir has byDim for something similar (numir also has alongDim).

This is how you would do it:

import mir.ndslice;

void main() {
    auto x = [0.0, 1.4, 1.0, 5.2, 2.0, 0.8].sliced(3, 2);
    x.byDim!1.front.each!"a -= 2";
}

My recollection is that it is a little bit trickier if you want to subtract a vector from each column of a matrix (the sweep function in R).
February 21, 2020
On Friday, 21 February 2020 at 14:43:37 UTC, jmh530 wrote:
> [snip]

Actually, I kind of prefer the relevant line as
x.byDim!1[0].each!"a -= 2";
which makes it a little clearer that you can easily change [0] to [1] to apply each to the second column instead.
February 22, 2020
On Friday, 21 February 2020 at 08:51:49 UTC, Andre Pany wrote:
> Hi,
>
> I have a 2D double array and I want to subtract from the first column a value,
> is this possible with matrix operation in D?
>
> ```
> void main()
> {
>     double[][] data = [[0.0, 1.4], [1.0, 5.2], [2.0, 0.8]];
>
>     // subtract -2.0 from the first column for every value
>
>     // Expected output
>     // data = [[-2.0, 1.4], [-1.0, 5.2], [0.0, 0.8]];
>
> }
> ```
>
> Kind regards
> André

I've recently learning Mir and so interested in
this topic. For the above purpose, I am wondering if
this Numpy-like approach is also valid:

    y[ 0..$, 0 ] *= 100;

The online editor (https://run.dlang.io/) seems to give
the expected result.

// (the below code is based on the post by jmh530)

import std.stdio;
import mir.ndslice;

void main()
{
    auto x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0].sliced(3, 2);
    auto y = x.dup;

    writeln( x );   // [[0, 1], [2, 3], [4, 5]]
    writeln( y );   // [[0, 1], [2, 3], [4, 5]]

    x.byDim!1.front.each!"a *= 100";

    y[ 0..$, 0 ] *= 100;

    writeln( x );   // [[0, 1], [200, 3], [400, 5]]
    writeln( y );   // [[0, 1], [200, 3], [400, 5]]
}

February 22, 2020
On Friday, 21 February 2020 at 13:42:24 UTC, Andre Pany wrote:
> Mir is great and actually I try to rewrite some Python Pandas Dataframe index logic.

Maybe mir.series [1] can work for you.

Series!(Key*, Value*) - is a pair of two 1D ndslices, they can be sorted according to the first one ndslice (keys). Series has `get` methods.

Series!(Key*, Value*, 2) is a pair of 1D ndslice (keys) and 2D ndslice (values matrix).

Series has slicing primitives.

Keys corresponds to the first dimension.

http://mir-algorithm.libmir.org/mir_series.html#Series
February 22, 2020
On Saturday, 22 February 2020 at 08:29:32 UTC, 9il wrote:
> On Friday, 21 February 2020 at 13:42:24 UTC, Andre Pany wrote:
>> Mir is great and actually I try to rewrite some Python Pandas Dataframe index logic.
>
> Maybe mir.series [1] can work for you.
>
> Series!(Key*, Value*) - is a pair of two 1D ndslices, they can be sorted according to the first one ndslice (keys). Series has `get` methods.
>
> Series!(Key*, Value*, 2) is a pair of 1D ndslice (keys) and 2D ndslice (values matrix).
>
> Series has slicing primitives.
>
> Keys corresponds to the first dimension.
>
> http://mir-algorithm.libmir.org/mir_series.html#Series

Thanks a lot for all the answers.

Kind regards
Andre
February 25, 2020
On Saturday, 22 February 2020 at 08:29:32 UTC, 9il wrote:
> [snip]
>
> Maybe mir.series [1] can work for you.

I had a few other thoughts after looking at septc's solution of using
y[0..$, 0] *= 100;
to do the calculation.

1) There is probably scope for an additional select function to handle the use case of choosing a specific row/column. For instance, what if instead of
y[0..$, 0]
you want
y[0..$, b, 0..$]
for some arbitrary b. I think you would need to do something like
y.select!1(b, b + 1);
which doesn't have the best API, IMO, because you have to repeat b. Maybe just an overload for select that only takes one input instead of two?

2) The select series of functions does not seem to work as easily as array indexing does. When I tried to use the select/selectFront functions to do what he is doing, I had to something like
auto z = y.selectFront!1(1);
z[] *= 100;
This would adjust y as expected (not z). However, I couldn't figure out how to combine these together to one line. For instance, I couldn't do
y.selectFront!1(1) *= 100;
or
auto y = x.selectFront!1(1).each!(a => a * 100);
though something like
y[0..$, 0].each!"a *= 100";
works without issue.

It got a little frustrating to combine those with any kind of iteration. TBH, I think more than the select functions, the functionality I would probably be looking for is more what I was doing with byDim!1[0] in the prior post.

I could imagine some very simple version looking like below
auto selectDim(size_t dim, T)(T x, size_t a, size_t b) {
    return byDim!dim[a .. b];
}
with a corresponding version
auto selectDim(size_t dim, T)(T x, size_t a) {
    return byDim!dim[a .. (a + 1)];
}
This simple version would only work with one dimension, even though byDim can handle multiple.