Increment column reference in formula - excel

I have data which I have to enter into a table that has the following layout:
I need to store it in another worksheet that has this layout:
I am trying to create a macro that will accomplish 3 things:
Copy the contents in the first table to a column in another worksheet
clear the original table data (but not the headers q, w, e, etc)
Increment the column reference so that the next time the macro is ran, it will copy the data into column C in the second table, and the third time into column D, and so on.
This is what I have tried:
Line 1: Sheets("sheet2").Range("B1").Value = Sheets("sheet1").Range("A5").Value
Line 2: Sheets("sheet1").Range("A5:D6").ClearContents
In order to accomplish 3), I have to manually change "A5" to "B5" in the code, and to do so for each cell (of which there are about 60). Is there a way to automate this?

You can use excel's offset() function.
To change A5 to B5 you can do
For x = 1 to N
Sheets("sheet2").Range("B1").Value = Sheets("sheet1").Range("A5").offset(0,x).Value
If you want to do it only once then you will not need the for loop.

Related

How do I take all values in a column and change their values

There are a lot of questions on how to multiply all values by some other cell or to move all values to another cell based on some value, but what I want is to take, in the example image below:
All the values that I have selected and divide by 2. I do not want another column, I just want to change all those values in the spread sheet and divide them by 2, the values themselves should change.
I have not found an answer for this any where and I sure it is super simple. For example, in:
base_damage_mod selected column, 0.03 would become: 0.015.
The only way I know to do this is manually, and that's a lot of work ...
Whats the easiest way to do this?
The easiest way to do this is by writing a macro, like in the following example:
Sub Divide_by_2()
For Each c In Selection:
If c <> "" Then
c.Value = c.Value / 2
End If
Next c
End Sub
In order to launch this, you need to select your cells (no need to copy, or press Ctrl+C), and then launch the macro.
As far as the source code is concerned, this is pretty obvious, except for the c <> "" part: I have added this in order to avoid the value 0 being filled in in empty cells.
Is there a way to do this without VBA, without macros?
Yes, there is, but it involves you creating a new column, in there type a formula, then copy the values of that formula into again another column and remove the first two columns, in other words: it's quite Messi :-)
If column C is empty (if not, temporarily insert a column), enter a 2 there next to every used column D item (*).
Copy all of column C, and "Paste Special" onto column D using Operation>Divide.
(*) If there are too many items to manually do the "2", copy this formula down column C =IF(ISBLANK(D1),"",2) and it will add them. After this, convert column C from formulas to values by copying it and using "Paste Values" to paste it back. (Special Operations won't work on formulas)

With openpyxl, nested list starts writing / appending to cell A2 not A1

I have an excel spreadsheet to calculate data that is added every week for comparison on the first sheet and each additional sheet is the raw data listed as W01 to W52 for each week. I've stripped down the code here to make the issue the only thing not working. In reality, I'm taking multiple CSVs and dumping them into a list, formatting the list and then I am trying to write that list into the first unused worksheet.
It is successfully finding the first empty worksheet, but then when it writes the data from the list into it, it is starting at A2 not A1.
So the worksheet already exists. I have tried deleting all the cells and clearing the contents within excel first, but it always starts at A2 not A1. What am I missing?
Note: If this helps, originally, I had just deleted last year's data from the worksheet, and when I left it like this, it would start adding the list's data on the first row after the deleted info, such as A52 or something. Deleting the contents of the sheet has "fixed" that issue so it is now only starting at A2 but I want to start it at A1.
If I manually add something to the A1 cell, it works, such as:
ws_week.cell(row=1, column=1).value = 'This should be A1'
So I think I can force it to write with a loop for the row number in a loop for the column number, but it seems like the below should be working.
import openpyxl
output_excel = 'KB Videos 2021.xlsx' #Excel Report
#opens excel report
wb = openpyxl.load_workbook(output_excel)
#find the first blank worksheet
for sheet in wb.sheetnames:
if sheet == 'Weekly Stats':
pass #ignore first worksheet i.e. calculations
elif wb[sheet]['A1'].value == None:
ws_week = wb[sheet]
break
test=[[1,2,3],['A','B','C'],[4,5,6],['a','b','c']]
for x in test:
ws_week.append(x)
wb.save(output_excel)
print('Populated ',ws_week.value)`enter code here`)
The 1,2,3 from the "test" list are being put into A2, B2, C2 when I expect them to be in A1, B1, C1.
What did I miss?
Worksheets should never really be considered as "blank". When appending to worksheets, openpyxl uses an internal counter that will start at the next below any existing cells. As openpyxl creates cells on demand, wb[sheet]['A1'].value will implicitly create the cell "A1" so that it can check the value. This is why data is subsequently appended from the second row. You can avoid this by deleting the row after the check, but you might also want to make the check a little more robust.
Here's the code I used to make it work:
I replaced:
for x in test:
ws_week.append(x)
with:
# calculate max rows and columns
maxr = len(test)
maxc = len(test[0]) #I could probably put this into the loop in case a column is different, but not with my data
for this_row in range (1, maxr + 1):
for this_column in range (1, maxc + 1):
cellsource = test[this_row-1][this_column-1]
ws_week.cell(row = this_row, column = this_column).value = cellsource

How do I write VBA script so that it will compare data?

We need a script to compare the dates in column D to the dates in column E.
If the date in column D is two days before the date in column E then we need column F to state that in the corresponding row to D. We have a range of 2 days before and 2 days after (shoulder days). We need to be able to easily reproduce this and have it be able to run when we import it into access. we have roughly 3300 unique days to check.
I have tried using datevalue or a formula but we need to be able to reproduce this in the future and the formula doesn't allow us to easily do that.
I will not provide a complete implementation, but I can show you the way how you can do it (either the Excel formula way or with VBA code). Both ways will automatically calculate the values you need so you can easily import it into Microsoft Access - the import will take the calculated values from the cells you've selected.
The first way, which is also the simplest way, is to do that with simple Excel formula, consider this picture showing how the formula will calculate the values in columns F and G:
(Note: It is showing German date format, but you can easily change that in Excel).
In column G it calculates the difference D2 - D1 and displays it in days.
Then, in column F there is a simple if condition to determine if the date D1 is less, greater or equal than D2. The Result is calculated automatically.
The formula in row 2 are:
Hint: You can drag and drop the formula to the rows 3 - n below, so you can create a lot of rows prefilled with that formula.
A second way is that you can write a VBA function for the (simple) calculation formula in column F:
Function CalcResult(D1 As Variant, D2 As Variant) As Variant
CalcResult = ""
Dim result As String
Dim diff As Single
diff = D2 - D1
If (diff > 0) Then
result = "D1 < D2"
ElseIf (diff = 0) Then
result = "D1 = D2"
ElseIf (diff < 0) Then
result = "D1 > D2"
End If
CalcResult = result
End Function
This needs to be entered in the VBA editor, which you can display if you press Alt+F11. After you have done that, press Alt+F11 again to close VBA and return to your Excel sheet.
Then, place the formula =CalcResult(E2; D2) in cell F2 as shown below:
Like in the previous example, you can drag & drop the formula to the rows 3 - n below, so you can create a lot of rows prefilled with that formula.
The advantage of the 2nd approach is that you can refine the function CalcResult later without having to change the cells again (as it is the case in the first example).
Note: The function above needs to be in a separate module and you need to save the workbook as "Macro enabled workbook" later - otherwise you'll lose the VBA code.
A third way is to use the Excel macro recorder and record whatever you intend to do. It will create a public module with VBA code. If the recorder asks you, choose to store the code in the workbook.
Later you re-visit the generated VBA code and refine it - for example, put a for loop around it to automate things you've recorded once.
This approach is good for creating a "Calculate" button and put some logic behind it.

How to set up Excel VBA Loop for vlookup

I have a dashboard (image below) where I manually add entries. Then there is a log (image below) where all entries are recorded with the help of IF and Vlookup functions.
I need a code so so that every output cell in the log finds through all the entries in the dashboard and gives the answer. I think loop for vlookup will be used.
[Edit]
Consider the Dasboard table as a discrete table where manually entries are posted.
Consider log table as a continues table where record of every hour for each date is kept. The entries from Dashboard table get posted to the log table. New Image attached New Image
I have entered this function in output column in the log table:
=IF( AND(H3=$B$3,I3>= $C$3,I3<$D$3) ,$E$3,0) + IF(AND(H3=$B$4,I3>=
$C$4,I3<$D$4) ,$E$4,0) + IF (AND(H3=$B$5,I3>= $C$5,I3<$D$5), $E$5,0)
This works fine for me for plotting the entries but the problem is for every row in the dashboard i have to add a new IF-And function in the above. so for example if i want to add the 4th row of dashboard to be sync with the log ill have to add
+If(AND(H3=$B$6,I3>=$C$6,I3<$D$6),$E$6,0)
I want every row in the dashboard to add automatically somehow with a loop like:
i = variable
= If (AND(H3=$B$i,I3>= $C$i,I3<$D$i), $E$i,0)
Only one i will be greater than 0 while the rest will be zero. so the function should return me the sum of all i rather than just the last iteration.
Maybe just filldown the formula manually? if insist with macro, something like
Sub test()
Range("K3").Value = "X"
Range("K3:K10").FillDown
End Sub
replace "X" with your formula, keep the " "
replace K10 with how far down you want
----edit----
let me break down below for you,
match(H3,B:B,0), will find the correct row in B that = H, in H3 case it finds B3
INDEX(B:E,MATCH(H3,B:B,0),2) -> now it find B3, index let you find C3 (notice the 2,3,4 in later codes, it means the column from B3)
and (I3>=...,J3>=...)now we got both start and end time, we use I and J to compare
if 3. is true, lookup the output column, else 0
=IF(AND(I3>=INDEX(B:E,MATCH(H3,B:B,0),2),J3>=INDEX(B:E,MATCH(H3,B:B,0),3)),INDEX(B:E,MATCH(H3,B:B,0),4),0)

Excel VBA changing my formulas in a table?

Has anyone come across a situation where Excel seems to manipulate your formulas.
I have a sheet where I have an Index value in Column A. The First row starts with any non zero Value. Subsequent rows in the column increment the value. Eg
A1 = 1000
A2= A1+ 1
A3= A2 + 1
and so on/
I have another column B whose values will either be blank or a formula pointing to column A(usually the subsequent rows)
Eg:
B1.Formula = "=A2"
B2.Formula = "=A3"
B3.Value = ""
B4.value = "=A6"
Now I have a backup-restore functionality that lets me write out the data/formulas to a text file and then read it back in another workbook.
In the case of columns A and B, I am checking if the text value starts with "=" and then set either the value or formula of that cell depending on whether there is a formula or not.
So far the functionality has worked fine. It lets me restore accurately.
Now, if I convert this data range to a table and modify the code accordingly the behaviour is strange. I am using the ListObject structure to refer to the table. So for Column B my restore code is:
If Left(soureString) = "=" Then
'This is a formula
Sheets("MySheet").ListObjects(1).ListColumns("Next").DataBodyRange(row).Formula = sourcestring
Else
'This is a value
Sheets("MySheet").ListObjects(1).ListColumns("Next").DataBodyRange(row).Value = soureString
End If
once I am done writing a row, I loop to the start and
Dim newRow AS listrow
Set newRow = Sheets("MySheet").Listrows.Add(AlwaysInsert:=False)
row = newRow.Index
But this time when I run the process. this is what I get:
B1.Formula = "=A5"
B2.Formula = "=A5"
B3.Value = ""
B4.value = "=A5"
Why are my formula values all changing to the same value when I use a table instead of a range?
I had the same issue when populating a ListObject (Table) from an Excel Add-in, setting AutoFillFormulasInLists was the solution.
My workaround is to save the current setting, set AutoFillFormulasInLists to false, populate the table with data, formulas etc, then set AutoFillFormulasInLists back to the original setting.
bool OriginalAutoFillFormulaInListsFlag = app.AutoCorrect.AutoFillFormulasInLists;
app.AutoCorrect.AutoFillFormulasInLists = false;
//[ListObject population code....]
if (OriginalAutoFillFormulaInListsFlag == true)
{
app.AutoCorrect.AutoFillFormulasInLists = true;
}
Hope this helps someone.
I faced a similar issue. Ideally you could tell excel to stop doing this but I haven't been able to figure out how. Supposedly doing the following is supposed to keep excel from copying the formulas:
xlApp.AutoCorrect.AutoFillFormulasInLists = false
but it didn't work for me.
Using the answer from this question How to create running total using Excel table structured references? helped me. It doesn't feel like the ideal solution but it does do the job.
I used this formula where Weight is a column name from my table. #This Row is a "Special item specifier" and has a special meaning. The syntax looks a little funky because it's what's called a Structured Reference:
=AVERAGE(INDEX([Weight],1):[[#This Row],[Weight]])
The INDEX([Weight],1) part gives the reference for the 1st row in the Weight column
While the [[#This Row],[Weight]] part gives the reference for the current row in the Weight column.
So for example, if Weight is column J, and the current row is, say, 7 then this is equivalent to
=AVERAGE(J1:J7)
and on the 8th row it will be equivalent to
=AVERAGE(J1:J8) and so on
I have found that the only way to solve the problem of formulas changing in Excel Tables when you insert in VBA is to insert at the first row of the table, NOT the bottom or the middle. You can sort after.
And I always select or reference the EntireRow to do my insert in the Worksheet object not in the table itself. I always put a table in its own Worksheet anyway using xx.Entirerow.Insert.

Resources