Related
Let's say I have a list with multiple arrays:
L = [
array([-10, -8, -3, 2, 1]),
array([-9, -4, -1, 3, 5]),
array([-11, -5, -4, 0, 10])
]
How can I find the index of the lowest value the most efficiently?
For my example, the minimum value is -11 and the index is (2, 0), so the output should be (0, 2).
What about the following?
import numpy as np
L = [
np.array([-10, -8, -3, 2, 1]),
np.array([-9, -4, -1, 3, 5]),
np.array([-11, -5, -4, 0, 10])
]
L_2d = np.array(L)
min_index = np.unravel_index(L_2d.argmin(), L_2d.shape)
(min_index[0], min_index[1])
I have an array of integers in Ruby. I want to find the differences between each number and every other number.
I can do it with one of the integers and find the difference between it and all the other numbers but I can't work out how to iterate twice per se.
Here is what I have:
def stock_picker(ary)
ary.map {|a| ary[0] - a }
end
stock_picker [1, 2, 3, 4, 5]
#=> [0, -1, -2, -3, -4]
When I run the above for ary[1] instead of ary[0] I get:
[1, 0, -1, -2, -3]
For ary[2] it's:
[2, 1, 0, -1, -2]
and so on. But How can I generate the differences for all numbers in ary regardless of its size?
The expected result is:
[0, -1, -2, -3, -4, 1, 0, -1, -2, -3, 2, 1, 0, -1, -2, 3, 2, 1, 0, -1, 4, 3, 2, 1, 0]
More compact version:
arr.product(arr).map { |a,b| a - b }
It appears you want the following.
def doit(arr)
arr.flat_map { |n| arr.map { |m| n-m } }
end
doit [1, 2, 3, 4, 5]
#=> [0, -1, -2, -3, -4, 1, 0, -1, -2, -3, 2, 1,
# 0, -1, -2, 3, 2, 1, 0, -1, 4, 3, 2, 1, 0]
See Enumerable#flat_map.
Note: This question poses a problem that I have already solved, however I feel my solution is very rudimentary and that other people, like myself, would benefit from a discussion with input from more experienced developers. Different approaches to solving the problem, as well as more sophisticated methods and algorithms would be really appreciated. I feel this is a good place to learn how Ruby can tackle what I consider to be a fairly difficult problem for a beginner.
Given a 6x6 2D Array arr:
1 1 1 0 0 0
0 1 0 0 0 0
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
We define an hourglass in arr to be a subset of values with indices falling in this pattern in arr's graphical representation:
a b c
d
e f g
There are 16 hourglasses in arr and an hourglass sum is the sum of an hourglass' values. Calculate the hourglass sum for every hourglass in arr, then print the maximum hourglass sum.
For example, given the 2D array:
arr = [
[-9, -9, -9, 1, 1, 1],
[ 0, -9, 0, 4, 3, 2],
[-9, -9, -9, 1, 2, 3],
[ 0, 0, 8, 6, 6, 0],
[ 0, 0, 0, -2, 0, 0],
[ 0, 0, 1, 2, 4, 0]
]
We calculate the following hourglass values:
-63, -34, -9, 12,
-10, 0, 28, 23,
-27, -11, -2, 10,
9, 17, 25, 18
Our highest hourglass value is from the hourglass:
0 4 3
1
8 6 6
My solution is:
def hourglass_sum(arr)
hourglasses = []
arr.each_with_index do |row, i|
# rescue clause to prevent iterating outside the array
unless arr[i].nil?
arr[i].length.times do |iteration|
# generate n 3x3 arrays
r1 = arr[i][iteration...iteration+3]
r2 = arr[i+1][iteration...iteration+3] if arr[i+1] != nil
r3 = arr[i+2][iteration...iteration+3] if arr[i+2] != nil
# rescue clause to stop creating 3x3 arrays that fall outside given input array
if arr[i+1] != nil && arr[i+2] != nil
# take all values except indices 0 and 5 from the 9 element array
result = r1 + [r2[1]] + r3
hourglasses << result.sum unless result.include? nil
end
end
end
end
p hourglasses.max
end
arr = [[-9, -9, -9, 1, 1, 1], [0, -9, 0, 4, 3, 2], [-9, -9, -9, 1, 2, 3], [0, 0, 8, 6, 6, 0], [0, 0 ,0, -2, 0, 0], [0, 0, 1, 2, 4, 0]]
hourglass_sum(arr)
# => 28
One option is to use Matrix methods.
require 'matrix'
ma = Matrix[*arr]
#=> Matrix[[-9, -9, -9, 1, 1, 1],
# [ 0, -9, 0, 4, 3, 2],
# [-9, -9, -9, 1, 2, 3],
# [ 0, 0, 8, 6, 6, 0],
# [ 0, 0, 0, -2, 0, 0],
# [ 0, 0, 1, 2, 4, 0]]
mi = Matrix.build(6-3+1) { |i,j| [i,j] }
#=> Matrix[[[0, 0], [0, 1], [0, 2], [0, 3]],
# [[1, 0], [1, 1], [1, 2], [1, 3]],
# [[2, 0], [2, 1], [2, 2], [2, 3]],
# [[3, 0], [3, 1], [3, 2], [3, 3]]]
def hourglass_val(r,c,ma)
mm = ma.minor(r,3,c,3)
mm.sum - mm[1,0] - mm[1,2]
end
max_hg = mi.max_by { |r,c| hourglass_val(r,c,ma) }
#=> [1,2]
hourglass_val(*max_hg,ma)
#=> 28
[1,2] are the row and column indices of the top-left corner of an optimal hourglass in arr.
Here is an option I came up with.
def width_height(matrix)
[matrix.map(&:size).max || 0, matrix.size]
end
def sum_with_weight_matrix(number_matrix, weight_matrix)
number_width, number_height = width_height(number_matrix)
weight_width, weight_height = width_height(weight_matrix)
width_diff = number_width - weight_width
height_diff = number_height - weight_height
0.upto(height_diff).map do |y|
0.upto(width_diff).map do |x|
weight_height.times.sum do |ry|
weight_width.times.sum do |rx|
weight = weight_matrix.dig(ry, rx) || 0
number = number_matrix.dig(y + ry, x + rx) || 0
number * weight
end
end
end
end
end
arr = [
[-9, -9, -9, 1, 1, 1],
[ 0, -9, 0, 4, 3, 2],
[-9, -9, -9, 1, 2, 3],
[ 0, 0, 8, 6, 6, 0],
[ 0, 0, 0, -2, 0, 0],
[ 0, 0, 1, 2, 4, 0],
]
weights = [
[1, 1, 1],
[0, 1, 0],
[1, 1, 1],
]
sum_matrix = sum_with_weight_matrix(arr, weights)
#=> [
# [-63, -34, -9, 12],
# [-10, 0, 28, 23],
# [-27, -11, -2, 10],
# [ 9, 17, 25, 18]
# ]
max_sum = sum_matrix.flatten.max
#=> 28
This solution uses the width_diff and height_diff to create an output matrix (4x4 for the sample data 0.upto(6 - 3).to_a #=> [0, 1, 2, 3]). The indexes of the weight_matrix (rxand ry) will be used as relative index compared to the larger number_matrix.
If your 2d array always has the same number of elements for each sub-array you can replace matrix.map(&:size).max with matrix[0]&.size || 0 to speed up determining the matrix width. The current solution uses the maximum size of the sub-arrays. Sub-arrays having a smaller size will use 0 for the missing elements thus not effecting the sum.
My solution might be a bit variable heavy. I've done this to have descriptive variable names, that hopefully tell you most you need to know about the solution. You can shorten variable names, or remove them completely when you feel like you don't need them.
If something isn't clear just ask away in the comments.
Without using the Matrix class, here's how I've done it for any arbitrary rectangular array:
offsets = [[-1, -1], [-1, 0], [-1, 1], [0, 0], [1, -1], [1, 0], [1, 1]]
sums = 1.upto(arr.length - 2).flat_map do |i|
1.upto(arr[0].length - 2).map do |j|
offsets.map {|(x, y)| arr[i+x][j+y] }.sum
end
end
puts sums.max
The values we're interested in are just offsets from a current position. We can map out the values in the array relative to the current position by some row and column offset, sum them, then select the max of the sums.
first of all I explain what I would like to do. I have a function which gives me some lists. These lists have the same number of elements and they contain numbers, which represents positions on the x-axis. For example one of them is [-11, -6, -5, -4, -1, 1, 3, 4, 6, 7], another one is [-11, -6, -5, -3, -1, 1, 2, 4, 5, 7]. The entries will always be integers and in ascending order.
I want to run this function many times and at the end "sum-up" all these vectors in a particular way. Imagine that each vector shows the position of a person in the x-axis. I want to know, at the end of say q experiments, how many people there are in each position. However, they do not all start from -11 or end at 7.
For example [-13, -8, -3, -1, 0, 1, 2, 4, 5, 7] or [-12, -7, -2, -1, 0, 1, 3, 4, 5, 6] are other two valid output from the function.
How can I do that?
My idea was to create a loop, compute the function, and store these lists into an array and then use some weird matrix operation. However I am absolutely stuck, this is my attempt, where rep_assign_time2(n,p,m) is the function that gives me the lists:
def many_experiments(n,p,m,q):
jj = 0
vector_min = []
vector_max = []
a = np.array([])
while jj < q:
s = rep_assign_time2(n,p,m)
a = np.concatenate((a,s), axis = 0) # I add s as an element of a
for k in range(a.shape):
ma = max(a[k])
mi = min(a[k])
vector_min.append(mi)
vector_max.append(ma)
minimum = min(vector_min)
maximum = max(vector_max)
And then I have NO IDEA on how to create an operation that does what I want. I've been thinking for an hour and still no clue. Do you have any idea?
You are in luck with NumPy, as there's a built-in for it as np.unique. It gives us both such unique labels (axis positions in this case) and their counts at each such label. So, let's say you have the lists stored as a list, thus a list of lists as A, you could simply do -
unq,counts = np.unique(A,return_counts=True)
Sample run -
In [33]: A = [[-11, -6, -5, -4, -1, 1, 3, 4, 6, 7], \
...: [-11, -6, -5, -3, -1, 1, 2, 4, 5, 7],\
...: [-13, -8, -3, -1, 0, 1, 2, 4, 5, 7],\
...: [-12, -7, -2, -1, 0, 1, 3, 4, 5, 6]]
In [34]: unq,counts = np.unique(A,return_counts=True)
In [35]: unq
Out[35]:
array([-13, -12, -11, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1,
2, 3, 4, 5, 6, 7])
In [36]: counts
Out[36]: array([1, 1, 2, 1, 1, 2, 2, 1, 2, 1, 4, 2, 4, 2, 2, 4, 3, 2, 3])
In [40]: import matplotlib.pyplot as plt
In [41]: # Plot the results
...: plt.bar(unq, counts, align='center')
...: plt.grid()
...: plt.show()
...:
I'm sure this one is easy in numpy, however I did not find the appropriate method. Basically I need something like outer() but for subtraction. Here is my working code via LC:
import numpy as np
a = np.array(((1,2,3), (2,3,4), (3,4,5)))
b = np.array(((6,7,8), (6,3,1)))
print np.array([i-j for i in a for j in b])
Output:
[[-5 -5 -5]
[-5 -1 2]
[-4 -4 -4]
[-4 0 3]
[-3 -3 -3]
[-3 1 4]]
Here is the speed comparison of the posted answers. a has 1000000 entries and b has 100:
In [22]: %timeit np.array([i-j for i in a for j in b])
1 loops, best of 3: 12.3 s per loop
In [23]: %timeit np.repeat(a,b.shape[0],0)-np.tile(b,(a.shape[0],1))
10 loops, best of 3: 50.1 ms per loop
In [24]: %timeit (a[:,np.newaxis,:]-b).reshape(-1, 3)
10 loops, best of 3: 125 ms per loop
You could take advantage of broadcasting by reshaping a before subtraction, then reshaping again to get the desired result:
In [97]: (a[:,np.newaxis,:]-b).reshape(-1, 3)
Out[97]:
array([[-5, -5, -5],
[-5, -1, 2],
[-4, -4, -4],
[-4, 0, 3],
[-3, -3, -3],
[-3, 1, 4]])
Explanation:
In [101]: a.shape
Out[101]: (3, 3)
In [99]: a[:,np.newaxis,:].shape
Out[99]: (3, 1, 3)
In [100]: b.shape
Out[100]: (2, 3)
When NumPy evaluates a[:,np.newaxis,:]-b it broadcasts the shapes of a[:,np.newaxis,:] and b both to (3, 2, 3) before subtraction. Roughly speaking, the first and second axes do not interact. The subtraction occurs only in the 3rd axis. The subtraction is performed for each location with respect to the first and second axes. That's sort of the NumPy equivalent of
for i in a for j in b
The result is
In [102]: a[:,np.newaxis,:]-b
Out[102]:
array([[[-5, -5, -5],
[-5, -1, 2]],
[[-4, -4, -4],
[-4, 0, 3]],
[[-3, -3, -3],
[-3, 1, 4]]])
In [103]: (a[:,np.newaxis,:]-b).shape
Out[103]: (3, 2, 3)
The only thing left to be done is to reshape the result to get it in the desired form.
Use np.repeat() and np.tile() to repeat/tile the array
import numpy as np
a = np.array(((1,2,3), (2,3,4), (3,4,5)))
b = np.array(((6,7,8), (6,3,1)))
c = np.repeat(a,b.shape[0],0)-np.tile(b,(a.shape[0],1))
print c
array([[-5, -5, -5],
[-5, -1, 2],
[-4, -4, -4],
[-4, 0, 3],
[-3, -3, -3],
[-3, 1, 4]])