For the first 15 ranks, I want to manually enter the values in B2:P11. For ranks 16 to 30, I want to randomize these values using an Excel VBA button, with the following code:
Sub rand_group()
Dim i As Long
Dim j As Long
Dim myFlag(1 To num_man)
Dim s_group As Worksheet
Set s_group = Worksheets("group")
'óêêîånóÒÇèâä˙âª
Randomize
s_group.Cells.Clear
s_group.Range("A1") = "group_id"
For i = 1 To num_group
s_group.Cells(i + 1, 1) = i
Next i
For i = 1 To num_man
s_group.Cells(1, i + 1) = "m_rank" & i
Next i
For i = 1 To num_group
For j = 16 To num_man
myFlag(j) = False
Next j
For j = 16 To num_man
Do
'óêêî=Int((ç≈ëÂíl - ç≈è¨íl +1 ) * Rnd + ç≈è¨íl)
myNum = Int((num_man - 1 + 1) * Rnd + 1)
Loop Until myFlag(myNum) = False
s_group.Cells(i + 1, j + 1).Value = myNum
myFlag(myNum) = True
Next j
Next i
End Sub
However, these random values should neglect the manually entered values in B2:P11
How can I change the code to fix this?
Screenshot of the excel file is displayed below:
I want to manually fill values from B2 to P11
Thank you in advance for your response!
Michiel
To fill in the random section of the grid, the myFlag array is not needed. Just use the cell coordinates to set the random values. Also remove the Clear call since that clears the entire sheet.
To prevent duplicate numbers in a row, use the excel CountIf function in a loop until a unique random number is found.
Here is the updated code. It can be run multiple times without affecting the manual data.
Sub DoRand()
Dim i As Long
Dim j As Long
Dim s_group As Worksheet
Set s_group = Worksheets("group")
num_group = 10
num_man = 30
Randomize
's_group.Cells.Clear ' clear sheet
' build table
For i = 1 To num_group ' group id
s_group.Cells(i + 1, 1) = i
Next i
For i = 1 To num_man ' column names
s_group.Cells(1, i + 1) = "m_rank" & i
Next i
' fill in random numbers
For i = 1 To num_group 'row
For j = 16 To num_man 'column
Do While True
n = Int((num_man) * Rnd + 1) ' get random number
cnt = Application.WorksheetFunction.CountIf(Range(s_group.Cells(i + 1, 2), s_group.Cells(i + 1, j)), "=" & n) ' check if number in row
s_group.Cells(i + 1, j + 1).Value = n ' set cell value
If cnt = 0 Then Exit Do ' if unique in row, go to next cell
Loop ' not unique, try new random value
Next
Next
End Sub
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
In Excel or Excel Macro, I am trying to figure out an alghorithm/formula to reach a number with using highest ones as possible but there are some limitations. Let me explain with an example;
Let's say I have 5 numbers in 5 cells (line by line) to use (2,5,10,20,50) and I need to reach 98.
In this example, I should only use 50 + 20 + 20 + 5 + 2 + 2. Even if I can't reach exact number (like reaching 98), it should be the results with minimum exceeds (like 99). It can't be lower than the target number.
I will copy these rows (as fetching their numbers in their first column) and paste to another sheet from the list. Please think these numbers to select rows, so I could find the appropriate rows as finding the right algorithm / formula for it.
How about this:
Put your data for example (2,5,10,20,50) into A1:A5 at the top of sheet1.
Put the below code into a vba code module, and run Algorithm.
I wrote the result into column C, but modify as needed.
Sub Algorithm()
Dim Tgt As Double, currVal As Double
Dim iRow As Integer, oRow As Integer 'input and output row
Dim ary, sortedAry 'input arrays
Dim aResult(1 To 100, 1 To 1) 'assuming result is less than 100 rows long
Tgt = 98 'Target number
ary = Sheet1.Range("A1").CurrentRegion 'put data into an array
sortedAry = BubbleSort(ary, False) 'sort in descending order
iRow = 1
oRow = 1
currVal = 0
Do Until iRow > UBound(sortedAry)
If currVal + sortedAry(iRow, 1) > Tgt Then
iRow = iRow + 1 'goto next smallest number
Else
aResult(oRow, 1) = sortedAry(iRow, 1)
currVal = currVal + sortedAry(iRow, 1)
oRow = oRow + 1
End If
Loop
'one more so it goes over Tgt
aResult(oRow, 1) = sortedAry(UBound(sortedAry), 1)
'put results back into spreadsheet
Sheet1.Range("C1").Resize(100, 1) = aResult
End Sub
Function BubbleSort(myArray As Variant, Optional Ascending = True)
'suitable to sort a small list that is in a 1 column array
Dim i As Long, j As Long
Dim Temp As Variant
If Ascending = True Then
For i = LBound(myArray, 1) To UBound(myArray, 1) - 1
For j = i + 1 To UBound(myArray)
If myArray(i, 1) > myArray(j, 1) Then
Temp = myArray(j, 1)
myArray(j, 1) = myArray(i, 1)
myArray(i, 1) = Temp
End If
Next j
Next i
Else
For i = LBound(myArray, 1) To UBound(myArray, 1) - 1
For j = i + 1 To UBound(myArray)
If myArray(i, 1) < myArray(j, 1) Then
Temp = myArray(j, 1)
myArray(j, 1) = myArray(i, 1)
myArray(i, 1) = Temp
End If
Next j
Next i
End If
BubbleSort = myArray
End Function
The aim of this code is to have the user input that they want an, e.g. 4x10 grid. I have attached a photo below of the desired output.
However, I'm stuck on the logic of the problem. I can generate one set of grid numbers (e.g. 1-25), but unsure how to duplicate this process to create the whole grid.
Hard to explain using words....
In short I am aiming for:
A1, A2, A3, A4, B1, B2, B3, B4 ...
But I am currently getting: A1, B2, C3, D4 ...
Tried experimenting with different code but to no success. Current code has a loop that I think is right in principle, but re-writes the data in the rows above it once it finishes one 'j' loop and goes back to the start. I'm not sure how to get 'j' to start on a blank cell rather than overwrite what is already in it.
['Userform prior to this step gathers user input
Dim Axial_Data_Points As Variant
Dim Circum_Data_Points As Variant
Axial_Data_Points = Axial_Data_Points_Box.Value 'User input value
Circum_Data_Points = Circum_Data_Points_Box.Value 'User input value
'Basic loop to generate a list of numbers up to the user imposed limit
For j = 1 To Axial_Data_Points
Worksheets("Data Entry").Activate
For k = 1 To Circum_Data_Points
Range("E" & ((j + k) + 1)).Select
ActiveCell.FormulaR1C1 = j
Next k
Next j]
1
This will produce output like:
Dim Axial_Data_Points As Variant
Dim Circum_Data_Points As Variant
Axial_Data_Points = Axial_Data_Points_Box.Value 'User input value
Circum_Data_Points = Circum_Data_Points_Box.Value 'User input value
'Basic loop to generate a list of numbers up to the user imposed limit
Dim i As Integer
Dim j As Integer
For i = 1 To Axial_Data_Points
For j = 1 To Circum_Data_Points
Worksheets("Data Entry").Cells(j + (i - 1) * 10, 4).Value = Chr(i + 64)
Worksheets("Data Entry").Cells(j + (i - 1) * 10, 5).Value = j
Next j
Next i
End Sub
Using Something of this type you can generate what you require:
j = 1
k = 1
For i = 1 To 200
If j < 27 Then
Range("A" & i).Value = Chr(j + 64)
j = j + 1
ElseIf j > 26 And j < 53 Then
G:
Range("A" & i).Value = Chr(k + 64) & Chr(j - 26 + 64)
j = j + 1
Else
j = 27
k = k + 1
GoTo G
End If
Next
You will have to put it in your code.
I have an input sheet called "Testfall-Input-Vorschlag where we have to choose a value from a dropdown in the cells of the first row from the 7th (J)column and when a value gets chosen for example "ARB13" I want to fill out the column where it is selected. The filling of the column is with random values. There is a Sheet called "Admin" which has values stored in the cells of columns from A:ZZ. Now I in the "Testfall-Input-Vorschlag" sheet I want to fill out the cells of the column sequentially. Which means for example for cell(11,7) i want to generate a random value from column A in "Admin" for cell (12,7) the value has to be from Column B in "Admin" for cell (13,7) the value is from column C in "Admin and so on. So I have been trying and I've come up with this code
Sub ARB13()
Dim col As Integer
For i = 11 To 382
For j = 7 To 1000
If Sheets("Testfall-Input_Vorschlag").Cells(1, j) = "ARB13" Then
col = 0
col = col + 1
LB = 2
UB = Sheets("Admin").Range("col" & Rows.Count).End(xlUp).Row
Cells(i, j).Select
ActiveCell.FormulaR1C1 = Sheets("Admin").Range("Y" & Int((UB - LB + 1) * Rnd + LB))
End If
Next j
Next i
End Sub
How can I update the col value for every i. Which means for every i I need col value to be increased by 1. Where am I going wrong?
Define col before you start your first loop, and don't put col = col + 1 in the For j = 7 to 1000 loop. Otherwise col will increment for every j instead of every i. Something like this:
Sub ARB13()
Dim col as Long
Dim i as Long
Dim j as Long
col = 0
For i = 11 To 382
For j = 7 to 1000
LB = 2
UB = Sheets("Admin").Cells(Rows.count, col).End(xlUp).row
Cells(i, j).Select
ActiveCell.FormulaR1C1 = Sheets("Admin").Range("Y" & Int((UB - LB + 1) * Rnd + LB))
Next j
col = col +1
Next i
End Sub
I have a large table, sometimes with hundreds of rows.
This table is generated by another application that exports to excel.
One column has the heading "Adjusted Price".
I want all the rows in this column to contain a formula (presently they're all 0's).
I want to automate this process because the table gets regenerated all the time.
This column may not always be in the same place. So I need a macro that can find this column ("Adjusted Price") and then fill all the cells in that column with a formula (with the exception of the first row of course).
Can this be done?
Thanks in advance :)
Your homework is to figure out how to plug it in!
Option Explicit
Sub setAdjustedPrice()
Dim column As Integer
Dim adjustedPriceColumn As String
Dim found As Boolean
Dim rowCount As Long
column = 1
rowCount = 1
Do While Range(FncAlphaCon(column) & rowCount).Value <> "" And found = False
If (Range(FncAlphaCon(column) & rowCount).Value = "Adjusted Price") Then
found = True
adjustedPriceColumn = FncAlphaCon(column)
Else
column = column + 1
End If
Loop
If found = True Then
Do While rowCount < ActiveSheet.UsedRange.Rows.count
rowCount = rowCount + 1
Range(adjustedPriceColumn & rowCount) = "YOUR FORMULA"
Loop
Else
MsgBox ("'Adjusted Price' column not found, cannot continue.")
End If
End Sub
Private Function FncAlphaCon(aNumber As Integer) As String
' Fixed version 27/10/2011
Dim letterArray As String
Dim iterations As Integer
letterArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
If aNumber <= 26 Then
FncAlphaCon = (Mid$(letterArray, aNumber, 1))
Else
If aNumber Mod 26 = 0 Then
iterations = Int(aNumber / 26)
FncAlphaCon = (Mid$(letterArray, iterations - 1, 1)) & (Mid$(letterArray, 26, 1))
Else
'we deliberately round down using 'Int' as anything with decimal places is not a full iteration.
iterations = Int(aNumber / 26)
FncAlphaCon = (Mid$(letterArray, iterations, 1)) & (Mid$(letterArray, (aNumber - (26 * iterations)), 1))
End If
End If
End Function
First time poster and new to programming in general. I have a project in which i have to build a financial model to mine for data in excel. I have succeeded in building said model on VBA. I have ran tests on 3,000 line dataset and it was successful. I will briefly explain what it does.
I track a given stock on a given day on multiple exchanges. I download the data (roughly 935,000 lines) The first step is copy all the data for a given exchange (roughly 290,000) onto a new sheet (this takes roughly 8min), then I create a new column to log the bid ask spread (12secs), the next step is what Im having trouble with, I basically rank each line of data twice, one column for Bid size and one column for Ask size. I created a function which uses excel Percentile function and ranks based on where the given bid and ask size lands. As of right now, I have been running the Macro for the last 35min and has yet to execute. I cant attempt the other macros since each macro depends on the previous one.
So my basic issue is that since my data set is large, my model keeps crashing.The code seems to be fine when working with the test data, and it doesn't throw any errors when I run the program, but with the larger data set it just crashes. Does anyone have any suggestions? Is this normal with such large amounts of data?
Thanks in advance.
Sham
Here is the sub and function thats giving me the trouble, the sub takes in the required inputs to run the function and then pops into the assigned cell. The code is suppose to repeat the process for three separate sheets. For now, Id like it to work on one sheet, hence used the comments to not include the loop
Sub Bucketting()
Dim firstRow As Long
Dim lastRow As Long
Dim counter As Long
Dim bidRange As Range
Dim offerRange As Range
Dim bidScroll As Range
Dim offerScroll As Range
Dim Ex As String
Dim i As Integer
'For i = 1 To 1 Step 1 'Sheet Selection Process
' If i = 1 Then
' Ex = "Z"
' ElseIf i = 2 Then
' Ex = "P"
' Else
' Ex = "T"
' End If
Sheets("Z").Select 'Sheet selected
With ActiveSheet
firstRow = .UsedRange.Cells(1).Row + 1
lastRow = .UsedRange.Rows.Count
Set bidRange = .Range("F2:F" & lastRow)
Set offerRange = .Range("G2:G" & lastRow)
For counter = lastRow To firstRow Step -1
Set bidScroll = .Range("F" & counter)
Set offerScroll = .Range("G" & counter)
With .Cells(counter, "J")
.Value = DECILE_RANK(bidRange, bidScroll)
End With
With .Cells(counter, "K")
.Value = DECILE_RANK(offerRange, offerScroll)
End With
Next counter
End With
Range("J1").Select
ActiveCell = "Bid Rank"
ActiveCell.Offset(0, 1) = "Offer Rank"
'Next i
End Sub
Function DECILE_RANK(DataRange, RefCell)
'Credit: BJRaid
'DECILE_RANK(The Range of data)
'Declares the function that can be called in the spreadsheet cell - enter '=DECILE_RANK(A5:A50,A5)
'Using the percentile worksheet function calculate where the 10th, 20th etc percentile of the reference range are
DEC1 = Application.WorksheetFunction.Percentile(DataRange, 0.1)
DEC2 = Application.WorksheetFunction.Percentile(DataRange, 0.2)
DEC3 = Application.WorksheetFunction.Percentile(DataRange, 0.3)
DEC4 = Application.WorksheetFunction.Percentile(DataRange, 0.4)
DEC5 = Application.WorksheetFunction.Percentile(DataRange, 0.5)
DEC6 = Application.WorksheetFunction.Percentile(DataRange, 0.6)
DEC7 = Application.WorksheetFunction.Percentile(DataRange, 0.7)
DEC8 = Application.WorksheetFunction.Percentile(DataRange, 0.8)
DEC9 = Application.WorksheetFunction.Percentile(DataRange, 0.9)
' Calculate the Decile rank that the reference cell value sits within
If (RefCell <= DEC1) Then DECILE_RANK = 1
If (RefCell > DEC1) And (RefCell <= DEC2) Then DECILE_RANK = 2
If (RefCell > DEC2) And (RefCell <= DEC3) Then DECILE_RANK = 3
If (RefCell > DEC3) And (RefCell <= DEC4) Then DECILE_RANK = 4
If (RefCell > DEC4) And (RefCell <= DEC5) Then DECILE_RANK = 5
If (RefCell > DEC5) And (RefCell <= DEC6) Then DECILE_RANK = 6
If (RefCell > DEC6) And (RefCell <= DEC7) Then DECILE_RANK = 7
If (RefCell > DEC7) And (RefCell <= DEC8) Then DECILE_RANK = 8
If (RefCell > DEC8) And (RefCell <= DEC9) Then DECILE_RANK = 9
If (RefCell > DEC9) Then DECILE_RANK = 10
End Function
935,000 lines is a lot for excel. Like, really a lot. Barring saying using a real database, If your application is literally putting a =Percentile(...) in each cell, I would recommend Trying to use another tool for that. Perhaps something within VBA itself. More generally, use something outside of a cell - then store the result value in the cell. There is a lot of overhead in maintaining those formulas that are interdependent on 935k rows of data.
The problem is that your looping through each row individually, the Excel way is to try and work with whole ranges at once whenever possible. I would load the ranges into arrays, then modify your DECILE_RANK code to work with the items in the array.
Note that variant arrays that read ranges in are 2-D.
Here is the fully functioning code including my custom VBA array slicer. Note that it was only tested on a small dataset:
Sub Bucketting()
Dim lastRow As Long
Dim bidArray As Variant
Dim offerArray As Variant
Sheets("Sheet1").Select 'Sheet selected
With ActiveSheet
lastRow = .UsedRange.Rows.Count + 1
bidArray = .Range("F2:F" & lastRow)
offerArray = .Range("G2:G" & lastRow)
Range("J2:J" & lastRow).Value = GetArraySlice2D(DECILE_RANK(bidArray), "column", 1, 1, 0)
Range("K2:K" & lastRow).Value = GetArraySlice2D(DECILE_RANK(offerArray), "column", 1, 1, 0)
End With
Range("J1").Select
ActiveCell = "Bid Rank"
ActiveCell.Offset(0, 1) = "Offer Rank"
End Sub
Function DECILE_RANK(DataRange As Variant) As Variant
' Credit: BJRaid
' DECILE_RANK(The Range of data)
' Declares the function that can be called in the spreadsheet cell - enter '=DECILE_RANK(A5:A50,A5)
Dim DEC(0 To 10) As Variant
Dim i As Integer, j As Integer
'Using the percentile worksheet function calculate where the 10th, 20th etc percentile of the reference range are
DEC(0) = 0
For i = 1 To 9
DEC(i) = Application.WorksheetFunction.Percentile(DataRange, 0.1 * i)
Next i
DEC(10) = Application.WorksheetFunction.Max(DataRange)
' Calculate the Decile rank that the reference cell value sits within
For i = 1 To UBound(DataRange, 1)
For j = 1 To 10
If ((DataRange(i, 1) > DEC(j - 1)) And (DataRange(i, 1) <= DEC(j))) Then
DataRange(i, 1) = j
Exit For
End If
Next j
Next i
DECILE_RANK = DataRange
End Function
Public Function GetArraySlice2D(Sarray As Variant, Stype As String, Sindex As Integer, Sstart As Integer, Sfinish As Integer) As Variant
' this function returns a slice of an array, Stype is either row or column
' Sstart is beginning of slice, Sfinish is end of slice (Sfinish = 0 means entire
' row or column is taken), Sindex is the row or column to be sliced (NOTE:
' 1 is always the first row or first column)
' an Sindex value of 0 means that the array is one dimensional 3/20/09 Lance Roberts
Dim vtemp() As Variant
Dim i As Integer
On Err GoTo ErrHandler
Select Case Sindex
Case 0
If Sfinish - Sstart = UBound(Sarray) - LBound(Sarray) Then
vtemp = Sarray
Else
ReDim vtemp(1 To Sfinish - Sstart + 1)
For i = 1 To Sfinish - Sstart + 1
vtemp(i) = Sarray(i + Sstart - 1)
Next i
End If
Case Else
Select Case Stype
Case "row"
If Sfinish = 0 Or (Sstart = LBound(Sarray, 2) And Sfinish = UBound(Sarray, 2)) Then
vtemp = Application.WorksheetFunction.Index(Sarray, Sindex, 0)
Else
ReDim vtemp(1 To Sfinish - Sstart + 1)
For i = 1 To Sfinish - Sstart + 1
vtemp(i) = Sarray(Sindex, i + Sstart - 1)
Next i
End If
Case "column"
If Sfinish = 0 Or (Sstart = LBound(Sarray, 1) And Sfinish = UBound(Sarray, 1)) Then
vtemp = Application.WorksheetFunction.Index(Sarray, 0, Sindex)
Else
ReDim vtemp(1 To Sfinish - Sstart + 1)
For i = 1 To Sfinish - Sstart + 1
vtemp(i) = Sarray(i + Sstart - 1, Sindex)
Next i
End If
End Select
End Select
GetArraySlice2D = vtemp
Exit Function
ErrHandler:
Dim M As Integer
M = MsgBox("Bad Array Input", vbOKOnly, "GetArraySlice2D")
End Function
I'm not sure if this will directly address your problem, but have you considered using Application.ScreenUpdating = False? Don't forget to set it back to true once your data has processed.