I am rather new to VBA so I would appreciate more thorough explanations of what you did to solve this minor issue.
I am tasked with creating a simple VBA program that takes an already sorted set of data (I learned about it and then used a VBA program to sort my data alphabetically already based on a particular column's values) and does the following two things with it:
Graphs a scatter plot with it (either within that sheet or in a separate sheet)
AND
Creates series with that scatter plot dynamically (meaning that scatter plot has series on it based on a particular column's value). I will not know how many series I need but I do know that since it's sorted, the column defining the type of data in that row will be in alphabetical order (and I assume the VBA program can first create a series with the first row's name and then not create more series until it finds a different name in that column until it is finished graphing the data)
an example of a simple 3 column table is shown below:
SERIES NAME ___X VALUE_____Y VALUE
A__________________1___________1
A__________________2___________2
A__________________3___________3
A__________________4___________4
B__________________5___________5
B__________________6___________6
B__________________7___________7
C__________________8___________8
C__________________9___________9
C__________________1___________1
(there could be more rows and more unique series names of course... )
so in this example, the graph is already sorted and I would like there to be a scatter plot with 3 series on it (A being the 1st, B being the 2nd, and 3 being the 3rd)
I have the code to create a scatter plot with one series so far but I've been stuck trying to figure this out (code shown below). Any help with explanation is greatly appreciated :D
Here's my code so far (without the dynamic series part ofc)
Sub creatingmyscatterplot()
'Dim aRng As Range
'Dim seriescheck As Range
Dim Chart1 As Chart
Set Chart1 = Charts.Add
'Set aRng = Selection.CurrentRegion
'Set aRng = aRng.Offset(1, 0).Resize(aRng.Rows.Count - 1)
'Set seriescheck = aRng.Resize(aRng.Rows.Count, 1)
'Dim seriesName As String, seriesData As Range
'These lines, as their names suggest, turn off screen refresh and recalculating the workbook's formulas before running the macro.
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With Chart1
.ChartType = xlXYScatterLines
.SeriesCollection.NewSeries
'This creates the graph
.SeriesCollection(1).Name = "=Sheet1!$A$2"
.SeriesCollection(1).XValues = "=Sheet1!$B$2:$B$26001"
.SeriesCollection(1).Values = "=Sheet1!$C$2:$C$26001"
'Titles
.HasTitle = True
.ChartTitle.Characters.Text = "X vs. Y"
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "SOME TEXT"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "SOME TEXT AS WELL"
.Axes(xlCategory).HasMajorGridlines = True
'Formatting
.Axes(xlCategory).HasMinorGridlines = False
.Axes(xlValue).HasMajorGridlines = True
.Axes(xlValue).HasMinorGridlines = False
.HasLegend = False
.Axes(xlValue).MaximumScale = 100
.Axes(xlValue).MinimumScale = 0
End With
'These lines, as their names suggest, turn off screen refresh and recalculating the workbook's formulas before running the macro.
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Sorry about the indentation of the first and last lines. I just wanted those lines to be in the code block instead of outside it.
THANKS :D
After collect range by your series name, and add series.
Sub creatingmyscatterplot()
Dim rngData() As Range, rngDB As Range
Dim Ws As Worksheet
Dim i As Long, n As Long
Set Ws = Sheets(1)
With Ws
Set rngDB = .Range("a2", .Range("a" & Rows.Count).End(xlUp))
End With
'By same value area, set rngData() array
n = 1
ReDim Preserve rngData(1 To n) 'dynamic array
For i = 1 To rngDB.Rows.Count
If rngData(n) Is Nothing Then
Set rngData(n) = rngDB(i)
Else
Set rngData(n) = Union(rngData(n), rngDB(i))
End If
If rngDB(i) <> rngDB(i + 1) Then
n = n + 1
ReDim Preserve rngData(1 To n)
End If
Next i
'Dim aRng As Range
'Dim seriescheck As Range
Dim Chart1 As Chart
Set Chart1 = Charts.Add
'Set aRng = Selection.CurrentRegion
'Set aRng = aRng.Offset(1, 0).Resize(aRng.Rows.Count - 1)
'Set seriescheck = aRng.Resize(aRng.Rows.Count, 1)
'Dim seriesName As String, seriesData As Range
'These lines, as their names suggest, turn off screen refresh and recalculating the workbook's formulas before running the macro.
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
With Chart1
.ChartType = xlXYScatterLines
'if your activecel in data range, series is created automatically Unintentionally. So,all series are to be deleted
For i = .SeriesCollection.Count To 1 Step -1
.SeriesCollection(i).Delete
Next i
'This creates the graph
For i = 1 To n - 1 'useful rngData()'s count is n -1
.SeriesCollection.NewSeries
.SeriesCollection(i).Name = rngData(i)(1)
.SeriesCollection(i).XValues = rngData(i).Offset(, 1)
.SeriesCollection(i).Values = rngData(i).Offset(, 2)
Next i
'Titles
.HasTitle = True
.ChartTitle.Characters.Text = "X vs. Y"
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "SOME TEXT"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "SOME TEXT AS WELL"
.Axes(xlCategory).HasMajorGridlines = True
'Formatting
.Axes(xlCategory).HasMinorGridlines = False
.Axes(xlValue).HasMajorGridlines = True
.Axes(xlValue).HasMinorGridlines = False
.HasLegend = False
.Axes(xlValue).MaximumScale = 100
.Axes(xlValue).MinimumScale = 0
End With
'These lines, as their names suggest, turn off screen refresh and recalculating the workbook's formulas before running the macro.
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
Related
I am attempting to plot multiple named ranges on to a chart. These named ranges (named for example Time_Series_1) are of the form:
=INDIRECT(Ranges!$AJ$3) where this argument Ranges!$AJ$3 is for example
="'Source Data'!"&ADDRESS(N3,(Comparisons!$G$28+5))&":"&ADDRESS(N3(Comparisons!$I$28+5))
The code I'm attempting to use to plot the these ranges (I have started with one as any more remain to be impossible as one is not working) is:
Sheets("Comparisons").Select
Sheet10.ChartObjects("Chart 2").Activate
Set Rng = range("Time_Series_1")
Set Date_Rng = range("Time_Series_Dates_1")
With ActiveChart
.SetSourceData Source:=Rng
.XValues = Date_Rng
End With
It's not working! The Chart remains blank. Completely empty. What am I not updating? What am I not activating?
Thanks
I have no way of testing your model, but try this:
Dim Chart_Sheet As Worksheet
Dim My_Chart As ChartObject
Dim Rng As Range
Dim Date_Rng As Range
Set Chart_Sheet = ThisWorkbook.Sheets("Comparisons")
Set My_Chart = Chart_Sheet.ChartObjects("Chart 2")
Set Rng = Range("Time_Series_1")
Set Date_Rng = Range("Time_Series_Dates_1")
With My_Chart.Chart
.SetSourceData Source:=Rng
.SeriesCollection(1).XValues = Date_Rng
End With
(I am assuming that your Indirect reference works properly)
ActiveChart.SeriesCollection(1).XValues = Date_Rng
ActiveChart.SeriesCollection(1).Values = Rng
ActiveChart.Axes(xlCategory, xlPrimary).HasTitle = True
ActiveChart.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Dates"
ActiveChart.Axes(xlValue, xlPrimary).HasTitle = True
ActiveChart.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Pounds"
ActiveChart.Axes(xlCategory).HasMajorGridlines = True
I have written a macro to generate graph in excel but i want that graph to be generated in the next sheet not in the same sheet.i have pasted my macro below its working properly i just want it to be in the next sheet. please provide me some solution*
Sub LumData1()
If IsEmpty(Range("B2,D2")) = False Then
Range("B:B,D:D,H:H,I:I,J:J,M:M").Select
Range("M1").Activate
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlXYScatterSmoothNoMarkers
ActiveChart.SetSourceData Source:=Sheets(ActiveSheet.Name).Range( _
"$B:$B,$D:$D,$H:$H,$I:$I,$J:$J,$M:$M" _
)
ActiveChart.Location Where:=xlLocationAsObject, Name:=ActiveSheet.Name
With ActiveChart.ChartArea
.Width = 1060
.Height = 420
.Left = 0
End With
ActiveChart.SeriesCollection(1).Select
ActiveChart.SeriesCollection(1).AxisGroup = 2
ActiveChart.SeriesCollection(5).Select
ActiveChart.SeriesCollection(5).AxisGroup = 2
ActiveChart.Axes(xlCategory, xlPrimary).HasTitle = True
ActiveChart.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Date"
ActiveChart.Axes(xlValue, xlPrimary).HasTitle = True
ActiveChart.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Value"
ActiveChart.Axes(xlCategory).TickLabels.Orientation = 45
End If
End Sub
My answer covers not only how to solve your question in your post, but how to automatically define and set-up charts in VBA in a “cleaner” and safer way.
It's better if you avoid using ActiveSheet, Select, and ActiveChart. Instead always use referenced objects, in this case Worksheets and ChartObject. E.g Dim ChtObj As ChartObject , and later set it with Set ChtObj = ShtCht.ChartObjects.Add(100, 100, 500, 500) , where ShtCht is the worksheet where you create your chart.
Once you defined and set your ChartObject, its quite easy to modify its properties by using the With ChtObj , and also nested properties, by adding With .Chart.ChartArea under the first With statement.
Code
Option Explicit
Sub LumData1()
Dim ChtObj As ChartObject
Dim ShtSrc As Worksheet
Dim ShtCht As Worksheet
' change "Sheet1" to your sheet's name (where you have your chart's data)
Set ShtSrc = Worksheets("Sheet1") ' <-- I preffer not to work with ActiveSheet
'Set ShtCht = Worksheets("Sheet2") ' <-- set the chart's destination worksheet
Set ShtCht = Worksheets.Add ' <-- create a new worksheet to place the chart
ShtCht.Name = "Chart"
If Not IsEmpty(ShtSrc.Range("B2,D2")) Then
Set ChtObj = ShtCht.ChartObjects.Add(100, 100, 500, 500)
With ChtObj
.Chart.ChartType = xlXYScatterSmoothNoMarkers
.Chart.SetSourceData ShtSrc.Range("$B:$B,$D:$D,$H:$H,$I:$I,$J:$J,$M:$M")
' set position of the chart to Cell M1
.Top = ShtCht.Range("M1").Top
.Left = ShtCht.Range("M1").Left
' modify chart position and dimensions
With .Chart.ChartArea
.Width = 1060
.Height = 420
.Left = 0
End With
With .Chart
.SeriesCollection(1).AxisGroup = 2
.SeriesCollection(5).AxisGroup = 2
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Date"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Value"
.Axes(xlCategory).TickLabels.Orientation = 45
End With
End With
End If
End Sub
edited after OP's clarification he has to make a new sheet to host the chart into
to stick to your code as much as possible:
remove ActiveChart.Location Where:=xlLocationAsObject, Name:=ActiveSheet.Name
place the following code right before End if
Dim myChart As Chart: Set myChart = ActiveChart
Worksheets.Add
myChart.Location Where:=xlLocationAsObject, Name:=ActiveSheet.Name
My problem is that I want to make many graph in one chart but the data is from different sheets.
At the moment my code can only take multi data from one sheet, meaning I can plot 2 graph from one sheet.
My code at the moment is:
Sub ChartSheet()
Dim ChartSheet1 As Chart
Set ChartSheet1 = Charts.Add
With ChartSheet1
.SetSourceData Source:=Sheets("Sheet1").Range("E12:E6232, Y12:Y6232")
.ChartType = xlLine
.HasTitle = True
.ChartTitle.Characters.Text = "Test Chart"
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "x"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "y"
End With
End Sub
What I want is to say:
.SetSourceData Source:=Sheets("Sheet1").Range("E12:E6232, Y12:Y6232")
.SetSourceData Source:=Sheets("Sheet2").Range("D12:E23")
.SetSourceData Source:=Sheets("Sheet3").Range("Y12:Y6232, G27:G496, H3:5977")
and so on..
But when I do this my code it only print the last line from .SetSoureData
Hope some of you can help me work around this, Many thank in advarnce :)
Update:
I found abit of a work around by looping but this is not my total answer
But here is my other code:
Sub MultiSheetPlot()
Dim cht As Chart, s As Series, xRng As Range
Dim i As Long, chartName As String
Set cht = Charts.Add
cht.ChartType = xlLine
For i = 1 To 3
chartName = "Sheet" & i
Set xRng = Sheets("Sheet1").Range("A1:A20, C1:C20")
With cht.SeriesCollection.NewSeries()
.Values = xRng
.Name = chartName
End With
Next i
End Sub
the problem in this code is that it ignores the last range I define like the C10:C20
Responding to your latest update, I have a Sub that creates a series each time it's called from MultiSheetPlot Sub.
You can actually add more parameters to this Sub (as long as you remeber to pass them in the Calling).
Option Explicit
Dim cht As Chart
Sub MultiSheetPlot()
Set cht = Charts.Add
cht.ChartType = xlLine
' call the series creation chart function (each time for each series you want to add to the existing chart
Call Create_SeriesChart("Sheet1", Sheets("Sheet1").Range("E12:E6232"), 1, True, msoThemeColorText1)
Call Create_SeriesChart("Sheet1", Sheets("Sheet1").Range("Y12:Y6232"), 1, True, msoThemeColorText1)
End Sub
' ------ this Sub creates a series to the chart, it receives the following parameters: ------
' 1. seriesName - String
' 2. serValues - Range
' 3. lineWeight - Double (the weight of the line)
' 4. lineVis - Boolean (if you want to hide a certail series)
' 5. lineColor - MsoColorType (using the current's PC Theme colors
Sub Create_SeriesChart(seriesName As String, serValues As Range, lineWeight As Double, lineVis As Boolean, lineColor As MsoColorType)
Dim Ser As Series
Set Ser = cht.SeriesCollection.NewSeries
With Ser
.Name = seriesName
.Values = serValues
.Format.Line.Weight = lineWeight
If lineVis = True Then
.Format.Line.Visible = msoTrue
Else
.Format.Line.Visible = msoFalse
End If
.Format.Line.ForeColor.ObjectThemeColor = lineColor ' Line color Black
End With
End Sub
My solution is use Name of plage, you can give a name of your range
Like in my photo :
Everytime when you want to use these datas you just need call them
Like this plage i gived them name Poste_EtatDeLaDemande
When i want to use these elements i just call it like Range("Poste_EtatDeLaDemande") Excel will find it you don't need tell them where it is anymore ; )
I have to create almost 200 charts of time series. So I tried to write a macro that finishes most of the work I need to do.
I generated names for the time series like this as an example:
Name:= AKB_ExampleA
The name refers to a dynamic range which I declared with this formula:
=OFFSET('sheet1'!$C$7:$C$137;0;0;COUNT('sheet1'!$C$7:$C$206))
So now to the macro I coded so far:
Sub graphik_erstellen()
Call graphik1("AKB")
End Sub
Sub graphik(Name As String)
'
Dim Ch As Chart
Dim RngToCover As Range
Set Ch = charts.Add
Set Ch = Ch.Location(Where:=xlLocationAsObject, Name:="Charts")
With Ch
.ChartType = xlLine
.SetSourceData Source:=Range(Name & "_ExampleA")
.SeriesCollection(1).XValues = Range("Datum_Volumen")
.SeriesCollection(1).Name = "SERIES1"
.FullSeriesCollection(1).Select
With Selection.Format.Line
.Visible = msoTrue
.ForeColor.RGB = RGB(192, 0, 0)
.Transparency = 0
End With
.HasTitle = True
.ChartTitle.Text = Name & ", Volumen (nach Korrektur)"
.HasLegend = True
.Legend.Position = xlLegendPositionBottom
.Legend.Select
Selection.Format.TextFrame2.TextRange.Font.Size = 11
Selection.Format.TextFrame2.TextRange.Font.Bold = msoTrue
With .Parent
.top = 100
.left = 100
.height = 287.149606299
.width = 543.685039370078
.Name = Name & "_chart"
End With
End With
End Sub
My problem is, that if I do that, the dynamic range is not really considered. It takes the range of the name (which is $C$7:$C$137) but it should refer to the name itself (in order to be dynamic).
So if I click on the chart to see the series, the series values are declared as: ='sheet1'!$C$7:$C$137 instead of ='sheet1'!ExampleA.
I would be really, really grateful if somebody could help me out.
Best
Elio
I have rearranged a few lines of code and tried to place comments refering to them as well.
Let me know what works. Youjust might need to change SeriesCollection to FullSeriesCollection. Other than that the code works in my Excel 2010.
The first Sub I just get the Range size according to the data available in Column "C" from Row 7.
Let me know.
Option Explicit
Sub graphik_erstellen()
'You always want to use direct reference to a sheet/range chart
'Refering to the WorkBook they are in and the worksheet as well.
'especially if you are opening multiple WorkBooks / Sheets
Dim CurrentWorkSheet As Worksheet
Set CurrentWorkSheet = Workbooks("Book1").Worksheets("Sheet1")
'Dynamically finding the end of the data in Column C
Dim LastRow As Long
LastRow = CurrentWorkSheet.Cells(CurrentWorkSheet.Rows.Count, "C").End(xlUp).Row
'Setting the range using the document reference aswell
Dim AKB As Range
Set AKB = Workbooks("Book1").Worksheets("Sheet1").Range(Cells(7, "C"), Cells(LastRow, "C"))
Call graphik(AKB)
End Sub
Sub graphik(Name As Range)
Dim DataChart As Chart
Dim RngToCover As Range
Set DataChart = Workbooks("Book1").Charts.Add
'With Excel 2010 the line above will automatically add the chart as a sheet and not aobject in a sheet
'Set DataChart = DataChart.Location(Where:=xlLocationAsObject, Name:="Charts")
With DataChart
.Name = "Charts" ' This will be the Name of the CHart Tab
.ChartType = xlLine
.SetSourceData Source:=Name
'You can see below I avoided the Select and Selection
With .SeriesCollection(1)
'Using Offset I just used the data one cell to the left of the range
.XValues = Name.Offset(0, -1)
.Name = "SERIES1"
With .Format.Line
.Visible = msoTrue
.ForeColor.RGB = RGB(192, 0, 0)
.Transparency = 0
End With
End With
.HasTitle = True
.ChartTitle.Text = "MIDDEL TOP TEXT" 'Name & ", Volumen (nach Korrektur)"
.HasLegend = True
With .Legend
.Position = xlLegendPositionBottom
.Format.TextFrame2.TextRange.Font.Size = 11
.Format.TextFrame2.TextRange.Font.Bold = msoTrue
End With
'Not sure about this, it doesnt work in my Excel 2010
'
With .Parent
.Top = 100
.Left = 100
.Height = 287.149606299
.Width = 543.685039370078
.Name = Name & "_chart"
End With
End With
End Sub
Let me know what your intention is for the Sheet and Chart names and then I can help with getting that to what you need as well.
Here's the section of the code that is giving me trouble. I incorporated messagebox's to let me know that things are working up to that given point. So when I type "MsgBox xaxis.Address()" it comes out like $C$20:$C$42. Which is what im looking to graph on the x-axis. The issue is, my graph doesnt actually show up with that on the x-axis... it graphs like the entire sheet. I am guessing my issue lies with the syntax of xaxis that I am using.
Dim StartTime As Range
Dim EndTime As Range
Set StartTime = wb1.Sheets("Sheet2").Range("C:C").Find(wb1.Sheets("Sheet1").Range("$B$2").Text, MatchCase:=False, lookat:=xlWhole)
Set EndTime = wb1.Sheets("Sheet2").Range("C:C").Find(wb1.Sheets("Sheet1").Range("B3").Text, MatchCase:=False, lookat:=xlWhole)
'If Not StartTime Is Nothing Then
'End If
MsgBox StartTime.Address()
MsgBox EndTime.Address()
MsgBox StartTime.Value
MsgBox EndTime.Value
MsgBox "Hi"
Dim xaxis As Range
Dim yaxis As Range
Set xaxis = Range(StartTime.Address & ":" & EndTime.Address)
MsgBox xaxis.Address
'Set xaxis = Range("$C$16", Range("$C$16").End(xlDown))
Set yaxis = xaxis.Offset(0, ColumnOffset:=1)
MsgBox yaxis.Address
'''''''''''''''''''''''
'Dim x As String
'Dim y As String
' x = xaxis.Address
'MsgBox "Hi"
'y = yaxis.Address
'MsgBox x
Set Chrt = Charts.Add
With Chrt
.ChartType = xlXYScatter
.SeriesCollection.NewSeries
'.SeriesCollection(1).Name = "=""Scatter Chart"""
.SeriesCollection(1).Values = yaxis
.SeriesCollection(1).XValues = xaxis
'Titles
.HasTitle = True
.ChartTitle.Characters.Text = "Platen1"
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Time (Seconds)"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Temp (Deg. C)"
.Axes(xlCategory).HasMajorGridlines = True
'Formatting
.Axes(xlCategory).HasMinorGridlines = False
.Axes(xlValue).HasMajorGridlines = True
.Axes(xlValue).HasMinorGridlines = False
.HasLegend = False
End With
My overall goal: I have a spreadsheet with data that gets updated daily. I need the user to be able to input a start time, end time, and then machine number. This needs to be used to generate a graph from that data. The start and end time are always in column C, but the specific rows are always changing. So what I do is I locate the matching start time and end time from the data, and then I create this range (StartTime or EndTime). The machine will determine the column of data to use for the y-axis which im just offsetting from the x-axis.
Any help would be greatly appreciated.
Edit: I fixed the above by adding the following code right under the chart creation:
Do Until .SeriesCollection.Count = 0
.SeriesCollection(1).Delete
Loop
The issue i realize is that the "xaxis" and "yaxis" are grabbing data from Sheet1 while i want it to grab data from Sheet 2. How do I go about declaring that it should be graphing from Sheet2?
Do it like this
Set xaxis = Sheets("Sheet2").Range(StartTime.Address & ":" & EndTime.Address)
Set yaxis = xaxis.Offset(0, ColumnOffset:=1)