I am extremely new to excel vba and am using this first attempt as a learning experience. I am hoping to make a matrix of scatterplots in a separate sheet from the sheet they are taking the data from.
So heres a kind of schematic of the graphs I would like to be generated in an excel sheet. This represents a single satterplot [x-axis(ColumnletterRownumber), y-axis(ColumnletterRownumber)]
[(S2:S372),(AW2:AW372)] [(T2:T372),(AW2:AW372)] [(U2:U372),(AW2:AW372)]
[(S2:S372),(AX2:AX372)] [(T2:T372),(AX2:AX372)] [(U2:U372),(AX2:AX372)]
[(S2:S372),(AY2:AY372)] [(T2:T372),(AY2:AY372)] [(U2:U372),(AY2:AY372)]
[(S2:S372),(AZ2:AZ372)] [(T2:T372),(AZ2:AZ372)] [(U2:U372),(AZ2:AZ372)]
So those would be the scatterplots on the next sheet. Obviously I need a lot more graphs than that but that should give you an idea.
Here's what I got so far:
Sorry in advance for the large amount of commented out things... those are ideas I think might help but I haven't gotten them to work.
Sub SPlotMatrix1()
Application.ScreenUpdating = False
'SPlotMatrix1 Macro
'Define the Variables
'---------------------
Dim Xaxis As range
Dim Yaxis As range
''Initialize the Variables
''-------------------------
Set Xaxis = range("S2:S372")
Set Yaxis = range("AW2:AW372")
'Tell macro when to stop
'-----------------------
Dim spot As Long
spot = 0
Do Until spot > 50
Sheets("2ndFDAInterimData").Select
''MAIN LOOP
'Graph1
'-------
'Selection Range
range("S2:S372,AW2:AW372").Select
'range("Xaxis,Yaxis").Select
'range("AW1:AW372",S1:S372").Offset(0, rng).Select
'range("AW1:AW372", 0).Select
'range("0,S1:S372").Offset(0, rng).Select
range("S372").Activate
'Select Graph Range
ActiveSheet.Shapes.AddChart2(240, xlXYScatter).Select
' ActiveChart.SetSourceData Source:=range( _
"'2ndFDAInterimData'!$AW$1:$AW$372,'2ndFDAInterimData'!$S$1:$S$372")
'Graph Title
ActiveChart.SetElement (msoElementChartTitleAboveChart)
ActiveChart.FullSeriesCollection(1).Select
ActiveChart.FullSeriesCollection(1).name = "='2ndFDAInterimData'!$DL$1"
'Add Trendline
ActiveChart.Axes(xlValue).MajorGridlines.Select
ActiveChart.FullSeriesCollection(1).Trendlines.Add Type:=xlLinear, Forward _
:=0, Backward:=0, DisplayEquation:=0, DisplayRSquared:=0, name:= _
"Linear (Ave.Score)"
ActiveChart.FullSeriesCollection(1).Trendlines.Add Type:=xlLinear, Forward _
:=0, Backward:=0, DisplayEquation:=0, DisplayRSquared:=0, name:= _
"Linear (Ave.Score)"
ActiveChart.FullSeriesCollection(1).Trendlines(2).Select
Selection.DisplayRSquared = True
'Move Rsquare Label to Corner
ActiveChart.FullSeriesCollection(1).Trendlines(2).DataLabel.Select
Selection.Left = 30.114
Selection.Top = 13.546
'Format Trendline
ActiveChart.FullSeriesCollection(1).Trendlines(2).Select
With Selection.Format.Line
.Visible = msoTrue
.ForeColor.ObjectThemeColor = msoThemeColorText1
.ForeColor.TintAndShade = 0
.ForeColor.Brightness = 0
.Transparency = 0
End With
With Selection.Format.Line
.Visible = msoTrue
.DashStyle = msoLineSolid
End With
ActiveChart.ChartArea.Select
With Selection.Format.Line
.Visible = msoTrue
.Weight = 1.75
End With
'Resize Graph
ActiveChart.Parent.Height = 180
ActiveChart.Parent.Width = 239.76
'Y axis scale
ActiveChart.FullSeriesCollection(1).Select
ActiveChart.Axes(xlValue).Select
ActiveChart.Axes(xlValue).MaximumScale = 100
'Move graph to center (for the purposes of design and debugging)
ActiveChart.Parent.Cut
range("V4").Offset(spot, 0).Select
ActiveSheet.Paste
' 'Move Graph to other sheet
' ActiveChart.Parent.Cut
' Sheets("graphs").Select
' range("A1").Offset(spot, 0).Select
' ActiveSheet.Paste
spot = spot + 14
Loop
Application.ScreenUpdating = True
End Sub
I've gotten to the point where I am creating a number of the same graphs in a row or column if I want. But I can't successfully get the graphs ranges to change so that they are plotting different data.
Please help, let me know if I can further clarify. Thank you!
You can define the data with a couple simple loops. Create the chart and embellish it within the inner loop.
Sub InsertMultipleCharts()
' data particulars
Dim wksData As Worksheet
Const Xcol1 As Long = 19 ' column S
Const Xcol2 As Long = 21 ' column U
Const Ycol1 As Long = 49 ' column AW
Const Ycol2 As Long = 52 ' column AZ
Const Row1 As Long = 2
Const Row2 As Long = 372
' chart dimensions
Const FirstChartLeft As Long = 50
Const FirstChartTop As Long = 50
Const ChartHeight As Long = 180
Const ChartWidth As Long = 240
' working variables
Dim wksChart As Worksheet
Dim cht As Chart
Dim Xrange As Range
Dim Yrange As Range
Dim Xcol As Long
Dim Ycol As Long
' define sheets
Set wksData = ActiveSheet
Set wksChart = Worksheets.Add
' loop X
For Xcol = Xcol1 To Xcol2
' define x values
Set Xrange = Range(wksData.Cells(Row1, Xcol), wksData.Cells(Row2, Xcol))
' loop Y
For Ycol = Ycol1 To Ycol2
' define y values
Set Yrange = Range(wksData.Cells(Row1, Ycol), wksData.Cells(Row2, Ycol))
' insert chart
Set cht = wksChart.Shapes.AddChart2(Style:=240, XlChartType:=xlXYScatter, _
Left:=FirstChartLeft + (Xcol - Xcol1) * ChartWidth, _
Top:=FirstChartTop + (Ycol - Ycol1) * ChartHeight, _
Width:=ChartWidth, Height:=ChartHeight).Chart
' assign data to chart
cht.SetSourceData Source:=Union(Xrange, Yrange)
' chart title
cht.HasTitle = True
With cht.ChartTitle.Font
.Size = 12
.Bold = False
End With
' axis scale
cht.Axes(xlValue).MaximumScale = 100
' legend
cht.HasLegend = False
' series: name, trendline, and Rsquared
With cht.SeriesCollection(1)
.Name = "Series Name" '''' don't know where these are coming from
With .Trendlines.Add(Type:=xlLinear, DisplayRSquared:=True).DataLabel
.Format.Line.DashStyle = msoLineSolid
.Top = cht.PlotArea.InsideTop
.Left = cht.PlotArea.InsideLeft
End With
End With
Next
Next
End Sub
Macro recorder code is messy, but it gives you syntax.
Try using the macro recorder to edit an existing range so you get the code for setting the ranges for X, Y and the range name and size.
Once recorded you can swap out the new ranges as variables to get the new charts.
Related
I have several sheets in my workbook which contains data to plot, every time I run a new analysis a new sheet is generated.
On my first sheet I plot all the data in the same graph, so to avoid re plotting all the series every time I append a new sheet I would like to just add a new series.
I thought that should be simple, but it is not for two reasons: When I first create the chart it adds somewhere between 1 and 9 series automatically:
Set myChart = ws.Shapes.AddChart.Chart
myChart.ChartType = xlXYScatterLinesNoMarkers
why does this generate any random series?
also if I delete the graph because I want to rerun one analysis, the graph will then be called 2 and so on... So I tried to give it a name and refer to its name instead, however that does not work:
Set myChart = ws.ChartObjects(ws.Name)
So in the first sheet(Orginal) I plot all data in the workbook, and in the rest I just plot the data for the current sheet as seen below. I use the same code function for both cases, where i just pass the argument all as true(orginal sheet) or false(sheet 1.....300)
Below is the code:
Sub createChart(ws As Worksheet, Optional all As Boolean = False)
Dim lastRow As Long
Dim myChart As Chart
Dim temp As Integer
Dim n As Integer
On Error Resume Next
' Delete the charts, just in case
If ws.ChartObjects.Count > 0 Then ' And Not all Then
ws.ChartObjects.Delete
End If
'If ws.ChartObjects.Count = 0 Then
Set myChart = ws.Shapes.AddChart.Chart
myChart.Name = ws.Name
'Else
'Set myChart = ws.ChartObjects(ws.Name) '''Fails why commented out
'End If
myChart.ChartType = xlXYScatterLinesNoMarkers
myChart.SetElement (msoElementPrimaryCategoryGridLinesMinor)
myChart.SetElement (msoElementPrimaryValueGridLinesMinorMajor)
myChart.SetElement (msoElementLegendBottom)
myChart.SetElement (msoElementChartTitleCenteredOverlay)
myChart.Parent.width = 800 ' px width graph
myChart.Parent.height = 500 ' px height graph
' it adds mysterious sometimes several random series, so we need to delete those that does not match sheet name
For n = myChart.SeriesCollection.Count To 0 Step -1
If Not SheetExists(myChart.SeriesCollection(n).Name) Then
myChart.SeriesCollection(n).Delete
End If
Next n
'*******************************************************************
'**************** FIRST PAGE CHART *********************************
'*******************************************************************
If all Then
Dim wsOther As Worksheet
Dim i As Integer
Dim fixRange As Boolean
Dim skipGraph As Boolean
fixRange = True
myChart.HasLegend = True
myChart.Legend.Position = xlLegendPositionRight
myChart.Parent.Top = 120
myChart.Parent.Left = 450
For Each wsOther In ThisWorkbook.Worksheets
If wsOther.Name <> ws.Name Then
lastRow = getLastRow(wsOther, 1)
skipGraph = False
'******* we only add graphs if it is not before ******************
If myChart.SeriesCollection.Count > 0 Then
For n = myChart.SeriesCollection.Count To 1 Step -1
If myChart.SeriesCollection(n).Name = wsOther.Name Then
skipGraph = True
Exit For
End If
Next n
End If
If Not skipGraph Then
With myChart.SeriesCollection.NewSeries
.Values = "=" & wsOther.Name & "!$E$2:$E$" & lastRow
.Name = wsOther.Name
.XValues = "=" & wsOther.Name & "!$B$2:$B$" & lastRow
End With
End If
If fixRange Then
' Range on axis
myChart.Axes(xlPrimary).MinimumScale = CDate(Application.WorksheetFunction.Min(Range(wsOther.Name & "!$B$2:$B$" & lastRow).Value2))
myChart.Axes(xlPrimary).MaximumScale = CDate(Application.WorksheetFunction.Max(Range(wsOther.Name & "!$B$2:$B$" & lastRow).Value2))
myChart.Axes(xlValue, xlPrimary).ScaleType = xlLogarithmic
fixRange = False
End If
End If
Next
'*******************************************************************************************************
'****************** SINGLE CHART ***********************************************************************
'*******************************************************************************************************
Else
myChart.HasLegend = False
myChart.Parent.Top = 40
myChart.Parent.Left = 300
lastRow = getLastRow(ws, 1)
With myChart.SeriesCollection.NewSeries
.Values = "=" & ws.Name & "!$E$2:$E$" & lastRow
.XValues = "=" & ws.Name & "!$B$2:$B$" & lastRow
End With
' Range on axis
myChart.Axes(xlPrimary).MinimumScale = CDate(Application.WorksheetFunction.Min(Range(ws.Name & "!$B$2:$B$" & lastRow).Value2))
myChart.Axes(xlPrimary).MaximumScale = CDate(Application.WorksheetFunction.Max(Range(ws.Name & "!$B$2:$B$" & lastRow).Value2))
End If
' *********************************************************************
' ******************* Sizing ******************************************
' *********************************************************************
With myChart.PlotArea
temp = .Top
temp = .height
.Top = 70
.height = 420
End With
'really dirty and crappy formatting of title
myChart.ChartTitle.Text = "Faraday Torr"
'X axis name
myChart.Axes(xlCategory, xlPrimary).HasTitle = True
myChart.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Time [s]"
'y-axis name
myChart.Axes(xlValue, xlPrimary).HasTitle = True
myChart.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Pressure[Torr]"
Set myChart = Nothing
Set wsOther = Nothing
ws.Select
ws.Range("A1").Select
End Sub
The problem with the code below is this. I have a radar chart and a bar chart. I want a gap for a missing value for the radar chart, as a result, I need to delete the value in the cell to get the gap, however when I remove the cell value, the label goes missing for the bar chart. I have tried to create a new label and align it, but it does not work.
Option Explicit
Sub ChangeChartText()
Dim myChartObject As ChartObject
Dim mySrs As Series
Dim myPts As Points
Dim ws As Worksheet, x, vals
Dim cht As Chart, s As Series, p As Point, y, z
For Each ws In ActiveWorkbook.Worksheets
If Left(ws.name, 4) = "Page" Then
With ws
'For Each myChartObject In ws.ChartObjects
' For Each mySrs In myChartObject.Chart.SeriesCollection
For y = 1 To ws.ChartObjects.Count
Set cht = ws.ChartObjects(y).Chart
For z = 1 To cht.SeriesCollection.Count
Set s = cht.SeriesCollection(z)
vals = s.Values
For x = LBound(vals) To UBound(vals)
On Error Resume Next
If Not s.Points(x).DataLabel.Text Is Nothing Then
If IsEmpty(vals(x)) Then
s.Points(x).HasDataLabel = True
s.Points(x).DataLabel.Text = "N\A"
With s.Points(x).DataLabel
.HorizontalAlignment = xlTop
.VerticalAlignment = xlTop
.ReadingOrder = xlLTR
.Position = xlLabelPositionAbove
.Orientation = xlHorizontal
End With
ElseIf s.Points(x).DataLabel.Text = "N\A" And vals(x) <> 0 Then
s.Points(x).DataLabel.AutoText = True
End If
End If
Next x
Next z
Next y
' Next mySrs
'Next myChartObject
End With
End If
Next ws
End Sub
I have a loop creating 10 charts using Range Offset to increment 2 columns at a time.
The issue i have is the placement of each chart, currently they're stacking 10 charts on top of each other. Is there a way so each time the loop runs the chart position changes each time. Below is the code i currently have.
Sub charts()
'Set up the variables.
Dim rng As Range
'Use the InputBox dialog to set the range for MyFunction
Set rng = Application.InputBox("Select a range", "Obtain Range Object", Type:=8)
'Call MyFunction
ActiveCell.Value = MyFunction(rng)
End Sub
Function MyFunction(rng As Range) As Double
For i = 1 To 20 Step 2
Endrow = Range("A1").End(xlUp).Row - 1
Set Range1 = rng.Offset(Endrow, i + 1)
Sheets("All team charts").Select
MyFunction = ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlLineMarkers
ActiveChart.HasLegend = False
ActiveChart.HasTitle = True
ActiveChart.ChartTitle.Text = "Team 1"
ActiveChart.Parent.Name = "Team 1"
ActiveChart.SetSourceData Source:=Range1
With ActiveChart.Parent
.height = 200 ' resize
.width = 350 ' resize
.top = 20 ' reposition
.left = 20 ' reposition
End With
Next i
End Function
I did sine calculator in Excel.
I try to insert chart into sheet.
The chart should be sine wave with Y-axis as Amplitude, and X-axis as Time.
The problem is that I get a chart with two graphs: sine, according to Y-column, and line - according to the X-column.
Here my code:
Public oneTimeFlag As Integer
Sub calc()
Range("A3", Range("A2").End(xlDown)).Clear
Range("B2", Range("B2").End(xlDown)).Clear
Range("A2").Value = "0"
lw = Int(Range("$I$3").Value + 1)
If lw >= 4 And lw < 21000 Then
Range("A3").Select
ActiveCell.Formula = "=(2*PI()/$I$3)+A2"
Range("A3:A" & lw).FillDown
Range("B2").Select
ActiveCell.Formula = "=ROUNDDOWN(POWER(2,$G$2)/2 + SIN(($F$7*2*PI()/360) + A2)*((POWER(2,$G$2)/2) -1), 0)"
Range("B2:B" & lw).FillDown
AddOrUpdateChartSheet (lw)
Else
MsgBox "Nof points must be 4 at least and less than 21000!"
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$F$5" Or Target.Address = "$F$6" Or Target.Address = "$F$7" Then
Dim rng As Range
Set rng = Range(Selection.Address)
Call calc
rng.Select
End If
End Sub
Sub AddOrUpdateChartSheet(ByVal lw As Integer)
Dim chtChart As Chart
If oneTimeFlag <> 1 Then
oneTimeFlag = 1
Set chtChart = Charts.Add
Set chtChart = chtChart.Location(Where:=xlLocationAsObject, Name:="Sheet1")
With chtChart
.ChartType = xlLine
.SetSourceData (ActiveSheet.Range("A1:B" & lw))
.HasTitle = True
.ChartTitle.Text = "Sine"
With .Parent
.Name = "Sine"
End With
End With
Else
Dim objChrt As ChartObject
Dim sineChartExists As Boolean
sineChartExists = False
For Each objChrt In ActiveSheet.ChartObjects
If objChrt.Name = "Sine" Then
sineChartExists = True
End If
Next
If sineChartExists = False Then
oneTimeFlag = 0
AddOrUpdateChartSheet (lw)
Else
Set objChrt = ActiveSheet.ChartObjects("Sine")
Set chtChart = objChrt.Chart
With chtChart
.SetSourceData (ActiveSheet.Range("A1:B" & lw))
End With
End If
End If
End Sub
I get something similar to:
Chart with sine wave
Clock frequency, divider and DAC resolution are constants.
User changes the Needed frequency, amplitude and phase.
The sheet automatic calculates the number of points, calculates points (time, dac_value) and according to number of points creates the needed chart.
As mentioned above, as a result I get chart with two graphs (X-axis is number of point, Y-axis - is amplitude (DAC value)).
I need chart with one graph only (sine), with X-axis as Time (Column A), and with Y-axis as Amplitude (Column B).
Clear A1 before you create the chart and then reinstate the title afterwards:
Sub calc()
Range("A1", Range("A2").End(xlDown)).Clear
Range("B2", Range("B2").End(xlDown)).Clear
lw = Int(Range("$I$3").Value + 1)
If lw >= 4 And lw < 21000 Then
Range("A2").Value = "0"
Range("A3:A" & lw).Formula = "=(2*PI()/$I$3)+A2"
Range("B2:B" & lw).Formula = "=ROUNDDOWN(POWER(2,$G$2)/2 + SIN(($F$7*2*PI()/360) + A2)*((POWER(2,$G$2)/2) -1), 0)"
AddOrUpdateChartSheet lw
Range("A1").Value = "X (Time)"
Else
MsgBox "Nof points must be 4 at least and less than 21000!"
End If
End Sub
I am trying to create a macro in VBA that will take a large data set in Sheet1 (called Raw Data) and create a XY scatter plot for every 8000 data points in another worksheet. The macro will also need to label each graph with what range it represents (ie 1-8000, 8001-16000 etc).
The large data set consists of temperature readings from 8 different thermocouples which record data every second. The number of data points will vary based on how long the experiment was run. The temperature values are stored in columns C through J and the time parameter is in column T.
What I have right now is a "batch" approach where the macro is set up to graph data in chunks of 8000 up to 32000 (4 different plots). This approach is not practical because the data set will almost always be significantly larger than 32000 points.
What I would like the macro to do is automatically graph and label every 8000 data points until there is no more data to graph.
I have been looking into using a loop but I am new to writing code and not sure how.
Any suggestions or help is greatly appreciated!
Here's some of my batch code:
'creates graph for first 8000 seconds in TC 1
Sheets("TC 1").Select
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlXYScatterSmoothNoMarkers
ActiveChart.SeriesCollection.NewSeries
ActiveChart.SeriesCollection(1).Name = "='Raw Data'!$C$1"
ActiveChart.SeriesCollection(1).XValues = "='Raw Data'!$t$2:$t$8000"
ActiveChart.SeriesCollection(1).Values = "='Raw Data'!$C$2:$C$8000"
With ActiveChart
'X axis name
.axes(xlCategory, xlPrimary).HasTitle = True
.axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Time (seconds)"
'y-axis name
.axes(xlValue, xlPrimary).HasTitle = True
.axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Temperature (F)"
'chart title
.HasTitle = True
.ChartTitle.Text = ("1-8000 seconds")
'adjusts the size/placement of graph and x-axis values
Set RngToCover = ActiveSheet.Range("A1:T25")
Set ChtOb = ActiveChart.Parent
ChtOb.Height = RngToCover.Height ' resize
ChtOb.Width = RngToCover.Width ' resize
ChtOb.Top = RngToCover.Top ' repositon
ChtOb.Left = RngToCover.Left ' reposition
ActiveChart.axes(xlCategory).Select
ActiveChart.axes(xlCategory).MinimumScale = 0
ActiveChart.axes(xlCategory).MaximumScale = 8000
End With
Here is what I came up with.
The macro calculates the total number of used rows, then divides that number by 8000.
The For...Next loop runs from 0 to the total rows divided by 8000.
Dim i As Integer
Dim j As Variant
Dim p As Integer
Dim start_row As Long
Dim end_row As Long
Dim RngToCover As Range
Dim ChtOb As ChartObject
i = Worksheets("Raw Data").UsedRange.Rows.Count
j = i / 8000
Sheets("TC 1").Activate
For p = 0 To j
start_row = (p * 8000) + 2
end_row = ((p + 1) * 8000) + 1
Set ChtOb = ActiveSheet.ChartObjects.Add(Left:=20, Width:=800, Top:=20, Height:=250)
ChtOb.Chart.ChartType = xlXYScatterSmoothNoMarkers
ChtOb.Activate
With ActiveChart.SeriesCollection.NewSeries
.Name = Worksheets("Raw Data").Cells(1, 3)
.XValues = Worksheets("Raw Data").Range(Worksheets("Raw Data").Cells(start_row, 20), Worksheets("Raw Data").Cells(end_row, 20))
.Values = Worksheets("Raw Data").Range(Worksheets("Raw Data").Cells(start_row, 3), Worksheets("Raw Data").Cells(end_row, 3))
End With
Next
It sounds like you already understand how to generate the charts for a given 8000 records. Below is a WHILE loop to keep running your export code until it finds an empty cell in the source column for the X-axis (column T).
Dim i As Integer
Dim ws As Worksheet
i = 2
Set ws = ThisWorkbook.Worksheets("Raw Data")
While ws.Cells(i, 20).Value <> ""
''' Create Chart for Next Data Set Starting at Row i (up to 8000 records)
i = i + 8000
Wend