Missing portion of the graph in VBA when 2 or more separate graphs are plotted when same data are used - excel

When I plot my chart using one set of data(current 1), the graph shows the correct output. However when I plot 2 sets of data concurrently(current 1 and current 2), part of the graph is missing(circled in red) for both data consisting of current 1 and current 2. Btw the data are the same for both scenarios and no data are missing. As my data for them is huge, I can only show you the part of my data sample which looks like this as shown below for current 1 and current 2. I know the code for plotting graph of current1 only contains a lot of variables that you all might deem as the one that causes problem so let me clarify that totalsample1 and myarray values should be correct as they are the ones responsible for the data(Like the one shown in data sample for current1) for plotting the graph for current and all data for plotting are present in this case. So what could be the code that causes this problem and how to remedy it?
Data sample for current 1
Data sample for current 2
1st update:add wsf to range and cells
2nd update: replaced activechart with cht1
3rd update: Remove from For i = 2 To totalsample1 Step 1 till all the cht1.series collection
4th update: I make a simpler version of my code to plot graph for current 1.
5th update: And using the code suggested by #Dy.Lee, the ideal graph for current 1 is as shown.
Private Sub addgraph_Vramp1()
Application.ScreenUpdating = False
Dim i As Long
Dim wf As Workbook
Set wf = ActiveWorkbook
Dim wsf As Worksheet
Set wsf = wf.Worksheets("current1")
Dim shp1 As Shape
Dim Cht1 As Chart
Set shp1 = wsf.Shapes.AddChart
Set Cht1 = shp1.Chart
wsf.Activate
With Cht1
Cht1.SetSourceData Source:=wsf.Range("A1:BQ750")
Cht1.ChartType = xlXYScatterSmoothNoMarkers
Cht1.Axes(xlValue).ScaleType = xlLogarithmic
Cht1.Axes(xlValue).MaximumScale = 0.001
Cht1.Axes(xlValue).MinimumScale = 0.000000000000001
End With
With Cht1
.Legend.Delete
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Voltage"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Current"
End With
changes to the graph for the current 1 after using updated codes
changes to the graph after removing cht1.series collection(it still remains in the updated code just to let other knows what is being removed here)
Ideal graph :))))

Caught wrong range
Cht1.SeriesCollection(i).XValues = wsf.Range(Cells(2, 2 * i - 1), wsf.Cells(myarray(i + 1), 2 * i - 1))
To
Cht1.SeriesCollection(i).XValues = wsf.Range(wsf.Cells(2, 2 * i - 1), wsf.Cells(myarray(i + 1), 2 * i - 1))
This is an example of creating a chart using a parameterized procedure.
Sub test()
Dim Ws As Worksheet
Dim Ws2 As Worksheet
Set Ws = Sheets("current1")
Set Ws2 = Sheets("current2")
addgraph_Vramp1 Ws
addgraph_Vramp1 Ws2
End Sub
Private Sub addgraph_Vramp1(Ws As Worksheet)
Dim i As Long, c As Long
Dim shp As Shape
Dim Cht As Chart
Dim rngDB As Range, rngX As Range, rngY As Range
Dim Srs As Series
Set rngDB = Ws.UsedRange
c = rngDB.Columns.Count
Set shp = Ws.Shapes.AddChart
Set Cht = shp.Chart
With Cht
.ChartType = xlXYScatterSmoothNoMarkers
.HasLegend = False
For Each Srs In .SeriesCollection
Srs.Delete
Next Srs
For i = 1 To c Step 2
With Ws
Set rngX = Ws.Range(.Cells(2, i), .Cells(2, i).End(xlDown))
Set rngY = rngX.Offset(, 1)
End With
Set Srs = .SeriesCollection.NewSeries
With Srs
.XValues = rngX
.Values = rngY
End With
Next i
.Axes(xlValue).ScaleType = xlLogarithmic
.Axes(xlValue).MaximumScale = 0.001
.Axes(xlValue).MinimumScale = 0.000000000000001
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Voltage"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Current"
End With
End Sub

Related

Create multiple charts from dynamic columns in a table

I would like to create a macro that runs through a series of data in a table and is able to automatically create multiple formatted graphs from it.
Here is what I'm working with (below):
Sub MakeXYGraph()
'https://stackoverflow.com/questions/62285791/dynamically-select-cells-and-input-in-chart
Dim ws As Worksheet
Set ws = Sheet1 'This is the codename of the sheet where the data is
'For the test, deleting all the previous charts
Dim vChartObject As ChartObject
For Each vChartObject In ws.ChartObjects
vChartObject.Delete
Next vChartObject
'rngData is the range where the data are. It is assumed that nothing else is on the sheet than what you displ
Dim rngData As Range
Set rngData = ws.UsedRange.Offset(1).Resize(ws.UsedRange.Rows.Count - 1)
' Get the number of series
Dim iMaxSeries As Integer
iMaxSeries = Application.WorksheetFunction.Max(rngData.Columns(1))
' Is the actual Series, but in the sheet it called Point
Dim iPoint As Integer
'Used for setting the ranges for the series data
Dim lFirstRow As Long, lLastRow As Long, lFirstColumn As Long, lLastColumn As Long
lFirstColumn = rngData(1).Column
lLastColumn = rngData.Columns(rngData.Columns.Count).Column
'Creating the Chart
Dim cht As ChartObject
Set cht = ws.ChartObjects.Add(Left:=250, Width:=500, Top:=50, Height:=300)
With cht.Chart
.ChartType = xlXYScatterLines
'X axis name
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Vertical Displacement"
'Y-axis name
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Vertical Coordinate"
' deleting the unwanted series (Excel tries to find out the data, but no need for it.)
Do Until .SeriesCollection.Count = 0
.SeriesCollection(1).Delete
Loop
End With
For iPoint = 1 To iMaxSeries
'Search for the first occurence of the point
lFirstRow = rngData.Columns(1).Offset(-1).Find(what:=iPoint).Row
'Search for the first occurence of the second point -1 is the last of this point
If iPoint = iMaxSeries Then
lLastRow = rngData.Rows(rngData.Rows.Count).Row - 1
Else
lLastRow = rngData.Columns(1).Find(what:=iPoint + 1).Row - 1
End If
'Add the series
With cht.Chart.SeriesCollection.NewSeries
.XValues = ws.Range(Cells(lFirstRow, lFirstColumn + 1), Cells(lLastRow, lLastColumn - 1))
.Values = ws.Range(Cells(lFirstRow, lFirstColumn + 2), Cells(lLastRow, lLastColumn))
.Name = "Point " & CStr(iPoint)
End With
Next iPoint
End Sub
Which plots the vertical coordinate vs. vertical displacement columns from this table:
To create this graph:
However, as you can see from the image with the table, I have multiple columns, and I would like to like to make graphs for several columns, all with the same format as the vertical coordinate vs. vertical displacement chart above, without interfering with the previous charts created. For example, the second graph that I would like to create is vertical coordinate vs. vertical stress. There is additional data on this worksheet, so one cannot just assume that the rest of the worksheet is blank.
One issue is that as you can see there are four different point numbers (1,2,3,4) and each point number is iterated 9 times. However, these numbers can change (for example there could be 8 Point numbers with three iterations each, and thus the data is dynamic and shouldn't just consider 4 Point No.'s with 9 iterations). And the table data will always be located starting from cell "C8". The current code deals with this.
The reason why the current code doesn't satisfy this is because it assumes that there is no other data on the worksheet where the table is (but there is). I want to be able to add more columns and create more charts (all of them plotted against vertical coordinate column) without affecting the other charts. Please if there is any way to modify the code so then I could create charts for several sets of data on the same worksheet then that would be much appreciated! I'm not sure what the best way to approach this is. Thank you.
https://drive.google.com/file/d/1cuW2eWYwrkNeJ-TmatiC4-PFodflNbSN/view?usp=sharing
Here's one approach:
Sub MakeXYGraph()
Const PLOT_HEIGHT As Long = 200
Const PLOT_WIDTH As Long = 300
Dim ws As Worksheet
Dim cht As ChartObject
Dim rngData As Range, rngHeaders As Range
Dim col As Long, posTop As Long, posLeft As Long
Dim ptRanges As Object, pt, dataRows As Range, i As Long
Set ws = Sheet1 'This is the codename of the sheet where the data is
For i = ws.ChartObjects.Count To 1 Step -1
ws.ChartObjects(i).Delete
Next i
Set rngData = ws.Range("C7").CurrentRegion
Set rngHeaders = rngData.Rows(1) 'the header row
Set rngData = rngData.Offset(1, 0).Resize(rngData.Rows.Count - 1) 'just the data
Set ptRanges = PointRanges(rngData.Columns(1))
posTop = ws.Range("M2").Top
posLeft = ws.Range("M2").Left
For col = 3 To rngData.Columns.Count
'add the chart
Set cht = NewChart(ws, posLeft, PLOT_WIDTH, posTop, PLOT_HEIGHT, rngHeaders.Cells(col).Value)
'loop over the keys of the dictionary containing the point numbers and corresponding ranges
For Each pt In ptRanges
Set dataRows = ptRanges(pt).EntireRow
With cht.Chart.SeriesCollection.NewSeries
.XValues = dataRows.Columns(rngData.Columns(col).Column)
.Values = dataRows.Columns(rngData.Columns(2).Column)
.Name = "Point " & pt
End With
Next pt
posTop = posTop + PLOT_HEIGHT
Next col
End Sub
'Scan the "point No" column and collect unique values and
' corresponding ranges in a Scripting Dictionary object
' assumes data is sorted by point no
Function PointRanges(pointsRange As Range) As Object
Dim dict As Object, c As Range, p, rng As Range
Set dict = CreateObject("scripting.dictionary")
For Each c In pointsRange.Cells
p = c.Value
If Not dict.exists(p) Then
dict.Add p, c 'add the start cell
Else
Set dict(p) = dict(p).Resize(dict(p).Count + 1) 'resize to add this cell
End If
Next c
Set PointRanges = dict
End Function
'add a chart and do some initial configuration
Function NewChart(ws As Worksheet, L, W, T, H, yAxisName As String)
Dim cht As ChartObject
Set cht = ws.ChartObjects.Add(Left:=L, Width:=W, Top:=T, Height:=H)
With cht.Chart
.ChartType = xlXYScatterLines
.Axes(xlCategory, xlPrimary).HasTitle = True 'X axis name
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = yAxisName
.Axes(xlValue, xlPrimary).HasTitle = True 'Y-axis name
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Vertical Coordinate"
.Axes(xlValue, xlPrimary).ReversePlotOrder = True
Do While .SeriesCollection.Count > 0
.SeriesCollection(1).Delete
Loop
End With
Set NewChart = cht
End Function

Adding extra series to scattergraph

I am trying to create a code which adds series to a scatter-graph. The code runs but there is about 100 extra series of data added which were not specified. My vba skills are basic.
Dim DownSweep As Chart
Dim xrng As Range
Dim yrng As Range
Dim title As Range
Dim dsvt As Worksheet
Dim dst As Worksheet
Dim i As Integer
t = 1
CLEAN:
If t < ActiveWorkbook.Charts.Count + 1 Then
If ActiveWorkbook.Charts(t).Name = "DownSweep Graph" Then
Application.DisplayAlerts = False
ActiveWorkbook.Charts("DownSweep Graph").Delete
Application.DisplayAlerts = True
t = t + 1
GoTo CLEAN
End If
End If
Set dst = Worksheets("Template 2 - Down Sweep")
Set dsvt = Worksheets("DownSweep ViscosityTemperature")
Set xrng = dsvt.Range(dsvt.Range("C2"), dsvt.Range("C2").End(xlDown))
Set yrng = dsvt.Range(dsvt.Range("F2"), dsvt.Range("F2").End(xlDown))
Set title = dsvt.Range("F1")
dsvt.Range("E1").Select
Set DownSweep = Charts.Add
DownSweep.Name = "DownSweep Graph"
With DownSweep
.ChartType = xlXYScatter
.SeriesCollection.NewSeries
.SeriesCollection(1).XValues = xrng
.SeriesCollection(1).Values = yrng
.SeriesCollection(1).Name = title
End With
title = title.Offset(0, 1)
For i = 2 To 99
With DownSweep.SeriesCollection.NewSeries()
.XValues = xrng.Offset(0, i - 1).Value
.Values = yrng.Value
.Name = title
End With
title = title.Offset(0, i)
Next i
End Sub
How do I prevent this from happening?
Any help would be appreciated.
That's because you're selecting a cell within the source data prior to adding the chart. So it automatically sets that data as it's source and creates the series collection.
Therefore, either make sure that the active cell does not reside within the source data or use the following code to delete the existing series collection prior to adding your new series collection.
With DownSweep
Do While .SeriesCollection.Count > 0
.SeriesCollection(1).Delete
Loop
End With

how do i offset all the charts in the same worksheet in VBA?

Currently, all my charts are cramped together in the same spot in the same worksheet after running my code. So to view them i have to manually drag and move them to another spot. So is there a way such that i can place all the charts in a orderly manner as shown in expected output? If it is really impossible to do something like this, i am ok with offsetting the graph for every 20 cells even though it is abit inconvenient for viewing but still i attempted to do it but fail to make it happen when i include code with current output with the offsetting code.
Current output(looks like there is 1 chart but all the charts are in the same spot)
Below is the code for my current output
Sub plotgraphs()
'Call meangraph
Call sigmagraph
End Sub
Private Sub sigmagraph()
Dim i As Long, c As Long
Dim shp As Shape
Dim Cht As chart, co As Shape
Dim rngDB As Range, rngX As Range, rngY As Range
Dim Srs As Series
Dim ws As Worksheet
Set ws = Sheets("Data")
Set rngDB = ws.Range("A1").CurrentRegion
Set rngX = rngDB.Columns(1)
Set rngY = rngDB.Columns(4)
Do While Application.CountA(rngY) > 0
Set co = Worksheets("meangraphs").Shapes.AddChart
Set Cht = co.chart
With Cht
.ChartType = xlXYScatter
'remove any data which might have been
' picked up when adding the chart
Do While .SeriesCollection.Count > 0
.SeriesCollection(1).Delete
Loop
'add the data
With .SeriesCollection.NewSeries()
.XValues = rngX.Value
.Values = rngY.Value
End With
'formatting...
With Cht.Axes(xlValue)
.MinimumScale = 0
.MaximumScale = 0.5
.TickLabels.NumberFormat = "0.00E+00"
End With
Cht.Axes(xlCategory, xlPrimary).HasTitle = True
Cht.Axes(xlValue, xlPrimary).HasTitle = True
End With
Set rngY = rngY.Offset(0, 2) 'next y values
Loop
Code for offsetting chart for every 20 cells (fail to make it happen)
Dim OutSht As Worksheet
'
Dim PlaceInRange As Range
Set OutSht = ActiveWorkbook.Sheets("sigmagraphs") '<~~ Output sheet
Set PlaceInRange = OutSht.Range("B2:J21") '<~~ Output location
'
' To place charts at a distance between them
For Each chart In Sheets("sigmagraphs").ChartObjects
' OutSht.Paste PlaceInRange
' Code below changes the range itself to something 20 rows below
Set PlaceInRange = PlaceInRange.Offset(20, 0)
Next chart
Expected output
What you are looking for is the .Left and .Top properties of the Shape containing the Chart.
For example, a macro that would setup your charts into a 2-column grid would look like this:
Sub SetupChartsIntoGrid()
Const TopAnchor As Long = 50
Const LeftAnchor As Long = 50
Const HorizontalSpacing As Long = 10
Const VerticalSpacing As Long = 10
Const ChartHeight As Long = 211
Const ChartWidth As Long = 360
Dim shp As Shape
For Each shp In ActiveSheet.Shapes
If shp.Type = msoChart Then
Dim Counter As Long
Counter = Counter + 1
With shp
.Top = TopAnchor + (WorksheetFunction.RoundUp(Counter / 2, 0) - 1) * (VerticalSpacing + ChartHeight)
.Left = LeftAnchor + ((Counter + 1) Mod 2) * (HorizontalSpacing + ChartWidth)
End With
End If
Next
End Sub

How can I print different charts for different data with Excel or VBA?

I am trying to print one graph for each row/table, the graphs should keep the same format, only the data should change among graphs. I need to do it for about 120 rows/tables so I would like to avoid doing it manually, also, I would prefer not to use the sparklines as its format is not appropriate for the purpose of the analysis.
I have tried to use a dynamic chart with the combo button but when I change the filtered line, every copied graph changes as well, making it impossible to have at the same time graphs showing different rows - one solution may be pasting it as image but it is not optimal as I would like to check the data for each graph if needed.
Below I show the example for two different "items", in the original dataset there are about 350/400 rows and about 120 "items", every 3 rows create one graph.
The graphs I have created manually are: (one for each 3 rows)
for "item" xxx:
For "item" yyy:
I need to print this type of graph for other different ~120 "items", all at once.
In terms of showing the format of the chart, see the below for the first graph:
The blank is:
The second quartile is:
The third quartile:
and the "item", which is the black point in the graph:
Same reasoning for the second graph, but considering the three rows with yyy.
I hope you can help me!
Sorry for the big amount of images, but I wanted it to be clear!
Thank you in advance!
Best,
Ema
It is easy to create a typical procedure for creating a chart and use parameters to iterate over the sheet.
Sub makeCharts()
Dim Ws As Worksheet
Dim Cht As Chart, Shp As Shape
Dim obj As ChartObject
Dim Target As Range, rngShp As Range
Dim r As Long, n As Long, i As Long
Set Ws = ActiveSheet
For Each obj In Ws.ChartObjects
obj.Delete
Next
r = Ws.Range("a" & Rows.Count).End(xlUp).Row
n = 1
For i = 3 To r Step 3
Set rngShp = Ws.Range("k" & n).Resize(10, 8)
Set Target = Ws.Range("a" & i)
Set Shp = Ws.Shapes.AddChart
With Shp
.Top = rngShp.Top
.Left = rngShp.Left
.Width = rngShp.Width
.Height = rngShp.Height
End With
Set Cht = Shp.Chart
setCharts Target, Cht
n = n + 12
Next i
End Sub
Sub setCharts(Target As Range, Cht As Chart)
Dim Srs As Series
Dim vColor
Dim i As Integer
vColor = Array(RGB(246, 246, 246), RGB(255, 224, 140), RGB(47, 157, 39), RGB(0, 0, 0))
With Cht
.ChartType = xlColumnStacked
.HasLegend = False
.HasTitle = True
.ChartTitle.Text = Target.Value
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "OCF Percentiles"
.Axes(xlValue).MajorUnit = 20
For Each Srs In .SeriesCollection
Srs.Delete
Next Srs
For i = 0 To 2
Set Srs = .SeriesCollection.NewSeries
With Srs
.Values = Target.Offset(0, 1).Resize(3).Offset(0, i)
.XValues = Array("A", "D", "I")
.Format.Fill.ForeColor.RGB = vColor(i)
If i = 0 Then
.Format.Fill.Transparency = 1 '<~~~~~ Transparency was adjusted
End If
End With
Next i
Set Srs = .SeriesCollection.NewSeries
With Srs
.ChartType = xlXYScatter
.Values = Target.Offset(0, 4).Resize(1, 3)
.MarkerStyle = xlMarkerStyleSquare
.MarkerBackgroundColor = vColor(3) 'vbBlack
End With
End With
End Sub
Result image

Different colour between series VBA Scatter Graph

I have the following macro which plots a Scatter graph for three columns. One column (AL13, downwards) is on the x axis. How do I get it to plot the other two columns (AK and AM) onto the same scatter? Also in different colour to each other? Thank You
Sub Graphing()
Set rng4 = ActiveSheet.Range(Range("AP13"), Range("AV33"))
With ActiveSheet.ChartObjects.Add(Left:=rng4.Left, Width:=rng4.Width, Top:=rng4.Top, Height:=rng4.Height)
.Chart.ChartType = xlXYScatter
.Chart.HasLegend = False
.Chart.Axes(xlCategory).TickLabels.Font.Size = 18
.Chart.Axes(xlValue).TickLabels.Font.Size = 18
Set srs = .Chart.SeriesCollection.NewSeries
srs.Values = Range(Range("AK13"), Range("AK13").End(xlDown))
srs.XValues = Range(Range("AL13"), Range("AL13").End(xlDown))
srs.Values = Range(Range("AM13"), Range("AM13").End(xlDown))
End With
End Sub
I will repost the code that I revised for you above, thanks for crediting me :)
Sub Graphing()
'Declare all the variables to be used:'
Dim rng4 as Range
Dim srs as Series
Dim cht as Chart
Dim xVals as Range
Dim srsVals as Range
'Set the chart's data range:'
Set rng4 = ActiveSheet.Range(Range("AP13"), Range("AV33"))
'Set the range variable to contain the series values'
' You can later modify this to include any number of columns, and the '
' loop structure below will add each column as a series to the chart.'
Set srsVals = ActiveSheet.Range(Range("AL13"),Range("AM13").End(xlDown))
'Set the cht variable:'
Set cht= ActiveSheet.ChartObjects.Add(Left:=rng4.Left, Width:=rng4.Width, Top:=rng4.Top, Height:=rng4.Height).Chart
'Set the Range variable for xValues:
Set xVals = Range(Range("AK13"),Range("AK13").End(xlDown))
'Format the chart and add series to the chart by iterating over the columns in srsVals:'
With cht
.ChartType = xlXYScatter
.HasLegend = False
.Axes(xlCategory).TickLabels.Font.Size = 18
.Axes(xlValue).TickLabels.Font.Size = 18
'Create the series in a loop
For c = 1 to srsVal.Columns.Count
Set srs = .SeriesCollection.NewSeries
With srs
.Values = xVals
.XValues = Range(srsVals.Columns(c).Address)
.Name = "Series " & c '<-- Modify as needed.'
End With
Next
End With
End Sub
I found that if I set the series as two separate series then it will plot both and give them different colours. Not sure if it is the most efficient way of doing it but it works.
Sub Graphing()
'Declare all the variables to be used:'
Dim rng4 as Range
Dim srs as Series
Dim cht as Chart
Dim xVals as Range
Dim srsVals as Range
'Set the chart's data range:'
Set rng4 = ActiveSheet.Range(Range("AP13"), Range("AV33"))
'Set the range variable to contain the series values'
' You can later modify this to include any number of columns, and the '
' loop structure below will add each column as a series to the chart.'
Set srsVals = ActiveSheet.Range(Range("AL13"),Range("AM13").End(xlDown))
'Set the cht variable:'
Set cht= ActiveSheet.ChartObjects.Add(Left:=rng4.Left, Width:=rng4.Width, Top:=rng4.Top, Height:=rng4.Height).Chart
'Set the Range variable for xValues:
Set xVals = Range(Range("AK13"),Range("AK13").End(xlDown))
'Format the chart and add series to the chart by iterating over the columns in srsVals:'
With cht
.ChartType = xlXYScatter
.HasLegend = False
.Axes(xlCategory).TickLabels.Font.Size = 18
.Axes(xlValue).TickLabels.Font.Size = 18
'Create the series in a loop
For c = 1 to srsVal.Columns.Count
Set srs = .SeriesCollection.NewSeries
With srs
.Values = xVals
.XValues = Range(srsVals.Columns(c).Address)
.Name = "Series " & c '<-- Modify as needed.'
End With
Next
End With
End Sub

Resources