I would like to loop through four sets of data arranged in rows. I'd like to make a chart from each dataset and apply a trendline, let excel show the equation of the trendline and copy the "m" part of the equation of the trendline (y=mx+b) in a cell after the end of the row. I recorded a macro while doing the whole process with the first dataset and modified it a little to introduce the loop. My problem is that though the code creates the four charts with the trendlines and equations, but it copies the "m" value of the first chart after all the four lines. I tried to fix the problem, but failed. Now - in the same form, so I guess that it was the oroginal problem - this code prints after each dataset the first row of whatever is copied to the clipboarb from the code and after all the four datasets, and the remaining part of the copied part under it (only once).
It might seem to make no sense, so it is best to try this code in the following way: Fill the range C3:K6 with numbers and run the code. After, copy the code to the clipboard and run the code again.
So, my thwo questions are: 1. How to make the code to copy the "m" value of each dataset after them and 2. Why does it behave so crazy now?
Sub Lasttest()
Dim i As Integer
For i = 3 To 6
Range("C" & i).Select
ActiveCell.Range("A1:I1").Select
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlXYScatter
ActiveChart.SetSourceData Source:=ActiveCell.Range("Sheet1!A1:I1")
ActiveChart.SeriesCollection(1).Select
ActiveChart.SeriesCollection(1).Trendlines.Add
ActiveChart.SeriesCollection(1).Trendlines(1).Select
Selection.DisplayEquation = True
ActiveChart.SeriesCollection(1).Trendlines(1).DataLabel.Select
ActiveCell.Offset(0, 10).Range("A1").Select
ActiveSheet.Paste
Next
End Sub
Ferenc
Did some code cleanup and this works for me:
Sub InsertChartsAndPrintEquations()
Dim i As Integer
Dim rng As Range
For i = 3 To 6
Set rng = Range("C" & i & ":K" & i)
' insert chart
ActiveSheet.Shapes.AddChart.Select
With ActiveChart
.ChartType = xlXYScatter
.SetSourceData Source:=rng
With .SeriesCollection(1)
.Trendlines.Add
.Trendlines(1).DisplayRSquared = False
.Trendlines(1).DisplayEquation = True
End With
' grab & insert equation
With ActiveSheet.ChartObjects(i - 2)
.Activate
Range("M" & i) = .Chart.SeriesCollection(1).Trendlines(1).DataLabel.Text
End With
End With
Next
End Sub
Apparently, you have to use a range object when defining the source data and you have to activate the chart before you can grab the equation from it.
Edit #1
This code should be more robust:
Sub InsertChartsAndPrintEquations2()
Dim i As Integer
Dim rng As Range
Dim cht As ChartObject
' add charts
For i = 3 To 10
Set rng = Range("C" & i & ":K" & i)
ActiveSheet.Shapes.AddChart.Select
With ActiveChart
.ChartType = xlXYScatter
.SetSourceData Source:=rng
With .SeriesCollection(1)
.Trendlines.Add
.Trendlines(1).DisplayRSquared = False
.Trendlines(1).DisplayEquation = True
End With
End With
Next
' grab & insert equations
i = 3 ' set to same starting value as in the for next loop above
For Each cht In ActiveSheet.ChartObjects
cht.Activate
Range("M" & i) = cht.Chart.SeriesCollection(1).Trendlines(1).DataLabel.Text
i = i + 1
Next cht
End Sub
Related
I'm trying to create graphs of every column of data in a worksheet. As of right now it works as long as there are no gaps in the column of data, but I need it to be robust enough that it works if there are gaps in the data. The data is entered in batches with different columns having different lengths due to different measurement requirements. Each of the rows will also have an identifier in the first column indicating what batch of data that row comes from (see attached file). Since the identifier column will be the same length as the longest data column, I compare the last row of that to the bottom row of any given data column to make sure all the data is getting graphed. However right now the it gets stuck in the loop if there's a gap in the data.
Sub GraphAllColumns()
Dim col As Range 'The cell at the top of the data column
Dim bottomRow As Range
Dim bottomData As Range
Set col = ActiveSheet.Range("B7")
Set bottomRow = Range("A7").End(xlDown)
col.Select
If Not IsEmpty(Selection) Then 'If the worksheet is empty, nothing happens
Do
Set bottomData = Selection.End(xlDown)
If bottomRow.Row <= bottomData.Row Then
'Iterate through every column, select all the data in that column
'then call the create graph subroutine
Call CreateGraph
ActiveCell.Offset(0, 1).Select
Else
If IsEmpty(Selection.End(xlDown)) Then
Call CreateGraph
ActiveCell.Offset(0, 1).Select
Else
Range(Selection, Selection.End(xlDown)).Select
End If
End If
Loop Until IsEmpty(Selection)
End If
End Sub
Here's the CreateGraph subroutine as well. I'm happy the way that it works. I know it isn't the best way, but this is my first time using VBA.
Sub CreateGraph()
Dim startCell As Range 'Starting cell (important for column selection)
Dim graphRange As Range
Set startCell = Selection
Set graphRange = Range(startCell, startCell.End(xlDown)) 'Selects all data in column
'Create chart, define chart type and source data
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlLine
ActiveChart.SetSourceData Source:=graphRange
'Change chart location so that all charts on a sheet are stacked in top left corner
With ActiveChart.Parent
.Top = Range("A1")
.Left = Range("A1")
End With
'Change chart title and other attributes
With ActiveChart
.HasTitle = True
.ChartTitle.Text = startCell.Offset(-2, 0).Value
End With
End Sub
I may still be misunderstanding what you want, but this should get you started.
Sub PlotDataById()
Dim dict As Object, id, ws As Worksheet, rngId As Range
Set ws = ActiveSheet 'or whatever
Set dict = IdRanges(ws.Range("B3")) 'get the ranges for each id
For Each id In dict
Set rngId = dict(id).Offset(0, 1) 'first set of data
Debug.Print "Plotting id - " & id & ":" & rngId.Address
Do While Application.CountA(rngId) > 0
'use resize() to pass only the occupied range
CreateGraph rngId.Resize(Application.CountA(rngId)), id
Set rngId = rngId.Offset(0, 1) 'next column over
Loop
Next id
End Sub
'Capture the ranges occupied by each id in a list, starting at `startCell`
' Assumes list is sorted by id
Function IdRanges(startCell As Range) As Object
Dim c As Range, id, currId, cStart As Range, dict As Object
Set dict = CreateObject("scripting.dictionary")
currId = Chr(0) 'some non-value
Set c = startCell
Do While Len(c.Value) > 0
id = c.Value
If id <> currId Then
If Not cStart Is Nothing Then
dict.Add currId, c.Parent.Range(cStart, c.Offset(-1, 0))
End If
Set cStart = c
currId = id
End If
Set c = c.Offset(1, 0)
Loop
dict.Add currId, c.Parent.Range(cStart, c.Offset(-1, 0))
Set IdRanges = dict
End Function
'Create a plot of `rngData` with title `chtTitle`
Sub CreateGraph(rngData As Range, chtTitle)
Dim co As Shape, cht As Chart, ws As Worksheet
Set ws = rngData.Parent
Set co = ws.Shapes.AddChart
With co.Chart
.ChartType = xlLine
.SetSourceData Source:=rngData
.HasTitle = True
.ChartTitle.Text = chtTitle
End With
With co 'all charts on a sheet are stacked in top left corner
.Top = ws.Range("A1").Top
.Left = ws.Range("A1").Left
End With
End Sub
Using Select/ActiveCell is not a very robust way to structure your code, and typically you can avoid almost all uses of that approach.
I am a new user on Excel VBA, recently i encounter this error when ever i try to run my macro. What my macro do is by reading the cell row data and will create a chart by itself for export purpose, etc.
Below is my macro code
Sub CombinationChart()
Dim lastrow As String
Dim boundaryRow As String
ActiveSheet.Shapes.AddChart2(201, xlColumnClustered).Select
lastrow = mySheet.Range("A" & Rows.Count).End(xlUp).Row
boundaryRow = lastrow - 20
ActiveChart.SetSourceData Source:=Range("mySheet!$A$" & boundaryRow ":$B$" & lastrow) 'make sure the range is correct here
ActiveChart.FullSeriesCollection(1).ChartType = xlLine 'select which column should be the Line or the Column
ActiveChart.FullSeriesCollection(1).AxisGroup = 1
End Sub
The error part is here
ActiveChart.SetSourceData Source:=Range("mySheet!$A$" & boundaryRow ":$B$" & lastrow) 'make sure the range is correct here
My last row variable is the last row which contains data in the excel table whereas boundaryRow is a variable who gets the last row value and subtract it by 20 which is last row - 20 , but i just cant look for a way to put in my two variables into my ActiveChart.Range.
You have missed the concatenate operator (&) in your problem line.
Here is how it should look:
ActiveChart.SetSourceData Source:=Range("mySheet!$A$" & boundaryRow & ":$B$" & lastrow)
& was missing between boundaryRow and ":$B$"
Here's how to do what you want with a Range object variable. I'm assuming here - since you use it to calculate lastRow - that you have mySheet defined as a Worksheet object (probably globally).
Sub CombinationChart()
Dim lastrow As Long
Dim boundaryRow As Long
Dim chartData as Range
ActiveSheet.Shapes.AddChart2(201, xlColumnClustered).Select
lastrow = mySheet.Range("A" & Rows.Count).End(xlUp).Row 'mySheet is defined elsewhere
boundaryRow = lastrow - 20
Set chartData = mySheet.Cells(boundaryRow,1).Resize(21,2) ' 21 rows x 2 columns
ActiveChart.SetSourceData Source:=chartData 'make sure the range is correct here
ActiveChart.FullSeriesCollection(1).ChartType = xlLine 'select which column should be the Line or the Column
ActiveChart.FullSeriesCollection(1).AxisGroup = 1
End Sub
You could do this "on the fly" too, substituting in the expression I have assigned to chartData to the SetSourceData Source parameter.
I have a macro for creating a graph and part of it identifies and highlights the final data point like below:
This works all well and good when there's data in the final row of a column, but in some cases the final row is empty therefore no point is highlighted like so:
I was wondering if there was a way to make it highlight the last point that has actual data, so even though the last row may be empty, it highlights the last row with data.
Could the following be incorporated into my code? it finds the last data point in column B:
Dim lRow As Long
lRow = Cells(Rows.Count, 2).End(xlUp).Row
Here is my code:
With co.Chart
.FullSeriesCollection(1).ChartType = xlXYScatter
.FullSeriesCollection(1).AxisGroup = 1
.FullSeriesCollection(2).ChartType = xlLine
.FullSeriesCollection(2).AxisGroup = 1
.SetSourceData Source:=my_range
.Axes(xlCategory).TickLabels.NumberFormat = "m/yy"
'highlight final dot of data
.FullSeriesCollection(1).Points(.FullSeriesCollection(1).Points.Count).ApplyDataLabels Type:=xlShowValue
.FullSeriesCollection(1).Points(.FullSeriesCollection(1).Points.Count).MarkerSize = 7
.FullSeriesCollection(1).Points(.FullSeriesCollection(1).Points.Count).MarkerStyle = xlCircle
.FullSeriesCollection(1).Points(.FullSeriesCollection(1).Points.Count).MarkerBackgroundColorIndex = 6
.FullSeriesCollection(1).Points(.FullSeriesCollection(1).Points.Count).MarkerForegroundColorIndex = 1
.HasTitle = True
.ChartTitle.Text = t
ResolveSeriesnames co.Chart
.Location Where:=xlLocationAsObject, Name:="Graphs"
I found this code on https://peltiertech.com/label-last-point-for-excel-2007/ and made a couple adjustments which works
Sub LastPointLabel2()
Dim srs As Series
Dim iPts As Long
Dim cht As ChartObject
Dim vYVals As Variant
Dim vXVals As Variant
Set ws = ActiveSheet
If ActiveChart Is Nothing Then
MsgBox "Select a chart and try again.", vbExclamation
Else
Application.ScreenUpdating = False
For Each cht In ws.ChartObjects
Set srs = cht.Chart.SeriesCollection(1)
With srs
vYVals = .Values
'vXVals = .XValues
' clear existing labels
.HasDataLabels = False
For iPts = .Points.Count To 1 Step -1
If Not IsEmpty(vYVals(iPts)) Then
' add label
srs.Points(iPts).ApplyDataLabels _
ShowSeriesName:=False, _
ShowCategoryName:=False, ShowValue:=True, _
AutoText:=True, LegendKey:=False
Exit For
End If
Next
End With
Next
' legend is now unnecessary
Application.ScreenUpdating = True
End If
End Sub
I'm trying to make a macro that will run through an excel sheet and go through a set of rows and make a graph for each row.
I've got a bit of code that kind of does what I need, but puts it all on one graph when I need an individual graph for each row.
`Dim i As Integer
Dim ws As Worksheet
Set ws = Sheets("Master Sheet")
For Row = 1 To 20
Dim my_cell
Dim rng As Range
Set rng = Sheets("Master Sheet").Range("J8:Y8")
For Each my_cell In rng
If my_cell <> "" Then
ActiveSheet.Shapes.AddChart.Select
ActiveChart.SetSourceData Source:=Range("'Master Sheet'!$J$8:$Y$8")
ActiveChart.ChartType = xlLineMarkers
ActiveChart.Location Where:=xlLocationAsNewSheet
ActiveSheet.Activate
ActiveChart.PlotArea.Select
ActiveChart.SeriesCollection(1).XValues = "='Master Sheet'!$J$2:$Y$2"
ActiveChart.SeriesCollection(1).Name = "=""FP"""
ActiveChart.SeriesCollection.NewSeries
ActiveChart.SeriesCollection(2).Name = "=""Progress"""
ActiveChart.SeriesCollection(2).Values = _
"='Master Sheet'!$J$8,'Master Sheet'!$AF$8:$AH$8"
ActiveChart.DisplayBlanksAs = xlInterpolated
ActiveSheet.Activate
ActiveChart.ChartArea.Select
Else
Exit For ' Blank cell found, exiting
End If
Next
Next Row
End Sub`
If anyone can give me a hand to see where I'm going wrong that would be great.
Not really sure you have structured your For Next and For Each loops well. Ideally you'd like to step through ranges and actually use the value you define in your For Each statement. Without seeing your workbook I just adapted a small range of data to simulate creating a graph.
I just took your same code and generate a graph on the same worksheet for each row of numbers. You can take these principles and apply it to your logic.
Sub test()
Dim Row As Integer
Dim ws As Worksheet
Dim rng As Range
Set ws = Sheets("Sheet1") 'Change this to: Set ws = Sheets("Master Sheet")
For Row = 1 To 6
Set rng = ws.Range("B1:D1").Offset(Row, 0) 'Change to (I'm guessing here): ws.Range("$J$7:$Y$7").Offset(Row, 0)
ActiveSheet.Shapes.AddChart.Select
ActiveChart.SetSourceData Source:=Range(ws.Name & "!" & rng.Address)
ActiveChart.ChartType = xlLineMarkers
ActiveChart.PlotArea.Select
ActiveChart.SeriesCollection(1).XValues = "='Sheet1'!$B$1:$D$1" 'Change to "='Master Sheet'!$J$2:$Y$2"
ActiveChart.SeriesCollection(1).Name = ws.Range("A1").Offset(Row, 0).Value 'Change this to whatever you want to name the graphs. This is currently set to dynamicly name each graph by the series name set in Column A.
'ActiveChart.Location Where:=xlLocationAsNewSheet 'uncomment this line to put on new sheet
'ws.Select 'Need to go back to worksheet
Next Row
Set ws = nothing
Set rng = nothing
End Sub
Here are a couple links that may help;
Creating and positioning graphs in a for loop VBA and
Excel VBA: Chart-making macro that will loop through unique name groups and create corresponding charts?
Google also has many other links.
If I've misunderstood your question or need anything else please let me know.
Cheers
I'm trying to write a macro to create graphs in excel 2007. I don't know the number of cells that will be in the range for one of the series of data (it could be anywhere from 50 - 1000). I've googled this and I've found answers but they are all over the map and the few I've tried haven't helped me at all.
I'm a newb at vba macros but am an experienced programmer.
I've found examples such as:
Sub FindLast2()
x = ActiveSheet.UsedRange.Rows.Count
ActiveCell.SpecialCells(xlLastCell).Select
End Sub
I'm not sure if this works & if it does work how would I incorporate that into my macro
Here's my macro as it stands now:
Sub temp_graph_5()
'
' temp_graph_5 Macro
'
'
Sheets.Add After:=Sheets(Sheets.Count)
Sheets(2).Select
Sheets(2).Name = "Temperature"
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlLine
ActiveChart.SetSourceData Source:=Sheets(1). _
Range("B2:B324")
ActiveChart.SeriesCollection(1).Name = "=""Temperature"""
End Sub
The 'B324' is the section that I need to be variable.
Any help is greatly appreciated.
This code may help achieve what you need:
Sub temp_graph_5()
Dim myRng As Range
Dim lastCell As Long
//Get range to be plotted in chart
lastCell = Worksheets(1).Range("B2").End(xlDown).Row
Set myRng = Worksheets(1).Range("B2:B" & lastCell)
//Add worksheet and name as "Temperature"
Dim newSheet As Worksheet
Set newSheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
newSheet.Name = "Temperature"
newSheet.Select
//Add a new chart in Temperature and plot values from sheet 1
Charts.Add
With ActiveChart
.ChartType = xlLine
.SetSourceData Source:=myRng, PlotBy:=xlColumns
.Location Where:=xlLocationAsObject, Name:="Temperature"
End With
End Sub
sub test()
last_row_all = Range("A65536").End(xlUp).Row
msgbox last_row
end sub