Insert range of values into a multidimensional array - excel

I am trying to create a multidimensional array where the first column contains identifiers and the adjacent columns contains data relevant to that identifier. So for instance I would like to create an array with the following structure:
Banana 10 20 30 40
Coconut 5 10 2 4
Apple 3 4 5 6
The construction of the array begins with the definition of the relevant identifiers. So for instance in the above that would be Banana, Coconut and Apple. The data I use to construct the array would have a layout as in the below:
Banana 10 20 30 40
Parrot 5 3 1 4
Apple 3 4 5 6
Car 10 20 30 40
Donkey 4 12 3 0
Coconut 5 10 2 4
As such, I start out by defining the Banana, Coconut and Apple identifiers and then want to automatically populate my array based on a loop through of identifier name in the data (I have defined this as "INPUT"). However, I am unsure of how to correctly insert the adjacent data in my array every time there is a match of identifiers. I would much appreciate if someone can explain how I can do this based on the code below.
identifierArray = Array("Banana", "Coconut", "Apple")
NumElements = UBound(identifierArray) - LBound(identifierArray) + 1
For Each Element In identifierArray
ReDim Preserve arr(0 To NumElements, x)
arr(i, 0) = identifierArray(i)
i = i + 1
Next Element
For Each cell In ws.Range("INPUT")
For Each Element In identifierArray
If cell.Value = Element Then
[Need help here]
End If
Next Element
Next cell
I don't need help with creating VLOOKUP or INDEX/MATCH solutions as that is not relevant to the above.

You can fill an array from a range on your sheet like this:
Option Explicit
Sub Test()
Dim arr
With ThisWorkbook.Sheets("Data")
arr = .Range("A1:E6")
End With
End Sub
So a range like this:
Turns into a array like this:
So you don't need to loop at all, which means faster execution and cheaper to code.

Related

How to take data from one excel table and add it to matching rows in a different table?

I have two excel tables, Table A has 4 columns, Table B has 13. Each of the 4 columns in Table A can be found in Table B. They are composed of count data from censuses. During the censuses, people counted the species they encountered and gave a value but they did not write down when they did not encounter a species. I added in 0's for years and locations where species were not found using pivot charts/macros. But now I have my Table A that includes the 0 values but it's missing all the extra data from Table B. The tables look something like this (simplified):
Table A
species location year value
Mango A 2001 2
Mango A 2002 3
Mango A 2003 1
Avocado A 2001 1
Avocado A 2002 0
Avocado A 2003 0
Mango B 2001 0
Mango B 2002 2
Mango B 2003 20
Avocado B 2001 25
Avocado B 2002 80
Avocado B 2003 0
Table B
species location year value month day group uploaded?
Mango A 2001 2 12 1 X No
Mango A 2002 3 12 5 X Yes
Mango A 2003 1 12 3 X No
Avocado A 2001 1 12 1 X No
Mango B 2002 2 12 6 Y No
Mango B 2003 20 12 7 Y No
Avocado B 2001 25 12 4 Y No
Avocado B 2002 80 12 6 Y No
You can see that Table B contains all the rows in Table A that have values above 0 but does not contain the rows with values of 0. Every year/location combo in Table B has the same data for every other column other than species and value.
Is there a way to take the data from Table B and put it into the appropriate rows in Table A? I would like it to work so that every location/year combo in Table B will be transported into every row (including the rows with 0) in Table A. I thought maybe I could do something with relationships but I couldn't figure it out.
Any help is appreciated. Thank you!
Additional Columns
This solution requires the addresses of the ranges containing data, the two column numbers of the ranges to be compared and the number of columns to be added i.e. the number of the last columns from Range1 to be added to Range2.
Before
After
The Code
Sub AdditionalColumns()
Const cStr1 As String = "A4:D15" ' First Range
Const cStr2 As String = "A21:H28" ' Second Range
Const cIntCol1 As Integer = 2 ' First Compare Column
Const cIntCol2 As Integer = 3 ' Second Compare Column
Const cIntAdd As Integer = 4 ' Additional Columns
Dim vnt1 As Variant ' First Array
Dim vnt2 As Variant ' Second Array
Dim vntTarget As Variant ' Target Array
Dim i As Long ' First Array Row Counter
Dim j As Long ' Second Array Row Counter
Dim k As Long ' Target Array Column Counter
With ThisWorkbook.Worksheets("Sheet1")
vnt1 = .Range(cStr1)
vnt2 = .Range(cStr2)
ReDim vntTarget(1 To UBound(vnt1), 1 To cIntAdd)
For i = 1 To UBound(vnt1)
For j = 1 To UBound(vnt2)
If vnt1(i, cIntCol1) = vnt2(j, cIntCol1) Then
If vnt1(i, cIntCol2) = vnt2(j, cIntCol2) Then
For k = 1 To cIntAdd
vntTarget(i, k) = vnt2(j, k + UBound(vnt1, 2))
Next
Exit For
End If
End If
Next
Next
.Cells(.Range(cStr1).Row, .Range(cStr1).Columns.Count _
+ .Range(cStr1).Column) _
.Resize(UBound(vntTarget), UBound(vntTarget, 2)) = vntTarget
End With
End Sub

generate random numbers between 0 and 1 in 10 cells in the row , in which the sum of the random number always equal to 7

How can I generate random numbers 0 or 1 in 10 cells in the row, in which the sum of the random number is always equal to 7?
enter image description here
Here's a way to get seven "1"s and three "0"s in random order using RAND and RANK
In A1:J1: =RAND()
In A2:J2: =IF(RANK(A1,$A$1:$J$1,1)>3,1,0)
Available here is a version that I really think works! https://www.dropbox.com/s/ec431fu0h0fhb5i/RandomNumbers.xlsx?dl=0
And here's the '0 and 1' version (sheet 2 at the above link):
De-dup Rankings Randoms First Cut Sorted
0.47999002 7 0.479992063 1 1
0.68823003 3 0.688233075 1 1
0.07594004 9 0.075938331 1 1
0.02077005 10 0.020766892 1 0
0.69217006 2 0.692170173 1 0
0.73355007 1 0.733549516 1 1
0.51546008 6 0.515462872 1 1
0.62308009 4 0.623078278 0
0.33033001 8 0.330331577 1
0.561260011 5 0.561260557 1
Formulae for columns A-C exactly as before, D is just 7 1's, E is:
=VLOOKUP(ROW(E2)-1,B$1:D$11,3,FALSE)
Assuming that you want a list of positive random numbers that add to 7 you can use this following method.
Enter a 0 in the top-left cell (Blue Cell).
Enter =RAND()*7 into the next 9 cells below the 0 (Orange Cells).
Enter a 7 in the cell below the 9 random values (Blue Cell).
Copy the 9 random values and paste-special-values over top to turn the formulas into values.
Sort just these 9 cells in ascending order
In the cell just to the right of the first random value put a formula that subtracts the cell to the left and one above from the cell to the left (Yellow Cells).
Repeat this formula down to the cell next to the 7 that was typed in.
Sum the values in the second column (Green Cell).
That should give you 10 random values whose sum is exactly 7.
The only issue is that getting the values to be between 0 and 1 will take a bit of trial and error.
It appears that trial and error may not be practical. It's about a one in 2,710 times that this list will contain only numbers between 0 and 1. Not overly practical. Sorry.
To answer the question in the post, enter this in A1:J1 as an array formula (ctrl+shift+enter):
=1-(TRANSPOSE(MOD(SMALL(RANDBETWEEN(0,1e12*(ROW(INDIRECT("1:10"))>0))+(ROW(INDIRECT("1:10"))-1)/10,ROW(INDIRECT("1:10"))),1))>0.65)
To answer the question in the post title, do the following:
In A1:J1 enter:
=RAND()
In K1 enter:
=IF(SUM(A1:J1)<7,(7-SUM(A1:J1))/(COUNT(A1:J1)-7),7/SUM(A1:J1))
In L1 enter:
=IF(SUM($A1:$J1)<7,(A1+$K1)/($K1+1),A1*$K1)
Fill over to U1.
I believe the 10 numbers generated will be identically distributed in [0,1), but obviously not uniformly (I'm fairly certain the distribution does not have a name). The numbers can't be considered independent. A few statistics on the distribution:
Mean: 0.7 (as expected)
The other statistics are estimated from 10,000 simulations:
Variance: 0.0295
Kurtosis: -0.648
Skewness: -0.192
Think of it as drawing a sample of size 7 from the set {1, 2, ..., 10}. The 1s correspond to the numbers chosen for inclusion in the sample. Here is some VBA code which generates such samples:
Function sample(n As Long, k As Long) As Variant
'returns a variant of length n
'consisting of k 1s and n-k 0s
'thought of as a sample of {1,...,n} of size k
Dim v As Variant 'vector to hold sample
Dim numbers As Variant
Dim i As Long, j As Long, temp As Long
ReDim v(1 To n)
ReDim numbers(1 To n)
For i = 1 To n
v(i) = 0
numbers(i) = i
Next i
'do k steps of a Fisher-Yates shuffle on numbers
For i = 1 To Application.WorksheetFunction.Min(k, n - 1)
j = Application.WorksheetFunction.RandBetween(i, n)
If i < j Then 'swap
temp = numbers(i)
numbers(i) = numbers(j)
numbers(j) = temp
End If
Next i
'now use the first k elements of the partially shuffled array:
For i = 1 To k
v(numbers(i)) = 1
Next i
sample = v
End Function
Used like: Range("A1:J1").Value = sample(10,7)
Using a bit of brute force, I think I've got a workable solution to the original version of the question which asked for random numbers between 0 and 1.
Cells A1 to A9:
=rand()
Cell A10:
=7-sum(A1:A9)
Now you have 10 numbers that add up to 7, but the last one is probably not in the range 0 to 1. To deal with that, just recalculate the sheet to generate new random numbers until that last value is within range. It takes about 25 recalculations to have a ~95% chance that one of them will be within range, so it could take a while. A little VBA can do that for you very quickly:
Sub rand7()
While Range("A10").Value > 1 Or Range("A10").Value < 0
ActiveSheet.Calculate
Wend
End Sub

Average range between multiple unique number pairs in excel

Suppose I have the following data in exce l:
For each row, I want to know what the average range between each unique pair of variables is.
Is there a way to do this in one go, without having to manually calculate the range between every number pair?
Taking row one as an example, the unique pairs and their ranges are as follows:
8 - 5 = 3
8 - 6 = 2
6 - 5 = 1
The average range is (3 + 2 + 1)/3 = 2.
So, the output should be 2, but I want to know if there is a way to do this all in one formula
Paste the following array formula into cell D1 and drag down as far as necessary:
= SUM(ABS(A1:C1-TRANSPOSE(A1:C1)))/(COLUMNS(A1:C1)*(COLUMNS(A1:C1)-1))
(Obviously, you'll have to change the formula above to whatever your cell ranges are.)
Note this is an array formula, so you must press Ctrl+Shift+Enter on the keyboard when entering this formula rather than just Enter.
See below for a demonstration that this formula works.
As in the example in your question, the result for the first row is 2.
Second row:
10 - 1 = 9
3 - 1 = 2
10 - 3 = 7
(9+2+7)/3 = 18/3 = 6
Also note, this formula works for variable column ranges. (It doesn't just have to be 3 columns.) See below for an example with 4 columns. (The formula is in cell E1 and is evaluating over cell range A1:D1.)
8 - 2 = 6
13 - 2 = 11
11 - 2 = 9
13 - 8 = 5
11 - 8 = 3
13 - 11 = 2
(6+11+9+5+3+2)/6 = 36/6 = 6

Spreadsheet/Excel array functions that compare and validate values

I got this dataset
ID fruit price
1 apple 10
2 apple 50
3 apple 100
4 banana 10
5 banana 20
6 banana 50
and would like a (set of) forumla(s) that go through the rows and output the row for each fruit that has the highest price.
In e.g. PHP I would do something like this
foreach $array as $row{
if in_array( $row[fruit] ){
/* check if current $row[price] for current $row[fruit] is larger than existing post. If yes replace */
}
}
How would I do that in Google Spreadsheets / Excel?
You can do it in Excel with array formulas (so you enter it with Ctrl+Shift+Enter)...
If your fruit is in B and price in C your array formula in D2 would be
=C2=MAX(IF($B$2:$B$7=B2,$C$2:$C$7,0))
This will give you TRUE or FALSE for whether that row has the highest price for that fruit.
It works by doing an IF on the array of fruits (rows 2 to 7 - you can make it longer) being the same as the current fruit - if it is the same, return the price, otherwise 0. We then get the MAX and compare it to the current row's price.
Good luck!
I've put together a quick VBA macro that you could use in excel that will output the fruit with the highest price.
The macro converts the table of fruit toa an array and then loops through the array to find the fruit with the highest value, before outputting it to the sheet. This macro relies on the table of fruit being positioned in columns A to C.
Sub getMaxPriceFruit()
'put data table into an array
Dim dataTableArray() As Variant
dataTableArray = Range("A2:C" & Cells(Rows.Count, "A").End(xlUp).Row)
'loop through the aray looking for the largest value
'capture array index in variable when largest is found
Dim maxArray(1 To 1, 1 To 3) As Variant
maxArray(1, 1) = 0
maxArray(1, 2) = ""
maxArray(1, 3) = 0
Dim i As Long
For i = 1 To UBound(dataTableArray)
If dataTableArray(i, 3) > maxArray(1, 3) Then
maxArray(1, 1) = dataTableArray(i, 1)
maxArray(1, 2) = dataTableArray(i, 2)
maxArray(1, 3) = dataTableArray(i, 3)
End If
Next i
'output the fruit with the max value
Range("F2").Value = maxArray(1, 1)
Range("G2").Value = maxArray(1, 2)
Range("H2").Value = maxArray(1, 3)
End Sub
The limitation of this script is that if there are two fruit with an equal max value, the first fruit in the list with that value will be selected as the winner. If you would like the additional code to output multiple fruits if they have the same max value I can provide, but put simply you could utilise the maxArray array to capture all of the top ranking fruits and then loop through this array to output them all in one go.
Hope that helps!
A pivot table with fruit for Rows and price for Values (Summarise by: MAX) may serve.

Run a simulation several times with different parameters and store values

Consider a spreadsheet which performs some computations based on a fixed value (in the rxample below, D3) and is iterative. E.g.
D3: 4
B3: 12
B4: 58 (=B3*$D$3+10)
B5: 242 (=B4*$D$3+10)
B6: 978 (=B5*$D$3+10)
Total = 1290 (=sum(B2:B5))
Now, suppose I wanted to try out different values of D3 (let's call this P) and store the different totals I get, i.e.
P Total
4 1290
5 2252
6 3618
7 5460
How would I do this with Excel? A macro? Please note that the above example is a simplified version of the real thing. It should be clear that I need to compute B3-B6 so I can compute the sum.
Update:
Each computation requires several columns. E.g. we would use values on B3,B4, .. and C3,C4, ... .
A macro can do this. If B7 contains the sum formula, try this
Sub RunSimulation()
Dim p as long
for p = 4 to 7
Range("D3")=p
Debug.Print Range("B7")
Range("L" & (p-1)) = Range("B7").Value
next
End Sub
EDIT: added a line for storing the results, as requested.
If you don't want to enter the sum formula in your sheet, you can calculate the total in VBA either:
Dim total as Long
Dim row as long
total = 0
for row = 2 to 5
total = total + Range("B" & row)
Next
Debug.Print total
(Use Double instead of Long for total if you are dealing with floating point numbers.)
Usually it is done in following manner:
A B C D
1 x tmp1 tmp2 Total
2 3 $A2+10 $B2*10+10 $C2*$B2 The formulae are just for example.
3 $A2+1 $A3+10 $B3*10+10 $C3*$B3
4 $A3+1 $A4+10 $B4*10+10 $C4*$B4
Excel has capabilities to automatically increment indices in formulae.

Resources