How to generate multiple arrays containing elements from another array? - arrays

Using just Ruby I am trying to
Generate an array of random numbers
Create a new 2 dimensional array containing x amount of arrays filled with x amount of samples from the original number list.
This is what I have...
a = 1000.times.map{rand(100)}.to_a
b = 5.times.map{a.sample}
#=> [3, 96, 23, 45, 41]
I basically want to be able to generate what I did in b, x amount of times.
Is this possible?
Thank you for the comments everyone!

Just wrap your definition of b in another map:
a = 1000.times.map{rand(100)} # to_a is unnecessary here, map returns an array
b = 5.times.map{5.times.map{a.sample}}

A one-liner to do what you want.
3.times.map {2.times.map {rand 1000} }
#=> [[267, 476], [109, 950], [345, 137]]

I don't have Rails installed at the moment, so here's a pure Ruby solution.
a = (0..1000).to_a.map! { rand(100) }
x = 2
b = (0..x).to_a.map! { a.sample(x) }
# [[83, 73], [55, 93], [57, 18]]

Related

Find common elements in subarrays of arrays

I have two numpy arrays of shape arr1=(~140000, 3) and arr2=(~450000, 10). The first 3 elements of each row, for both the arrays, are coordinates (z,y,x). I want to find the rows of arr2 that have the same coordinates of arr1 (which can be considered a subgroup of arr2).
for example:
arr1 = [[1,2,3],[1,2,5],[1,7,8],[5,6,7]]
arr2 = [[1,2,3,7,66,4,3,44,8,9],[1,3,9,6,7,8,3,4,5,2],[1,5,8,68,7,8,13,4,53,2],[5,6,7,6,67,8,63,4,5,20], ...]
I want to find common coordinates (same first 3 elements):
list_arr = [[1,2,3,7,66,4,3,44,8,9], [5,6,7,6,67,8,63,4,5,20], ...]
At the moment I'm doing this double loop, which is extremely slow:
list_arr=[]
for i in arr1:
for j in arr2:
if i[0]==j[0] and i[1]==j[1] and i[2]==j[2]:
list_arr.append (j)
I also tried to create (after the 1st loop) a subarray of arr2, filtering it on the value of i[0] (arr2_filt = [el for el in arr2 if el[0]==i[0]). This speed a bit the operation, but it still remains really slow.
Can you help me with this?
Approach #1
Here's a vectorized one with views -
# https://stackoverflow.com/a/45313353/ #Divakar
def view1D(a, b): # a, b are arrays
a = np.ascontiguousarray(a)
b = np.ascontiguousarray(b)
void_dt = np.dtype((np.void, a.dtype.itemsize * a.shape[1]))
return a.view(void_dt).ravel(), b.view(void_dt).ravel()
a,b = view1D(arr1,arr2[:,:3])
out = arr2[np.in1d(b,a)]
Approach #2
Another with dimensionality-reduction for ints -
d = np.maximum(arr2[:,:3].max(0),arr1.max(0))
s = np.r_[1,d[:-1].cumprod()]
a,b = arr1.dot(s),arr2[:,:3].dot(s)
out = arr2[np.in1d(b,a)]
Improvement #1
We could use np.searchsorted to replace np.in1d for both of the approaches listed earlier -
unq_a = np.unique(a)
idx = np.searchsorted(unq_a,b)
idx[idx==len(a)] = 0
out = arr2[unq_a[idx] == b]
Improvement #2
For the last improvement on using np.searchsorted that also uses np.unique, we could use argsort instead -
sidx = a.argsort()
idx = np.searchsorted(a,b,sorter=sidx)
idx[idx==len(a)] = 0
out = arr2[a[sidx[idx]]==b]
You can do it with the help of set
arr = np.array([[1,2,3],[4,5,6],[7,8,9]])
arr2 = np.array([[7,8,9,11,14,34],[23,12,11,10,12,13],[1,2,3,4,5,6]])
# create array from arr2 with only first 3 columns
temp = [i[:3] for i in arr2]
aset = set([tuple(x) for x in arr])
bset = set([tuple(x) for x in temp])
np.array([x for x in aset & bset])
Output
array([[7, 8, 9],
[1, 2, 3]])
Edit
Use list comprehension
l = [list(i) for i in arr2 if i[:3] in arr]
print(l)
Output:
[[7, 8, 9, 11, 14, 34], [1, 2, 3, 4, 5, 6]]
For integers Divakar already gave an excellent answer. If you want to compare floats you have to consider e.g. the following:
1.+1e-15==1.
False
1.+1e-16==1.
True
If this behaviour could lead to problems in your code I would recommend to perform a nearest neighbour search and probably check if the distances are within a specified threshold.
import numpy as np
from scipy import spatial
def get_indices_of_nearest_neighbours(arr1,arr2):
tree=spatial.cKDTree(arr2[:,0:3])
#You can check here if the distance is small enough and otherwise raise an error
dist,ind=tree.query(arr1, k=1)
return ind

Ruby: How to remove specific digits from integers that are inside an array?

I'm new to programming. I would like to take an array of Integers, like [155, 151, 2, 15] and remove a specific digit, in this case 5, and add up the new numbers. I've broken this problem up into smaller steps, and gotten the result I wanted. I'm just wondering if there are easier ways to do a problem like this? Maybe a different method I could use? Any help is greatly appreciated.
Here is the code I have:
arr = [155, 151, 2, 15]
# goal: remove the 5 digit from values and add
# new numbers together --> 1 + 11 + 2 + 1 == 15
# convert to arr of strings and delete zeros
str_arr = []
arr.each do |el|
str_arr << el.to_s.delete('5')
end
# convert to arr of nums
num_arr = []
str_arr.each do |el|
num_arr << el.to_i
end
# reduce num_arr
num_arr.reduce(:+)
Maybe you can use map instead each, this way you avoid having to push to a new initialized array each element transformed inside the block, like:
p [155, 151, 2, 15].map { |el| el.to_s.delete('5').to_i }.reduce(:+)
# 15
If using ruby 2.4 or higher you can use Enumerable#sum instead reduce (which seems to be a faster option).
p [155, 151, 2, 15].sum { |el| el.to_s.delete('5').to_i }
# 15
arr = [155, 151, 2, 15]
arr.sum { |n| (n.digits - [5]).reverse.join.to_i }
#=> 15
using eval:
eval(arr.join('+').delete('5'))
#=> 15
using inject:
arr.inject(0){|sum, element| element.to_s.delete('5').to_i + sum }
#=> 15
The solution using eval is more fun, but the solution using inject is arguably easier to understand. Cheers.

Element by Element Comparison of Multiple Arrays in MATLAB

I have a multiple input arrays and I want to generate one output array where the value is 0 if all elements in a column are the same and the value is 1 if all elements in a column are different.
For example, if there are three arrays :
A = [28, 28, 43, 43]
B = [28, 43, 43, 28]
C = [28, 28, 43, 43]
Output = [0, 1, 0, 1]
The arrays can be of any size and any number, but the arrays are also the same size.
A none loopy way is to use diff and any to advantage:
A = [28, 28, 43,43];
B = [28, 43, 43,28];
C = [28, 28, 43,43];
D = any(diff([A;B;C])) %Combine all three (or all N) vectors into a matrix. Using the Diff to find the difference between each element from row to row. If any of them is non-zero, then return 1, else return 0.
D = 0 1 0 1
There are several easy ways to do it.
Let's start by putting the relevant vectors in a matrix:
M = [A; B; C];
Now we can do things like:
idx = min(M)==max(M);
or
idx = ~var(M);
No one seems to have addressed that you have a variable amount of arrays. In your case, you have three in your example but you said you could have a variable amount. I'd also like to take a stab at this using broadcasting.
You can create a function that will take a variable number of arrays, and the output will give you an array of an equal number of columns shared among all arrays that conform to the output you're speaking of.
First create a larger matrix that concatenates all of the arrays together, then use bsxfun to take advantage of broadcasting the first row and ensuring that you find columns that are all equal. You can use all to complete this step:
function out = array_compare(varargin)
matrix = vertcat(varargin{:});
out = ~all(bsxfun(#eq, matrix(1,:), matrix), 1);
end
This will take the first row of the stacked matrix and see if this row is the same among all of the rows in the stacked matrix for every column and returns a corresponding vector where 0 denotes each column being all equal and 1 otherwise.
Save this function in MATLAB and call it array_compare.m, then you can call it in MATLAB like so:
A = [28, 28, 43, 43];
B = [28, 43, 43, 28];
C = [28, 28, 43, 43];
Output = array_compare(A, B, C);
We get in MATLAB:
>> Output
Output =
0 1 0 1
Not fancy but will do the trick
Output=nan(length(A),1); %preallocation and check if an index isn't reached
for i=1:length(A)
Output(i)= ~isequal(A(i),B(i),C(i));
end
If someone has an answer without the loop take that, but i feel like performance is not an issue here.

Multiplying within 2D array

I'm trying to get the product of a permutation of an array:
orig_arr = (89..99).to_a
perm = [[89, 90], [89, 91], [89, 92], [89, 93]...]
need = [[8010], [8099], [8188]...]
My best guess was to enumerate, but reduce doesn't function within each:
perm.each{|set| set.reduce(:*)}
Why doesn't this work? And, is it better to not create a 2D array, and go with a hash or matrix to solve this problem?
You can make it work by using Array#map instead of each:
orig_arr = (89..99).to_a
orig_arr.permutation(2).map { |set| [set.reduce(:*)] }
# => [[8010], [8099], [8188], [8277], [8366], [8455], . . . ]]

Take a number and split it into indexed groups in a single dimensional array

I have a number like 12,345,678 and I'm trying to split it into an Array that's groups of 3. Such that if I have:
stack = 12345678
Then I could get the following Array:
overflow = [12,345,678]
I was thinking about using Enumberable#each_slice like another similar question asked here, but that creates a 2D array. So then I considered using Array#flatten but that wouldn't keep my numbers grouped properly.
Is there any nice way to do this, or is it going to be some nasty nested loop? Thx!
This may not be efficient, but it works. You can build on/optimize it.
stack.to_s.chars.reverse.each_slice(3).map {|s| s.reverse.join.to_i }.reverse
# => [12, 345, 678]
For pure Ruby way (Non-Rails):
number = 12345678
number.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
#=> "12,345,678"
> number.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse.split(',').map(&:to_i)
#=> [12, 345, 678] # your expected output
If you want it in rails then there is very nice method number_with_delimiter
number_with_delimiter(12345678)
# => 12,345,678
Another option:
def break_in_3s(num)
return [0] if num.zero?
a = []
until num.zero?
num, last = num.divmod(1000)
a.unshift(last)
end
a
end
break_in_3s(12_345_678)
#=> [12, 345, 678]
break_in_3s(2_345_678)
#=> [2, 345, 678]
break_in_3s(312_345_678)
#=> [312, 345, 678]
I've assumed num is non-negative.
Aside: why don't we have:
class Array; alias >> unshift; end
so we could write:
a >> last
?

Resources