Translate VBA array into Python - arrays

USING IDLE/Python 3.5.1
May I first of all begin by saying I am a reasonably experienced programmer in VBA but am on day 2 of Python. I assure you I have conducted many searches on this question but the 30 or so documents I have read do not seem to explain my problem.
May I also please request that any answers given are properly formatted code for Python 3.5.1 rather than helpful pointers to other documentation or links?
The Problem
I am running a report and outputting results as I go. I need to store the results (presumably in an array) during this so that I can refer to them afterwards. The report (and the populating of the array) can be rerun multiple times so please bear that in mind if using concepts like 'append' when building the array. The array has dimensions [25,4] - a maximum of 25 records with four items in each.
Day X Y Z Total
1 2 3 4 9
2 3 4 5 12 ...
(Purists: The total needs to be recorded rather than calculated because of rounding.)
I could solve the problem myself if someone could translate this bit of code into Python (from VBA for illustration purposes). I do not want to import the arrays module unless it's the only way. Note: Variable l is a loop that makes the array get built twice to demonstrate that the array needs to be capable of rebuilding from scratch rather than being created just the once.
Sub sArray()
Dim a(25, 4)
For l = 1 To 2
For i = 1 To 25
For j = 1 To 4
a(i, j) = Int(100 * Rnd(1)) + 1
Debug.Print a(i, j);
Next j
Next i
Next l
End Sub
Thanks,
Tom

I am not sure I got your question correctly...
If you want to make an array (list i a better term in this case) of size [25,4] this is one way to go:
import random
a = [[int(100*random.random())+1 for j in range(4)] for i in range(25)]
>>> print a
[[74, 17, 36, 75],
[1, 79, 33, 90],
[58, 66, 47, 95],
[35, 40, 87, 38],
[43, 46, 34, 66],
[69, 34, 26, 49],
[56, 83, 44, 14],
[2, 44, 54, 97],
[50, 21, 39, 60],
[13, 94, 12, 48],
[36, 13, 2, 71],
[77, 44, 31, 11],
[56, 26, 30, 39],
[17, 13, 83, 84],
[54, 37, 34, 18],
[5, 54, 88, 100],
[22, 77, 70, 21],
[51, 88, 26, 97],
[69, 33, 86, 48],
[42, 66, 38, 78],
[71, 43, 96, 23],
[6, 46, 100, 29],
[32, 86, 15, 48],
[96, 84, 8, 56],
[29, 64, 69, 79]]
if you want to show that "the array needs to be capable of rebuilding from scratch rather than being created just the once" (why would you need this??)
for l in range(2):
a = [[int(100*random.random())+1 for j in range(4)] for i in range(25)]
Also, the way of generating random numbers is odd (I have translated you method). To get the same result in python, just use random.randint(1,100) to generate random integers from 1 (i think you do not want to have 0 there) to whatever number you like.
If I have correctly understood from your comments, this is what you want:
def report(g=25):
array = []
for _ in range(g):
x = random.randint(1,10)
y = random.randint(1,10)
z = random.randint(1,10)
total = x+y+x
row = [x,y,z,total]
print(row)
array.append(row)
return array
result = report()
#prints all the rows while computing
>>> result #stores the "array"
[8, 4, 3, 20]
[10, 7, 4, 27]
[2, 4, 5, 8]
[8, 5, 8, 21]
[9, 7, 2, 25]
[2, 2, 3, 6]
[5, 8, 6, 18]
[8, 6, 1, 22]
[7, 6, 4, 20]
[7, 2, 10, 16]
[6, 5, 9, 17]
[3, 8, 8, 14]
[9, 1, 9, 19]
[1, 7, 7, 9]
[6, 6, 2, 18]
[9, 10, 1, 28]
[4, 6, 2, 14]
[6, 1, 6, 13]
[4, 1, 3, 9]
[5, 3, 5, 13]
[7, 5, 2, 19]
[9, 5, 7, 23]
[2, 5, 8, 9]
[3, 10, 4, 16]
[5, 6, 5, 16]

Related

Formatting arrays with multiplication Numpy Python

I have 3 arrays down below a and b combine to make a_and_b. a is multiplied by a_multiplier and b gets multiplied by b_multiplier. How would I be able to modify a_and_b after the multiplier has been implemented in it.
Code:
import numpy as np
a_multiplier = 3
b_multiplier = 5
a = np.array([5,32,1,4])
b = np.array([1,5,11,3])
a_and_b = np.array([5,1,32,5,1,11,4,3])
Expected Output:
[15, 5, 96, 25, 3, 55, 12, 15]
first learn how to use the multiply:
In [187]: a = np.array([5,32,1,4])
In [188]: a*3
Out[188]: array([15, 96, 3, 12])
In [189]: b = np.array([1,5,11,3])
In [190]: b*5
Out[190]: array([ 5, 25, 55, 15])
One way to combine the 2 arrays:
In [191]: np.stack((a*3, b*5),axis=1)
Out[191]:
array([[15, 5],
[96, 25],
[ 3, 55],
[12, 15]])
which can be easily turned into the desired 1d array:
In [192]: np.stack((a*3, b*5),axis=1).ravel()
Out[192]: array([15, 5, 96, 25, 3, 55, 12, 15])

Sort a numpy array using multiple index and different order

I have an array of size 300x5 and I am trying to sort the array in such a way that column with index 4 is my primary index and in ascending order, index 1 is secondary index and in descending order, index 3 is tertiary index and in ascending order.
I have tried this using following code,
idx = np.lexsort((arr[:,3],arr[:,1][::-1],arr[:,4]))
arr= arr[idx]
where arr --> array of size 300x5
On executing this the secondary index also gets sorted in ascending order instead of descending order. Can anyone help me with this
I think you want -arr[:,1] and not arr[:,1][::-1] as the secondary index.
>>> import numpy as np
>>> arr = np.random.randint(0, 21, (300, 5))
>>> arr
array([[ 0, 19, 6, 19, 17],
[16, 2, 14, 17, 0],
[ 8, 17, 3, 17, 12],
...,
[ 4, 18, 18, 3, 8],
[10, 15, 4, 12, 4],
[ 9, 16, 12, 0, 12]])
>>> idx = np.lexsort((arr[:,3],-arr[:,1],arr[:,4]))
>>> arr = arr[idx]
>>> arr
array([[11, 20, 11, 18, 0],
[11, 16, 12, 2, 0],
[ 9, 16, 4, 8, 0],
...,
[20, 4, 5, 11, 20],
[ 9, 4, 0, 19, 20],
[ 9, 2, 4, 10, 20]])

Insert data from hash into an array at set index in Ruby

I’m doing Ruby task, which is “You have an array of numbers. Your task is to sort ascending odd numbers but even numbers must be on their places. Zero isn't an odd number and you don't need to move it. If you have an empty array, you need to return it”.
Decided to split initial array: odd nums pushed into another array, sorted it and even numbers added to hash, where the key is num and its initial index is value. After that, trying to insert the even nums from hash to odd array, at initial indexes of even nums. So, the code looks like this:
def sort_array(source_array)
even_nums = Hash.new
odd_nums = []
return source_array if source_array.length == 0
source_array.each_with_index {|n, ind| even_nums[n] = ind if n.even?}
source_array.select{|n| odd_nums.push(n) if n.odd?}
odd_nums.sort!
even_nums.each do |k, v|
odd_nums.insert(v, k)
end
odd_nums
end
With small array like [5, 3, 2, 8, 1, 4, 11] it works as expected but if I pass something bigger like [84, -64, 40, 53, 5, 88, 2, 14, 29, -79, -44, -23, 20, -67, -12, 28, -28, -37, -27, -62, -54, 93, -61, 50, 65, -63, -62, 77, -16, 49, -43, 26, -73, -27, 88, -88, -62, 84, 54, 25, 25, -2, -99, 69, -23, 47, -92, 7, -62, -62, -58, -30, -75, -31, 65, -63, 16, 64, -7, -22, -6, -82]
I’m getting nils at the end of the sorted array. Like this:
[-99, -64, 40, -79, -75, -73, 2, 14, -67, -63, -44, -63, 20, -61, -12, 28, -28, -43, -37, -31, -54, -27, -27, 50, -23, -23, -7, 5, -16, 7, 25, 26, 25, 29, 47, -88, 49, 53, 54, 65, 65, -2, 69, 77, 93, nil, -92, nil, nil, 88, -58, -30, nil, nil, nil, nil, 16, 64, nil, -22, -6, -82, 84, nil, -62]
Struggling to understand, why it isn’t working with bigger arrays?
There's a fairly easy way to do this if you think about it as two operations:
def sort_array(arr)
# Extract and sort the odd values
odd = arr.select(&:odd?).sort
# Merge the sorted odd values back in
arr.map do |v|
v.odd? ? odd.shift : v
end
end
Not much to it.
You had some of the right pieces, but I think you got stuck in the weeds when it started to get overly complicated.
That can be done as follows.
def sort_odds(arr)
odd_pos = arr.each_index.select { |i| arr[i].odd? }
odd_pos.zip(odd_pos.sort_by { |i| arr[i] }).
each_with_object(arr.dup) do |(old_pos,new_pos),a|
a[new_pos] = arr[old_pos]
end
end
sort_odds [5, 3, 2, 8, 1, 4, 11]
# o o e e o e o
#=> [1, 3, 2, 8, 5, 4, 11]
The steps are as follows.
arr = [5, 3, 2, 8, 1, 4, 11]
# o o e e o e o
odd_pos = arr.each_index.select { |i| arr[i].odd? }
#=> [0, 1, 4, 6]
new_pos = odd_pos.zip(odd_pos.sort_by { |i| arr[i] })
#=> [[0, 4], [1, 1], [4, 0], [6, 6]]
new_pos.each_with_object(arr.dup) do|(old_pos,new_pos),a|
a[new_pos] = arr[old_pos]
end
#=> [1, 3, 2, 8, 5, 4, 11]
I think this other answer is simpler and more elegant than mine, but this works too. Notably, this solution would allow you to validate the position of your even numbers (for example, in a spec) by looking up the indexes and values in evens. Unless you already know what the output array should look like, this may matter when it comes time to debug the interim results.
def odd_sorted array
odds = array.select { |e| e.odd? }.sort
evens = array.each_with_index.select { |e| e.first.even? }
arr2 = Array.new(array.size)
# put even numbers in at their previous index
evens.each do |e|
arr2.each_index { |i| arr2[i] = e[0] if e[1] == i }
end
arr2.each_with_index { |e, i| arr2[i] = odds.shift if e.nil? }
end
odd_sorted [5, 3, 2, 8, 1, 4, 11]
#=> [1, 3, 2, 8, 5, 4, 11]
odd_sorted [84, -64, 40, 53, 5, 88, 2, 14, 29, -79, -44, -23, 20]
#=> [84, -64, 40, -79, -23, 88, 2, 14, 5, 29, -44, 53, 20]
The Array#map solution is definitely more elegant, but this is (in my personal opinion) more debuggable. Your mileage in that regard will certainly vary.

Easy way to do nd-array contraction using advanced indexing in Python

I know there must be an elegant way to do this using advanced indexing, I just can't figure it out.
Suppose I have the (2,3,4) array
x = array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
and the (4,) array y = array([1,0,1,1])
What is the most elegant way to obtain the (3,4) array that
z = np.zeros((3,4))
for ii in xrange(3):
for jj in xrange(4):
z[ii,jj] = x[y[jj],ii,jj]
produces?
In [490]: x[y,:,np.arange(4)]
Out[490]:
array([[12, 16, 20],
[ 1, 5, 9],
[14, 18, 22],
[15, 19, 23]])
We need to transpose this. With a mix of basic and advanced indexing, the slice dimension has been put last:
In [491]: x[y,:,np.arange(4)].T
Out[491]:
array([[12, 1, 14, 15],
[16, 5, 18, 19],
[20, 9, 22, 23]])
(that basic/advanced quirk is documented and discussed in some SO.)
or with advanced indexing all around:
In [492]: x[y,np.arange(3)[:,None],np.arange(4)]
Out[492]:
array([[12, 1, 14, 15],
[16, 5, 18, 19],
[20, 9, 22, 23]])

Pythonic algorithm for doing an array calculation

I am looking for the most efficient and pythonic algorithm for doing an array calculation. Here is the problem:
I have an array of shape (5,2,3) and its sum along the axis=0 as follows:
import numpy as np
A = np.array([[[ 6, 15, 89],
[49, 62, 12]],
[[92, 8, 34],
[93, 81, 35]],
[[ 8, 35, 63],
[68, 89, 5]],
[[27, 20, 85],
[87, 42, 90]],
[[99, 64, 12],
[90, 93, 87]]])
B = A.sum(axis=0)
So B is basically equal to A[0]+A[1]+A[2]+A[3]+A[4] which is:
array([[232, 142, 283],
[387, 367, 229]])
I want to know at what stage of the sum process, each of 6 elements of B has gone bigger than 100. For example element B[0,0] goes above 100 after 3 steps: A[0]+A[1]+A[2], or B[1,1] goes above 100 after 2 steps A[0]+A[1].
So the final output of the algorithm should be this array:
array([[3, 5, 2],
[2, 2, 4]])
I know I can do the calculation for each element separately but I was wondering if anyone could come up with a creative and faster algorithm.
Cheers,
Use cumsum to get a cumulative summation, compare it against the threshold and finally use argmax to catch it as the first instance of crossing that threshold -
(A.cumsum(axis=0) > 100).argmax(axis=0)+1
Sample run -
In [228]: A
Out[228]:
array([[[ 6, 15, 89],
[49, 62, 12]],
[[92, 8, 34],
[93, 81, 35]],
[[ 8, 35, 63],
[68, 89, 5]],
[[27, 20, 85],
[87, 42, 90]],
[[99, 64, 12],
[90, 93, 87]]])
In [229]: (A.cumsum(0) > 100).argmax(0)+1
Out[229]:
array([[3, 5, 2],
[2, 2, 4]])

Resources