Add two array of arrays in Ruby - ruby

I have two arrays:
a = [[1,2],[3,4]];
b = [[5,6],[7,8]];
I want the resultant array to be their sum, i.e.,
c = [[6,8],[10,12]];
Would there be an elegant way to do so?
Note:
I currently know that to simply add a = [1,2] with b = [3,4] to get c = [4,6] I need to do
c = [a,b].transpose.map{|x| x.reduce(:+)};
but I'm not sure how to, if possible, extend this to my problem.

a.zip(b).map { |x,y| x.zip(y).map { |s| s.inject(:+) } }

c = [a, b].transpose.map{|ary| ary.transpose.map{|ary| ary.inject(:+)}}

An alternative, with better expression for manipulating numbers, would be to use 'narray'
require 'narray'
a = NArray[[1,2],[3,4]]
b = NArray[[5,6],[7,8]]
c = a + b
. . . yes really, c = a + b and it is much faster too.
You do pay for this though - NArray expects all the elements to contain the same type of object. If that's the case, and especially if your real-world problem has much larger matrices, then I highly recommend narray for handling this kind of data

Related

What are some nice ways to reverse a nested hash?

Suppose we have
b = {"b"=>"c"}
By doing b.invertwe can easily obtain the result of
{"c"=>"b"}
Thats when I thought of trying something pretty cool. Suppose we have
a = {"a"=>{"b"=>"c"}}
Whats a fairly efficient way to make this {{"c"=>"b"}=>"a"} (Here we reverse the most inner hash and work our way out)
Of course it would be best to extend this to n amount of hashes within each other. I've been looking for some other questions similar but haven't found any.
Thanks.
This can be accomplished with a recursive method for inverting the keys of the hash (and values, if desired). For example:
hsh = {{"c"=>"b"}=>"a"}
def recursive_invert(hsh)
hsh.each_with_object({}) do |(k, v), inverted_hsh|
if k.is_a? Hash
k = recursive_invert(k)
end
inverted_hsh[v] = k
end
end
recursive_invert(hsh) # {"a"=>{"b"=>"c"}}
Here's A recursive solution that will work in both directions.
def deep_invert(h)
h.each_with_object({}) do |(k,v),obj|
k = deep_invert(k) if k.is_a?(Hash)
v = deep_invert(v) if v.is_a?(Hash)
obj[v] = k
end
end
Example:
a = {"a"=>{"b"=>"c"}}
deep_invert(a)
#=> {{"c"=>"b"}=>"a"}
deep_invert(deep_invert(a)) == a
#=> true

How to multiply tensors in MATLAB without looping?

Suppose I have:
A = rand(1,10,3);
B = rand(10,16);
And I want to get:
C(:,1) = A(:,:,1)*B;
C(:,2) = A(:,:,2)*B;
C(:,3) = A(:,:,3)*B;
Can I somehow multiply this in a single line so that it is faster?
What if I create new tensor b like this
for i = 1:3
b(:,:,i) = B;
end
Can I multiply A and b to get the same C but faster? Time taken in creation of b by the loop above doesn't matter since I will be needing C for many different A-s while B stays the same.
Permute the dimensions of A and B and then apply matrix multiplication:
C = B.'*permute(A, [2 3 1]);
If A is a true 3D array, something like A = rand(4,10,3) and assuming that B stays as a 2D array, then each A(:,:,1)*B would yield a 2D array.
So, assuming that you want to store those 2D arrays as slices in the third dimension of output array, C like so -
C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B; and so on.
To solve this in a vectorized manner, one of the approaches would be to use reshape A into a 2D array merging the first and third dimensions and then performing matrix-muliplication. Finally, to bring the output size same as the earlier listed C, we need a final step of reshaping.
The implementation would look something like this -
%// Get size and then the final output C
[m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);
Sample run -
>> A = rand(4,10,3);
B = rand(10,16);
C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B;
>> [m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);
>> all(C(:)==out(:)) %// Verify results
ans =
1
As per the comments, if A is a 3D array with always a singleton dimension at the start, you can just use squeeze and then matrix-multiplication like so -
C = B.'*squeeze(A)
EDIT: #LuisMendo points out that this is indeed possible for this specific use case. However, it is not (in general) possible if the first dimension of A is not 1.
I've grappled with this for a while now, and I've never been able to come up with a solution. Performing element-wise calculations is made nice by bsxfun, but tensor multiplication is something which is woefully unsupported. Sorry, and good luck!
You can check out this mathworks file exchange file, which will make it easier for you and supports the behavior you're looking for, but I believe that it relies on loops as well. Edit: it relies on MEX/C++, so it isn't a pure MATLAB solution if that's what you're looking for.
I have to agree with #GJSein, the for loop is really fast
time
0.7050 0.3145
Here's the timer function
function time
n = 1E7;
A = rand(1,n,3);
B = rand(n,16);
t = [];
C = {};
tic
C{length(C)+1} = squeeze(cell2mat(cellfun(#(x) x*B,num2cell(A,[1 2]),'UniformOutput',false)));
t(length(t)+1) = toc;
tic
for i = 1:size(A,3)
C{length(C)+1}(:,i) = A(:,:,i)*B;
end
t(length(t)+1) = toc;
disp(t)
end

How to group Arrays with similar values

I'm looking for a smart way to group any number of arrays with similar values (not necessarily in the same order). The language I'm using is ruby but I guess the problem is pretty language agnostic.
Given
a = ['foo', 'bar']
b = ['bar', 'foo']
c = ['foo', 'bar', 'baz']
d = ['what', 'ever', 'else']
e = ['foo', 'baz', 'bar']
I'd like to have a function that tells me that
a & b are in one group
c & e are in one group
d is it's own group
I can think of a number of not so smart ways of doing this very inefficient, like I could compare each array's values to each others array's values.
Or I could check if ((a - b) + (b - a)).length == 0 for all combinations of arrays and group the ones that result in 0. Or I could check if a.sort == b.sort for all combinations of arrays.
I'm sure someone before me has solved this problem way more efficiently. I just can't seem to find how.
You can do it with sort without doing it "for all combinations of arrays" but doing it only for all arrays (Schwartzian transform).
arrays = [a, b, c, d, e]
arrays.group_by{|array| array.sort}.values

ruby array intersection performance issue

I need to make intersection of n-arrays with millions of elements (database ID's).
This code works perfect, but slow (with very big arrays). How can i improve it?
[[1,2,3,4],[2,4,6,8],[4,5,8]].inject([]){|c,v| c = v if c.size==0; c = c&v if c.size>0; c }
[1,2,3,4] & [2,4,6,8] & [4,5,8] #=> [4]
The intersection method uses hash so it should be quick.
Ruby provides an intersection opperator.
May I suggest you try this:
> [[1,2,3,4],[2,4,6,8],[4,5,8]].reduce{ |accum, arr| accum & arr }
=> [4]
Edit:
This can be written a little more concise but it suffers from readability.
[[1,2,3,4],[2,4,6,8],[4,5,8]].reduce(:&)

Is there a pre-built function to add the elements in two arrays?

If I have two arrays:
a = [1,2,3]
b = [2,3,4]
Is there a pre-built function to add the two arrays to give
c = a + b = [3,5,7]
i.e. add the values of each element in the array?
No, there isn't one method for this. But you can combine zip and map like this:
c = a.zip(b).map {|a,b| a+b}
I think the closest thing to what you ask is:
[1,2,3].zip([2,3,4]).map{|x| x.reduce(:+)}
it works even with more arrays
[1,2,3].zip([2,3,4], [3,4,5], [4,5,6]).map{|x| x.reduce(:+)}
That looks a lot like vector addition. Here's one way to accomplish that:
require 'matrix'
a = Vector[1,2,3]
b = Vector[2,3,4]
puts a+b
#=> Vector[3,5,7]
Simply use to_a on a Vector to get an array.

Resources