Goal Seek based off cells with formulas - vba

I'm trying to use goal seek across multiple sheets to updated the projects return on sale %. All of the cells have formulas and there is one cell that we hard code in order to tie back to our original return on sale %.
Below is the code I'm using, pretty straight forward.
for some reason when I run this macro its not giving me the desired value I would like for the return on sale %
Sheets("DEV INFAS WLKDN").Select
Range("F30").Select
Application.CutCopyMode = False
Application.CutCopyMode = False
Range("F30").GoalSeek Goal:=-0.002, ChangingCell:=Range("F19")
Sheets("NBN WLKDN").Select
Range("F30").Select
Application.CutCopyMode = False
Application.CutCopyMode = False
Range("F30").GoalSeek Goal:=-0.0062, ChangingCell:=Range("F19")
Sheets("MSIL LASER WLKDN").Select
Range("F30").Select
Application.CutCopyMode = False
Application.CutCopyMode = False
Range("F30").GoalSeek Goal:=-0.004, ChangingCell:=Range("F19")

Without knowing what value you're expecting and what its giving you, its hard to say why you might be getting an unexpected answer. The goal seek function in Excel is basically an iterative guessing function and is not guaranteed to return a globally optimal value. I don't have any documentation to support this claim but I'm pretty sure the goal seek function in Excel implements either a binary or golden-section search. To the best of my knowledge Excel doesn't specify what they are using under the hood, but these would be reasonable searches to use and looking into how they work might shed light on your issue.
Also, it looks like the length of your code could be reduced to
With ThisWorkbook
.Sheets("DEV INFAS WLKDN").Range("F30").GoalSeek Goal:=-0.002, ChangingCell:=Range("F19")
.Sheets("NBN WLKDN").Range("F30").GoalSeek Goal:=-0.0062, ChangingCell:=Range("F19")
.Sheets("MSIL LASER WLKDN").Range("F30").GoalSeek Goal:=-0.004, ChangingCell:=Range("F19")
End With
generally .Select is unnecessary and not recommended.

Related

Excel macro to normalize data

I am currently trying to create a macro for Excel in which a column containing certain values (numbers basically) will be displayed in a new column in a normalized way (highest number = 1, lowest number = 0).
Usually, I would just use the formula:
=(J2-MIN($J$2:$J$XXX))/(MAX($J$2:$J$XXX)-MIN($J$2:$J$XXX))
However, as the length of the column is dynamic and will change for each set of values, I cannot enter a value for XXX.
Now, I found out how to have a dynamic range (e.g.: numRows = Range(Selection, Selection.End(xlDown)).Rows.Count) but I did not manage to merge both functions.
I found a thread already in this site about normalization of data but I think it was a bit of a different story and this one here should be simpler.
I would appreciate any help! As I just started working with macros (2h ago) I would also appreciate if this will be in simple language.
EDIT:
First of all, thanks for the quick reply!
I naively tried making it work with this code:
Sub Normalize_TEST()
'
' Normalize_TEST Makro
'
'
Range("A1").Select
numRows = Range(Selection, Selection.End(xlDown)).Rows.Count
Range("K2").Select
ActiveCell.FormulaR1C1 = "=(("J2")-MIN($J$2:$J$numRows$))/((MAX($J$2:$J$numRows$)-MIN($J$2:$J$numRows$))
Range("K2").Select
Selection.AutoFill Destination:=Range(Cells(2, 11), Cells(numRows, 11))
End Sub
But it is not working and I get an error message ("error of compiling").
I just realize now that you are absolutely right, I don't even need VBA. Your line of =(J2-MIN($J:$J))/(MAX($J:$J)-MIN($J:$J)) works fine. I wasn't aware that with $j:$J it realizes to not include empty cells.
I simply used this code now for cell K2 and then did a VBA autofill function for the rest.
I think this can be closed.
Thank you #tigeravatar for your super quick help!

Shorten VBA code with repeat/loop

How do I shorten this code to repeat the task, but take less space? This code needs to run for like 27 more users. I know I am a total noob right now, but this would ease my workload for more than an hour daily...
Workbooks.Open ("https://blabla/Edgars/2017 Q3Q4/Edgars.xlsx")
Workbooks("Edgars.xlsx").Activate
Range("C24:AN27").Select
Selection.Copy
Workbooks("abc.xlsx").Activate
Sheets("Edgars").Activate
Range("C24:AN27").Select
ActiveSheet.Paste
Workbooks("Edgars.xlsx").Activate
Sheets("Edgars").Activate
Range("C33:AN36").Select
Selection.Copy
Workbooks("abc.xlsx").Activate
Sheets("Edgars").Activate
Range("C33:AN36").Select
ActiveSheet.Paste
Workbooks("Edgars.xlsx").Activate
Sheets("Edgars").Activate
Range("C42:AN45").Select
Selection.Copy
Workbooks("abc.xlsx").Activate
Sheets("Edgars").Activate
Range("C42:AN45").Select
ActiveSheet.Paste
Workbooks("Edgars.xlsx").Activate
Sheets("Edgars").Select
ActiveWindow.SelectedSheets.Delete
Workbooks("abc.xlsx").Activate
Sheets("Edgars").Select
Sheets("Edgars").Copy Before:=Workbooks("Edgars.xlsx"). _
Sheets(1)
Workbooks("Edgars.xlsx").Activate
Range("A1").Select
ActiveWorkbook.SaveAs Filename:="https://blabla/Edgars/2017 Q3Q4/Edgars.xlsx"
ActiveWorkbook.Close
Workbooks("abc.xlsx").Activate
Worksheets("TOOLS").Activate
Application.DisplayAlerts = True
Else
End If
Before you start tackling loops, or shortening the code, you will want to brush up on avoiding Select and Activate within VBA. Your code, in its current form, is difficult to read or improve because you are using a lot of implicit relationships.
For example:
Workbooks("Edgars.xlsx").Activate
Sheets("Edgars").Activate
Is actually:
Application.Workbooks("Edgars.xlsx").Activate
ActiveWorkbook.Sheets("Edgars").Activate
And can be changed to:
Application.Workbooks("Edgars.xlsx").Worksheets("Edgars").Activate
Of course, this doesnt eliminate the Activate problem, and you'll undoubtedly encounter an issue if 'Edgars.xlsx' is not an open workbook. This example isnt meant to solve your problem, it is only mean to point you in the right direction.
Once you refactor your code to eliminate the use of Activate, Select and the Implicit (unqualified) references, you can get yourself started with string variables. Something like this is valid code:
Dim UserName as String
UserName = "Edgars"
Application.Workbooks(UserName & ".xlsx").Worksheets(UserName).Activate
This allows your code greater flexibility, and gets you one step closer.
My recommendation would be to start here: How to avoid using Select in Excel VBA macros. Once you read this post, try implementing these steps within your code. Then read some more and try again.
Learning proper VBA takes a bit of a time investment, but if you can learn how to code VBA well you can save yourself much more time than just an hour (assuming you use Excel for more than just one process).
Best of luck!

VBA Paste values - Response time

I have a code that copies one range to another
rng1.Copy Destination:=rng2
However I only want the values, not all the formatting, so I have the below which works to achieve this
rng1.Copy
Application.ScreenUpdating = False
rng2.PasteSpecial Paste:=xlPasteValues
Application.ScreenUpdating = True
However, the processing speed of this is painfully slow. The sheet grinds to a halt for quite a long time compared to the instant response of the 1st formula.
Is there a method of amending the 1st formula to paste values only? I was unable to find a way in similar questions about pasting values....
Alternatively, is there another method that is quicker than using the 2nd formula?
copy & paste is notoriously slow in VBA, better to just assign directly, assuming both ranges are the same size
rng2.value = rng1.value

Optimizing VBA copy/paste functions and loops

I have a large Excel sheet. I'm interested in moving data from one cell to another. I have researched on how to optimize the copy/paste function to take as little time as possible and found that:
Sheet1.Range("A1").Value = Sheet1.Range("B1").Value
or
Sheet1.Range("A1").Copy Destination:=Sheet1.Range("B1")
is faster than:
Sheet1.Range("A1").Copy
Sheet1.Range("A1").PasteSpecial xlPasteValues
Application.CutCopyMode=False'Clear Clipboard
Now, I need to make a copy with the following options:
Range("A1").Select
Selection.Copy
Range("B1").Select
Selection.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=False
Is it possible to translate that into the code similar to one of the previous mentioned?
Also, in regard to loops. Is it generally faster to avoid them (if possible) or how is the Compiler optimized? Let me give you an example so you don't misunderstand me:
Lets say I want to Copy values from "A1:A200" to "B1:B200". Which method would then be better in general?
Dim counter As Integer
For counter = 1 To 200
Sheet1.Range("A" & CStr(counter)) = Sheet1.Range("B" & CStr(counter))
End counter
Or is it better to simply use the code:
Sheet1.Range("A1:A200").Copy Destination:=Sheet1.Range("B1:200")
These examples are of cause grossly simplified, though if someone could help me understand the principles applied, it would be of great value to me!
First of all don't use Select and Selection. This really slows your code. Especially in loops.
The fastest way to copy values is to simply do this Range("B1:B99").Value=Range("A1:A99").Value
Furthermore you should deactivate Screenupdating and especially if formulas referr to the paste area switch off the automated calculation of the worksheet: Application.ScreenUpdating=False
Application.Calculation=xlCalculationManual
In total it should somehow look like this:
Sub CopyFast ()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
ActiveSheet.Range("B1:B99") = ActiveSheet.Range("A1:A99")
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
But this really gets important when copying huge amounts of values e.g. in loops instead of the simple copying task in this example.

VBA .font.bold optimization

I am formatting two different reports using the same macro. The performance between the two spreadsheets is dramatically different. The macro functions instantaneously on the first report and takes about 10 seconds on the other. I have narrowed it down to the statement which makes cell A1 bold. When the bolding statement is omitted, both spreadsheets execute the macro instantaneously. I have tried the following methods of formatting cell A1.
Range("A1").Font.Bold = True
Range("A1:A1").Font.Bold = True
With Range("A1")
.Font.Bold = True
End With
ActiveSheet.Cells(1, 1).Font.Bold = True
As far as I know, .Font.Bold = True is the only way to make a cell bold, so the problem cannot be circumvented by using another command. The above are all the ways Google has suggested calling such action. Both spreadsheets are .xls extensions. Why might one spreadsheet's performance be dramatically different when executing the same statement?
There may be a lot of reasons. The main in my opinion being that the changes require ScreenUpdating and both Worksheets use a different amount of memory to compute your Worksheet layout e.g. having shapes, pivots, charts, hidden rows etc.
To workaround this turn off ScreenUpdating before your computations. Then turn it on again like below
Application.ScreenUpdating = False
'Your code here
Application.ScreenUpdating = True
Additionally to optimize your code utilize the following:
Turn off AutomaticCalculations
Save your file as binary Excel: .xslb