I have a spreadsheet that looks like this:
A 1/1/2013 100
A 2/1/2013 200
A 3/1/2013 300
B 1/1/2013 150
B 2/1/2013 175
B 3/1/2013 200
The three columns are fixed, but the number of entries for each series (first column) varies.
Using VBA, I'd like to automatically add each unique value in the first column as a series on a scatterplot, using the second column for the X values and third column for the Y values. For example, for Series A above, I would need to dynamically ascertain ranges B1:B3 and C1:C3.
I generated the code for adding the series to a chart using recorded macros, so my real obstacle is finding the ranges for each series. I need to retain the name of the series for association (first column), and would like to avoid using filters.
I eventually thought of a somewhat roundabout way of solving it; here's the code for anyone interested, adapted to the layout of my original question:
Sub Example()
' Variable Definitions
Dim y As Integer
Dim Name(73) As String
Dim Mins(73) As Integer
Dim Maxs(73) As Integer
' Loop Through Name Column (Series)
For Each x In Range("A4:A" & Cells(Rows.Count, "A").End(xlUp).Row)
' Add Values to Arrays, If Not Preexisting
If IsInArray(x.Value(), Name) = False Then
Name(y) = x.Value()
Mins(y) = x.Row()
Maxs(y) = x.Row()
y = y + 1
Else
Maxs(y - 1) = x.Row()
End If
Next x
' Add to Chart
ActiveSheet.ChartObjects("Chart 1").Activate
ActiveChart.SeriesCollection.NewSeries
ActiveChart.FullSeriesCollection(1).Name = Name(0)
ActiveChart.FullSeriesCollection(1).XValues = "=Data!$B$" & Mins(0) & ":$B$" & Maxs(0)
ActiveChart.FullSeriesCollection(1).Values = "=Data!$C$" & Mins(0) & ":$C$" & Maxs(0)
End Sub
' Array Function from #JimmyPena
' http://stackoverflow.com/a/11112305/2141501
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
Related
I would like to draw automatically accumulated inventory plot using Excel VBA 2016.
I have inventory data for different year in the format of "20XXYY". Here "XX" indicates year and "YY" indicates week number in a year (1 to 52). I will have data similar to picture 1. Then I would like to sort it as picture 2. Finally draw plot as picture 3. However, I would like to have it automatically using VBA.
My question is that how can I create sequential YearWeek column automatically for the plot?
I have used "=SUM($Y$2:Y2)"^^ formula in "Accumulated Inventory" column. I will highly appreciate if I will get any clue.
'This is the answer. I have figure it out. Thanks for your support.
Sub consecutiveNumber()
Dim wsFrom As Worksheet
Dim Com As String
Dim lLastNumber,FirstNumber, l, i As Long
'change to names of sheets you are coptying from and to
Set wsFrom = ThisWorkbook.Sheets("Sheet1")
'Get the value in the last and first used text box of column A
lLastNumber = wsFrom.Range("A" & Rows.Count).End(xlUp).Value
FirstNumber = wsFrom.Range("A1").Value
'clear column B
wsFrom.Range("B:B").Clear
'Initializing the cell index value
i = 1
'fill column B on sheet 1 with first to last number on column A
For l = FirstNumber To lLastNumber
'Converting number to string
Com = Right(CStr(l), 2)
If CInt(Com) >= 53 Then
l = l + 48
wsFrom.Range("M" & i) = l
i = i + 1
Else
wsFrom.Range("M" & i) = l
i = i + 1
End If
Next
End Sub
I need to print a string of arrays dependent on a difference of two values on my input page to separate sheets within the same PDF but I have been running into a few issues.
Based on the difference of two cells, the function will determine which arrays to print.
There are two possible solutions I have thought of but have been unsuccessful attempting both.
Indirectly reference a string of arrays in a cell to print such as "abc,bcd,cde,def,efg..."
(As Shown Below) Use conditional if-then functions to invoke the array based on the difference in these two cells
Primary Goals
Print into a single PDF
Determine specific arrays to print depending on the difference in two values contained in a cell on my input page
Allow for PageSetup values (have this figured out)
I am using MSFT 365. I tried initially using an indirect array reference to a cell with a variable value string including the arrays to be included without success.
Next, I tried to hardcode for all 100 possible values for this difference but in that case, I am running into line limits and errors associated with using _ to continue the array function on another line.
If the difference value equals 3, it is shown as below. If the difference value equals 4, you would add another array line including "schedule05","report05","p&l05"
Option Explicit
Sub PrintTest()
'if a certain difference value, use
If (Worksheets("Inputs").Range("D7") - Worksheets("Inputs").Range("D6")) = "3" Then
Dim pageArray As Variant
'set array for given difference
pageArray = Array("schedule01", "report01", "p&l01", _
"schedule02", "report02", "p&l02", _
"schedule03", "report03", "p&l03", _
"schedule04", "report04", "p&l04")
Worksheets("data").Activate
Worksheets("data").PageSetup.CenterHorizontally = True
'page setup values
With ActiveSheet.PageSetup
.FitToPagesWide = 1
.FitToPagesTall = 1
.Orientation = xlLandscape
End With
'call array for print
Worksheets("data").Range("pageArray").PrintOut
Elseif
'Here is where I could put another similar function for a difference of 4
'......
Else
'Here is where I could put another similar function for a difference of x
End If
End Sub
I expected this would get me a PDF where each of these arrays is printed on a separate sheet and will print a selection of arrays based on the difference value.
To expand on my comment, it would look like this:
Dim lDiff As Long
Dim pageArray As Variant
Dim sFormat As String
Dim i As Long, j As Long
'if a certain difference value, use
lDiff = Worksheets("Inputs").Range("D7").Value - Worksheets("Inputs").Range("D6").Value
ReDim pageArray(1 To (lDiff + 1) * 3)
For i = 1 To UBound(pageArray, 1) Step 3
j = j + 1
If j < 100 Then sFormat = "00" Else sFormat = "000"
pageArray(i) = "schedule" & Format(j, sFormat)
pageArray(i + 1) = "report" & Format(j, sFormat)
pageArray(i + 2) = "p&l" & Format(j, sFormat)
MsgBox pageArray(i)
Next i
I am trying to use some VBA code to copy a range of cells and paste its values in the next empty rows 2111 times.
This pastes successfully up to the 754507 row where after this it crashes.
I can see in the debug that it stops at the 1000th loop.
Option Explicit
Sub Paste_APIROWS()
Application.ScreenUpdating = False
Dim i As Long
Range("A2:H754").Copy
For i = 1 To 2111
Range("A2:H754" & i).PasteSpecial Paste:=xlPasteValues
Debug.Print i
Next i
Application.CutCopyMode = False
End Sub
I expect in the end to have about 1589583 rows but instead appear to be only getting about half of this.
The error message I get is "Run-time error '1004': Method 'Range' of object'_Global' failed"
Any advice would be greatly appreciated.
Many Thanks.
Run the loop in your head:
When i = 1, then the range is "A2:H7541" (Rows 2 through 7,541)
When i = 2, then the range is "A2:H7542" (Rows 2 through 7,542)
When i = 9, then the range is "A2:H7549" (Rows 2 through 7,549)
When i = 10, then the range is "A2:H75410" (Rows 2 through 75,410)
When i = 99, then the range is "A2:H75499" (Rows 2 through 75,499)
When i = 100, then the range is "A2:H754100" (Rows 2 through 754,100)
When i = 900, then the range is "A2:H754900" (Rows 2 through 754,900)
When i = 999, then the range is "A2:H754999" (Rows 2 through 754,999)
When i = 1000, then the range is "A2:H7541000" (Rows 2 through 7,541,000)
Notice as each value of i crosses each 10th power the row number increases by an order of magnitude:
From i = 9 to i = 10 you go from row 7,549 to 75,410
From i = 99 to i = 100 you go from row 75,499 to 754,100
From i = 999 to i = 1000 you go from row 754,100 to 7,541,000
Also note that your destination range row is always 2 - so on each iteration you're always overwriting yourself.
It crashes because Excel spreadsheets (since Excel 2007) cannot exceed 1,048,576 rows, hence the crash. The limit is 65,355 prior to Excel 2007 or when using a non-OOXML spreadsheet in modern versions of Excel).
I expect in the end to have about 1,589,583 rows but instead appear to be only getting about half of this.
Two things:
Excel does not support 1,589,583 rows anyway (as said, the maximum is 1,048,576).
Your logic does not compute copy destination ranges correctly, as per my explanation above.
The cause of your bug is the use of string concatenation (i.e. the & operator) instead of numerical addition.
You want to copy cells in the range A2:H754 some 2111 1930 times - that means you actually want to do this:
Const sourceRowLB = 2
Const sourceRowUB = 755 ' 755 not 754 because exclusive upper-bounds are easier to work with
Dim sourceRowCount = sourceRowUB - sourceRowLB
Dim lastCopyUB = 755
Dim sourceRangeExpr = GetRangeExpr( "A", sourceRowLB, "H", sourceRowUB ) ' Will be "A2:H754"
Range( sourceRangeExpr ).Copy
Const loopCount As Integer = 1389 ' This cannot be 2111 because ( 2111 * 754 ) exceeds the maximum row count
For i = 1 ToloopCount ' Loop 1389 times
' Recompute the destination range:
Dim destRowLB As Integer
destRowLB = lastCopyUB
Dim destRowUB As Integer
destRowUB = destRowLB + sourceRowCount
Dim rangeExpression As String
rangeExpression = GetRangeExpr( "A", destRowLB, "H" & destRowUB )
Range( rangeExpression ).PasteSpecial Paste:=xlPasteValues
lastCopyUB = destRowUB
Next i
Function GetRangeExpr(startCol As String, startRow As Integer, endCol As String, endRowExclUB As Integer) As String
GetRangeExpr = startCol & CStr( destRowLB ) & ":" & endCol & CStr( endRowExclUB - 1 ) ' We convert endRowExclUB to an inclusive upper-bound here
End Function
Here are some hints:
There is no need to do string math like Range("A2:H754" & i). A better solution is starting from the top left cell use .Cells(row, column) method to access a specific cell.
Expand a cell into a table using the .Resize(row_count, column_count) method.
Finally, there is no need to use the clipboard with the .Copy or .Paste methods are this is slow and memory intensive. Use direct assignment into the .Value property.
For example, to copy the 178th row from a table of 1000×8 cells located under A2 into the first row of the sheet, use the following
Range("A1").Resize(1,8).Value = Range("A2").Cells(178,1).Resize(1,8).Value
Note that the .Resize() values much match on both sides of the assignment.
I have a problem where I can't add numbers with decimals. Only numbers with no decimals.
I have written a code to sum up values from different cells. This work fine as long as the numbers are without decimals.
Here is my code:
Sub SumValues()
'This makro is made to add values together depending on
'x amount of Sheets in the workbook:
Application.ScreenUpdating = False
'A will sum up the values from each cell,
'depending on the amount of Sheets in the this Workbook:
A = 0
For I = 1 To ThisWorkbook.Sheets.Count
'Adding the values from cell E5 to Cells(5, 5 + (I - 1) * 3),
'with the distance 3 between each cell:
A = A + Cells(5, 5 + (I - 1) * 3)
Next I
'The values A that is added togheter from the cells, is now put in a cell:
Worksheets("Sheet1").Cells(1, 1).Formula = "=" & A & ""
Application.ScreenUpdating = True
End Sub
So for 3 number of sheets, "I" goes from 1 to 3.
So if my cells contain these numbers:
Cell(5,5) = 2
Cell(5,8) = 3
Cell(5,11) = 8
I get the sum in Cell(1,1) = 13
But if I have these values:
Cell(5,5) = 2,2
Cell(5,8) = 3
Cell(5,11) = 8
I get the "run-time error '9': Subscript out of range" Error message when running script.
Any suggestions?
Another question is if it is possible to get the formula into the cell I am adding up the values?
For Examlpe if I have 3 Sheets in my Workbook, it will sum up the values from Cell(5,5) , Cell(5,8) and Cell(5,11).
The sum is shown in Cell(1,1).
But all I get is the number, not the formula.
Is it possible to make the Cell show the formula "=E5+H5+K5"?
This last question might be a "fix" for the first question, if it is the separator "," that is making trouble, maybe?
Thanks
GingerBoy
Tested and working fine
Declare your variables
You need to qualify your objects with a worksheet
No need to toggle off Screen Updating here. You are just modifying one cell
This code will place the Value in A1 and the Formula in B1
Disclaimer:
Your code, and the code below, is subject to a potential Type Mismatch Error if you feed any cell with a non-numerical value into your loop. If there is some chance of any non-numerical cell being in the sum range, you can avoid the error by nesting something like the following inside your loop: If ISNUMERIC(Range) Then.
Sub SumValues()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim A As Double, i As Long, Loc As String
For i = 1 To ThisWorkbook.Sheets.Count
A = A + ws.Cells(5, (5 + (i - 1) * 3))
Loc = ws.Cells(5, (5 + (i - 1) * 3)).Address(False, False) & "+" & Loc
Next i
ws.Range("A1") = A
ws.Range("B1").Formula = "=" & Mid(Loc, 1, Len(Loc) - 1)
End Sub
I want to add number (0) to values and find a bit of trouble with that. Excels macro record this as
Sub Makro2()
ActiveSheet.ChartObjects("Chart 1").Activate
ActiveChart.SeriesCollection.NewSeries
ActiveChart.SeriesCollection(2).XValues = _
"='Sheet'!$C$221;'Sheet'!$C$223;'Sheet'!$C$225"
ActiveChart.SeriesCollection(2).Values = _
"='Sheet'!$B$222;'Sheet'!$B$224;'Sheet'!$B$226"
End Sub
But when I try the same with my code I get error.
Dim lineSeries1 As Range
Dim lineSeries2 As Range
With ActiveChart.SeriesCollection.NewSeries
.Values = "={0;100}" 'It works
.Name = ""
.XValues = "={0;100}" 'It works
End With
With ActiveChart.SeriesCollection.NewSeries
.Values = lineSeries1 ' + {0} or & SomeCellWithZero.Address
.Name = ""
.XValues = lineSeries2 ' + {0} or & SomeCellWithZero.Address
End With
So the question is how to add zero to Values?
Personally I'd make my ranges one cell bigger and add a constant zero value. If that's not possible for some reason, what follows may help ;-)
Here's a slightly roundabout way to get there. It might be possible to do it with fewer steps but I can't see how yet.
I'm going to use a VBA function to build a new array from the original range with a zero included. I'm putting the zero at the start of the array, change the code for something different. Here's the VBA function:
Public Function PrependZero(rng As range)
Dim val As Variant, res As Variant
Dim idx As Long
val = Application.Transpose(rng.Columns(1).Value) ' get range values as a 1-dimensional array, assumes values are in a column
ReDim res(LBound(val) To UBound(val) + 1) ' array to hold the extended values
res(LBound(res)) = 0 ' add the fixed value
For idx = LBound(val) To UBound(val)
res(idx + 1) = val(idx) ' copy the other values
Next
PrependZero = res
End Function
Excel doesn't seem to like us using a VBA function in a series definition, so we need to add some indirection to fool it. Create a new named formula (Formulas...Define Name). I called mine YSeries and set the "Refers to" value to =PrependZero(Sheet1!$A$2:$A$6), using my Y Value range as the input to the function.
That named formula can be used in a chart series definition: set "Series Y Values" to [YourWorkbookNameHere]!YSeries (use whatever name you created above).
If you want to do the same to the X values, the same approach should work.