Loop in Excel VBA - loops

I have a logic in place, but no idea as to how to execute/code it in Excel. The logic is below:
At office, I need to find out the value of many old articles based on their purchase value and age. I don't want to use VDB or other inbuilt functions.
In my spreadsheet:
A1 = "Table" (Name of the article)
B1 = "01/01/2005" (Date of purchase in MM/DD/YYYY format)
C1 = "1000" (Purchase value)
D1 = "=DATEDIF(B1,TODAY(),"Y")" (Gives the age of article in years)
E1 = "20" (Percentage depreciation for first year, which varies based on article)
F1 = "=C1*10%" '(Depreciated value should not be less than 10% of purchase value)
Now, in G1 I want to calculate the depreciation value of article "A1" with purchase value "C1" for "D1" number of years, # flat "E1"% for first year and 10% for subsequent years.
H1 = "=C1-G1" (Value of the article after depreciation)
I1 = "=IF(H1<=F1,F1,H1)"
Please help me with a macro or formula to loop and find out the value of G1.
Also, I want to apply this to "n" number of rows since there are "n" number of articles.
EDIT
#assylias Thanks for enlightening me about SO policy, putting me to work for myself to find the answer.
After some 30 min of googling followed by trial and error, I successfully wrote a macro to do what I wanted. Here it goes:
Sub DepVal()
'
' DepVal Macro
' Macro by Prashanth JC
'
' Keyboard Shortcut: Ctrl+d
'
fYear = 0
dVal = 0
tYear = ActiveCell.Offset(0, -3)
purVal = ActiveCell.Offset(0, -4)
depFirst = ActiveCell.Offset(0, -2)
depOther = 10
If tYear >= 1 Then
Do
If fYear = 0 Then
dVal = purVal - (purVal * depFirst) / 100
Else
dVal = dVal - (dVal * depOther) / 100
End If
fYear = fYear + 1
Loop Until fYear = tYear
ActiveCell.Value = dVal
Else
ActiveCell.Value = purVal
End If
End Sub
Finally, I formatted the G1 cell to a Number with Zero decimal place. Everything works perfect now!
Thanks again, SO and assylias!

You don't need a macro, it's simple math.
Assuming that your percentage (in E1) is stored as .2 (for 20%), not actually 20, this formula would work:
=MAX(F1,C1*(1-E1)-(C1*0.1*(D1-1)))

Related

Macro to reconstruct receipts from database

I need some help with a database I am trying to create on excel. Currently, I managed to build a system where I paste on to the excel sheet a receipt, then a macro extract certain pieces of information and stores it in another sheet, something like this;
BUYER SELLER DATE PRODUCTS CURRENCY
A B 123 abc USD
D E 456 def GBP
Now, I can search this database using simple filtering. What I would to do now is, once I have filtered and am left with, lets say, 5 entries, I would like those to be reconstructed in another sheet, looking like the receipts do originally e.g
123 456
A D
B E
USD GBP
a d
b e
c f
I know I need to loop through each row and once in a row, loop through each column to extract the required piece of information(e.g date, products etc).
I have looked around and couldn't find anything.
Any help would be appreciated, thank you.
I think that this can help you to start:
Sub From_DB()
Dim i As Long
Dim col As Integer
Dim DB_Sheet, Rec_Sheet As Object
Set DB_Sheet = ThisWorkbook.Worksheets("Database")
Set Rec_Sheet = ThisWorkbook.Worksheets("Receipts")
col = 1
For i = 2 To DB_Sheet.Range("A" & Rows.Count).End(xlUp).Row
If DB_Sheet.Rows(i).Hidden = False Then
Rec_Sheet.Cells(1, col) = DB_Sheet.Cells(i, 3)
Rec_Sheet.Cells(2, col) = DB_Sheet.Cells(i, 1)
Rec_Sheet.Cells(3, col) = DB_Sheet.Cells(i, 2)
Rec_Sheet.Cells(4, col) = DB_Sheet.Cells(i, 5)
Rec_Sheet.Cells(5, col) = DB_Sheet.Cells(i, 4)
col = col + 1
End If
Next i
End Sub

Counting rows based on multiple criteria

I've been searching for a way to fill in column 'H' and 'I' of the attached image.
For 'H' I need to count every time the date and date shipped out don't equal for each item. It will be something like this:
(A:A <> D:D, B:B = $G$2, E:E <> "Base Inventory")
The second equation is to calculate the average lead time for each item:
Average(E:E - D:D) where B:B = $G$2, and D:D = "Complete" while ignoring rows with blanks
The table is much larger than the image and I can't use a helper row.
Thanks.
Example Sheet
For the first part, shipping calc, I wrote a function that will compute it for you:
Public Function Shippingcalc(item1 As Range, codes As Range, date2 As Range, shipped As Range, status As Range)
Dim Count As Integer
Count = 0
For n = 1 To codes.Rows.Count
If status.Cells(n, 1).Value <> "Base Inventory" Then
If codes.Cells(n, 1).Value = item1.Cells(1, 1).Value Then
If shipped.Cells(n, 1).Value <> 0 Then
If shipped.Cells(n, 1).Value <> date2.Cells(n, 1).Value Then
Count = Count + 1
End If
End If
End If
End If
Next n
Shippingcalc = Count
End Function
You have to implement that as a macro and macro-enable the workbook.
Then the function in H would look something like:
=Shippingcalc(G2, B$2:B$10, A$2:A$10, D$2:D$10, C$2:C$10)
And you can copy that (of course updating to the full column size).
Something similar should work for the other computation.
For some reason this morning while I was taking a poop, the equation for lead time came to me. It was way simpler than I imagined and goes back to high school math.
Average = (SUM(Received date) - SUM(Shipped date)) / Total
=(SUMIFS(E:E,B:B, G2,D:D,"<>",E:E,"<>") - SUMIFS(D:D,B:B, G2,D:D,"<>",E:E,"<>")) / (COUNTIFS(B:B, Q14, D:D,"<>",E:E,"<>")+1)
Thanks for your help guys.

Select max value from huge matrix of 30 years daily data

Lets say i have daily data for 30 years of period in a matrix. To make it simple just assume it has only 1 column and 10957 row indicates the days for 30 years. The year start in 2010. I want to find the max value for every year so that the output will be 1 column and 30 rows. Is there any automated way to program it in Matlab? currently im doing it manually where what i did was:
%for the first year
max(RAINFALL(1:365);
.
.
%for the 30th of year
max(RAINFALL(10593:10957);
It is exhausting to do it manually and i have quite few of same data sets. I used the code below to calculate mean and standard deviation for the 30 years. I tried modified the code to work for my task above but i couldn't succeed. Hope anyone can modify the code or suggest new way to me.
data = rand(32872,100); % replace with your data matrix
[nDays,nData] = size(data);
% let MATLAB construct the vector of dates and worry about things like leap
% year.
dayFirst = datenum(2010,1,1);
dayStamp = dayFirst:(dayFirst + nDays - 1);
dayVec = datevec(dayStamp);
year = dayVec(:,1);
uniqueYear = unique(year);
K = length(uniqueYear);
a = nan(1,K);
b = nan(1,K);
for k = 1:K
% use logical indexing to pick out the year
currentYear = year == uniqueYear(k);
a(k) = mean2(data(currentYear,:));
b(k) = std2(data(currentYear,:));
end
One possible approach:
Create a column containing the year of each data value, using datenum and datevec to take care of leap years.
Find the maximum for each year, with accumarray.
Code:
%// Example data:
RAINFALL = rand(10957,1); %// one column
start_year = 2010; %// data starts on January 1st of this year
%// Computations:
[year, ~] = datevec(datenum(start_year,1,1) + (0:size(RAINFALL,1)-1)); %// step 1
result = accumarray(year.'-start_year+1, RAINFALL.', [], #max); %// step 2
As a bonus: if you change #max in step 2 by either #mean or #std, guess what you get... much simpler than your code.
This may help You:
RAINFALL = rand(1,10957); % - Your data here
firstYear = 2010;
numberOfYears = 4;
cum = 0; % - cumulative factor
yearlyData = zeros(1,numberOfYears); % - this isnt really necessary
for i = 1 : numberOfYears
yearLength = datenum(firstYear+i,1,1) - datenum(firstYear + i - 1,1,1);
yearlyData(i) = max(RAINFALL(1 + cum : yearLength + cum));
cum = cum + yearLength;
end

VBA create loop for column B based on value in column A

I have a code that does what I need. The problem is I have to do the range select manually I'd like to create a conditional loop.
Here's my code that I used so far
Sub Worksheet_functions()
Dim Sumtotal As Long
Sumtotal = WorksheetFunction.Sum(Selection)
Range("e2").Value = Sumtotal
Range("h2").Value = Range("e2") + Range("h2")
End Sub
This is what my data looks like
Price Volume E H
10 100
10 50
10 80
9 100
9 50
8 100
8 100
10 50
10 250
At the moment I'm manually selection the volume column from the top down based on the value in price column.
The conditions are
If The prices are the same continue selecting them until they are not
If the price goes down then those selected cells should be summed in e4 and added to h4
If the price goes up then those selected cells should be summed in e2 and added to h4
For my example this would look like:
( H2: 100+50+80 +50+250 = 530 )
( H4: 100+50 +100+100 = 350 )
Price Volume
( 10 100
100+50+80 < ( 10 50
( 10 80
Above volume to H2
(9 100
100+50 < (9 50
Above volume to H4 ( because 10>9)
(8 100
100+100 < (8 100
Above volume to H4 ( because 9>8)
(10 50
50+250 < (10 250
Above volume to H2 ( because 8<10)
Any suggestions on
How do I use the conditions to make it loop?
How do write a code that takes in consideration the last price?
Here's a way to get what you want.
It is much difficult to create loop and Nested If's to do this.
I let Excel do the job and used Formula instead.
Let say your data is in A1:B10.
In E2 type this in formula bar.
=IF(A2=A3,IF(ISNUMBER(A1),IF(A1<A2,1,IF(A1=A2,E1,0)),1),IF(ISNUMBER(A1),IF(A1<A2,1,IF(A1=A2,E1,0)),1))
Copy the formula all the way down until E10 or up to where you have data.
In H2, type this in formula bar:
=SUMIF(E:E,1,B:B)
Similarly in H4, type this in formula bar:
=SUMIF(E:E,0,B:B)
However, you want code in VBA which is below also using above formula:
Dim ws as Worksheet, lrow as long
Set ws = Thisworkbook.Sheets("Sheet1")'assuming your data is in Sheet1
With ws
lrow = .Range("A" & .Rows.Count).End(xlUp).Row
.Range("E2:E" & lrow).Formula = "=IF(A2=A3,IF(ISNUMBER(A1),IF(A1<A2,1,IF(A1=A2,E1,0)),1),IF(ISNUMBER(A1),IF(A1<A2,1,IF(A1=A2,E1,0)),1))"
.Range("E2:E" & lrow).Value = .Range("E2:E" & lrow).Value
.Range("H2").Formula = "=SUMIF(E:E,1,B:B)"
.Range("H2").Value = .Range("H2").Value
.Range("H4").Formula = "=SUMIF(E:E,0,B:B)"
.Range("H4").Value = .Range("H4").Value
End With
End Sub
Kinda late, but I hope this is what you want.

Find average of a specific number of rows/columns in VB.Net Datatable and store to array

I am trying to program a noise reduction algorithm that works with a set of datapoints in a VB.NET DataTable after being helped with my other question. Basically, I want to take two integers, a coordinate value (yCoord for example) and a threshold smoothing value (NoiseThresh), and take the average of the values in the range of (yCoord - NoiseThresh, yCoord + NoiseThresh) and store that number into an array. I'd repeat that process for each column (in this example) and end up with a one-dimensional array of average values. My questions are:
1) Did anything I just say make any sense ;), and
2) Can anyone help me with the code? I've got very little experience working with databases.
Thanks!
An example of what I'm trying to do:
//My data (pretend it's a database)
1 4 4 9 2 //yCoord would equal 5
6 3 8 12 3 //yCoord = 4
8 3 -2 2 0 //yCoord = 3
9 17 3 7 5 //yCoord = 2
4 1 0 9 7 //yCoord = 1
//In this example, my yCoord will equal 3 and NoiseThresh = 1
//For the first column
Array(column1) = //average of the set of numbers centered at yCoord = 3 _
//(in this case 8) and the NoiseThresh=1 number on either side (here 6 & 9)
//For the second column
Array(column2) = //average of the numbers 3,3,17 for example
//etc., etc.,
This would be performed on a large data set (typical numbers would be yCoord=500, NoiseThresh = 50, Array length = 1092) so there is no possibility of manually entering the numbers.
I hope this helps clarify my question!
P.S.: yes, I know that // isn't a VB.NET comment.
I must admit that i've yet not understood the range part (NoiseThresh etc.), but this is a start:
Dim averages = (From col In tbl.Columns.Cast(Of DataColumn)()
Select tbl.AsEnumerable().
Average(Function(r) r.Field(Of Int32)(col.ColumnName))).
ToArray()
It calculates every average of each column in the DataTable and creates a Double() from the result (average can result in decimal places even if used on integers).
Edit: With your example i've now understood the range part. So basically yCord is the row-index(+1) and noiseThreas is the row-range (+/- n rows).
Then this gives you the correct result(made some code comments):
Dim yCord = 2 ' the row index(-1 since indices are 0-based) '
Dim noiseThresh = 1 ' +/- row '
' reverse all rows since your sample begins with index=5 and ends with index=1 '
Dim AVGs As Double() = (
From colIndex In Enumerable.Range(0, tbl.Columns.Count)
Select tbl.AsEnumerable().Reverse().
Where(Function(r, index) index >= yCord - noiseThresh _
AndAlso index <= yCord + noiseThresh).
Average(Function(r) r.Field(Of Int32)(colIndex))).ToArray()
The most important part of this this LINQ query is the Where. It applies your range on the IEnumerable(of DataRow). Then i'm calculating the average of these rows for every column. The last step is materializing the query to a Double().
Result:
(0) 7.666666666666667 Double => (6+8+9)/3
(1) 7.666666666666667 Double => (3+3+17)/3
(2) 3.0 Double => (8-2+3)/3
(3) 7.0 Double => (12+2+7)/3
(4) 2.6666666666666665 Double => (3+0+5)/3
Edit2:
One last thing. I assume that to do the same for the other axis I just
switch x & y and row & column?
It's not that simple. But have a look yourself:
Dim noiseThresh = 1 ' +/- column '
Dim xCord = 2 ' the column index(-1 since indices are 0-based) '
' assuming that your x-cords now start with index=0 and ends with tbl.Column.Count-1 '
Dim AVGs As Double() = (
From rowIndex In Enumerable.Range(0, tbl.Rows.Count)
Select tbl.Columns.Cast(Of DataColumn)().
Where(Function(c, colIndex) colIndex >= xCord - noiseThresh _
AndAlso colIndex <= xCord + noiseThresh).
Average(Function(c) tbl.Rows(rowIndex).Field(Of Int32)(c.Ordinal))).ToArray()
Result:
(0) 5.666666666666667 Double => (4+4+9)/3
(1) 7.666666666666667 Double => (3+8+12)/3
(2) 1.0 Double => (3-2+2)/3
(3) 9.0 Double => (17+3+7)/3
(4) 3.3333333333333335 Double => (1+0+9)/3

Resources