Setting dates on X axis - vba

I'm trying to create a chart in Excel VBA and am having problems getting the X-Axis to display the dates correctly; the code is below:
Function CreateChart()
Dim objChart As Chart
ReDim detached_price(detachedProps.count - 1) As Double
ReDim detached_date(detachedProps.count - 1) As Date
ReDim semi_price(semiProps.count - 1) As Double
ReDim semi_date(semiProps.count - 1) As Date
Dim minDate As Date
Dim maxDate As Date
minDate = Date
Dim detachedCount As Integer
detachedCount = 0
Dim semiCount As Integer
semiCount = 0
Set objChart = Charts.Add
With objChart
.HasTitle = True
.ChartTitle.Characters.Text = "Price Paid"
.ChartType = xlXYScatter
.Location xlLocationAsNewSheet
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Date"
.Axes(xlCategory, xlPrimary).CategoryType = xlTimeScale
.Axes(xlCategory, xlPrimary).TickLabels.NumberFormat = "yyyy"
.Axes(xlCategory, xlPrimary).MinimumScaleIsAuto = True
.Axes(xlCategory, xlPrimary).MaximumScaleIsAuto = True
For Each prop In properties
Select Case prop.PropertyType
Case "Detached"
detached_price(detachedCount) = prop.Amount
detached_date(detachedCount) = prop.SellDate
detachedCount = detachedCount + 1
Case "Semi-detached"
semi_price(semiCount) = prop.Amount
semi_date(semiCount) = prop.SellDate
semiCount = semiCount + 1
End Select
If prop.SellDate < minDate Then
minDate = prop.SellDate
End If
If prop.SellDate > maxDate Then
maxDate = prop.SellDate
End If
Next
.SeriesCollection.NewSeries
.SeriesCollection(DETACHED).Name = "Detached"
.SeriesCollection(DETACHED).Values = detached_price
.SeriesCollection(DETACHED).XValues = detached_date
.SeriesCollection.NewSeries
.SeriesCollection(SEMI).Name = "Semi-Detached"
.SeriesCollection(SEMI).Values = semi_price
.SeriesCollection(SEMI).XValues = semi_date
End With End Function
The properties variable in the For..Each loop is populated, and fills the arrays correctly.
However, although the Scatter Graph data points are shown, the dates on the axis all show 1900.
I tried adding the lines:
.Axes(xlCategory, xlPrimary).MinimumScale = CDbl(minDate)
.Axes(xlCategory, xlPrimary).MaximumScale = CDbl(maxDate)
Which showed the correct years along the axis, but now all the data points for both series have disappeared.
I've tried a few other things, but it's been purely on a trial and error basis.
The data is as follows
The resulting charts are:
Correct dates, no data points
Incorrect dates, but we have data points

Even though I disagree with their definition of "common" date format :q, I think that #chiliNUT is on to something. There seems to be some problem with the coercion of the date format.
If you change all of your Date type variables to Double or Long, it should work.
For example change
ReDim detached_date(detachedProps.count - 1) As Date
to
ReDim detached_date(detachedProps.count - 1) As Double
or
ReDim detached_date(detachedProps.count - 1) As Long
This way the dates are not converted into String by the XValues method. They are stored as date serial numbers and the axis routine is able to coerce them successfully into the local date format.
Whats happening in your code is the Date types are coerced to String by the XValues method and the axis rendering routine seems to be unable to coerce them back into dates properly.
I don't think it is actually related to the international settings as I tried it using dates like this:
1/01/2013
1/01/2014
1/01/2015
1/01/2016
1/01/2017
1/01/2018
1/01/2019
1/01/2020
1/01/2021
which works in either system.
I think its just a bug in the axis rendering routine where its unable to properly coerce strings into dates.
I would be interested to hear from others more knowledgeable than me.
Also, I'm curious about this:
.SeriesCollection.NewSeries
.SeriesCollection(DETACHED).Name = "Detached"
.SeriesCollection(DETACHED).Values = detached_price
.SeriesCollection(DETACHED).XValues = detached_date
How does it know which series to reference? Is DETACHED a constant you have defined elsewhere?
I would think this would be better:
with objChart.SeriesCollection.NewSeries
.Name = "Detached"
.Values = detached_price
.XValues = detached_date
end with

Related

XYScatter Plot Incorrect XAxis when Drawn in Visual Basic?

I'm trying to create a scatter plot using visual basic, the y axis being numerical values and the x axis being dates. The intention is for the plot to contain multiple series. Here is the relevant code:
ActiveWorkbook.Charts.Add
ActiveChart.ChartArea.Select
With ActiveChart
.ChartType = xlXYScatter
.HasTitle = True
.ChartTitle.Text = "Time Trend of Data"
.Axes(xlCategory, xlPrimary).HasTitle = True
.Axes(xlCategory, xlPrimary).AxisTitle.Characters.Text = "Dates"
.Axes(xlCategory, xlPrimary).CategoryType = xlTimeScale
.Axes(xlCategory, xlPrimary).TickLabels.NumberFormat = "m/d/yy;#"
.Axes(xlValue, xlPrimary).HasTitle = True
.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = "Total Time"
.Legend.Position = xlLegendPositionBottom
End With
ActiveSheet.Move After:=Sheets(ActiveWorkbook.Sheets.count)
ActiveSheet.Name = "Time Trend " + CStr(currTT) ' This is just to make sure the new sheet does not have the same name
After I generate some data, I attempt to plot it with a loop. The arrays I use are chartLabels - which is the name of each series, chartData - a 3d array with several data points for each series and xval and yval - arrays built from the chartData array which are plotted against each other:
For j = 0 To UBound(chartLabels)
If IsEmpty(chartLabels(j)) Then Exit For
Erase xval
Erase yval
ReDim Preserve xval(0 To 0)
ReDim Preserve yval(0 To 0)
xval(0) = chartData(1, j, 0)
yval(0) = chartData(2, j, 0)
For i = 0 To UBound(chartData, 3) - 1
If Not IsEmpty(chartData(2, j, i + 1)) Then
ReDim Preserve xval(0 To i + 1)
ReDim Preserve yval(0 To i + 1)
xval(i + 1) = chartData(1, j, i + 1)
yval(i + 1) = chartData(2, j, i + 1)
End If
Next
MsgBox (Join(xval, " || "))
ActiveChart.SeriesCollection.NewSeries
ActiveChart.SeriesCollection(j + 1).XValues = xval
ActiveChart.SeriesCollection(j + 1).Values = yval
ActiveChart.SeriesCollection(j + 1).Name = main.chartLabels(j)
Next
The MsgBox Statement is included to view the array that I am passing as the XValues to my Scatter Plot. The Output of the Array from the first series looks like:
All of these values are in date format. The number of series created varies. Also, the number of data points in each series can very depending on options that the user picks. However, the output that is generated on the scatter plot looks like:
Everything on the graph is correct except the xaxis is scaled to the position of each data point in its respective series, NOT the actual date (i.e. 1/0/00 is actually 0 in date format and 3/10/00 is 70 because there are about 70 data points).
I've tried using xlCategoryScale and xlAutomaticScale as my CategoryType. I've tried using CDate() on each of my xvals as well as CStr(). I've tried outputting different arrays for the XValues. Nothing works.
I have my suspicions that the problem is related to the fact that I'm trying to graph multiple series of data. But, if anybody could tell me the actual issue and/or a way around this issue, I would appreciate it very much. Thank you in advance!
I'm not sure why I didn't think of this... Two hours after I posted this I discovered that though using CStr() and CDate() on my XValues was useless, CDbl() actually worked. I'm going to refrain from just deleting my question because I don't think this is very intuitive. I'm not sure why this worked because I'm still formatting the xaxis as .CategoryType = xlTimeScale. I added this bit of code to the code above (this is the code I had tested before with CStr() and CDate(), but I did not include it in the question):
For k = 0 To UBound(xval)
xval(k) = CDbl(xval(k))
Next
This was added right before the MsgBox in the code from the question. This is the new output from the MsgBox for the XValues of the first series in the set:
Note: These values are all in the 42000's because each whole number is equal to a day and this data is from 2015.
2015 - 1900 = 115 years
115 years * 365.25 days/year = 42003.75
'In reality 2015 = 42005 (because of what years were leap years, etc.)
Finally, here is the actual output given the "Numerical" dates as xvalues in the scatter plot:
I guess the lesson here is that the xaxis likes numbers and using anything else could turn out to be a royal pain. If anyone comes across this post, I hope it was helpful!

Format datatable values in VBA

Currently working on a vba script that makes charts automatically. I would like to add a datatable which is done using: .HasDataTable = True
However I would like to show the values of series as percentages. Currently the value is defined as a Double containing all the values but not the right formatting. Using Format() or FormatPercent() will give the right values but returned in a String. This works for the datatable but not for the chart itself since it doesn't recognize the values anymore.
My question comes down to whether it is possible to show the values as percentages in both the datatable and the chart? Without VBA it is easily done by formatting the data in the cells itself. The problem is that for formatting a String is returned but for the graph Integers or Doubles are needed.
Below is part of the code. If I dim Ratio as String and use FormatPercent() I get the requested formatting but then the values in Ratio ar no longer doubles so it doesn't give the required chart.
Dim Ratio() As Double
Dim labels() As String
ReDim Ratio(1 To Height)
ReDim labels(1 To Height)
For Each Column In sArray
labels(i) = Sheets(DataSheetName).Cells(LabelsRow, Column)
Ratio(i) = Math.Round(Sheets(DataSheetName).Cells(LabelsRow + 3, Column), 2)
i = i + 1
Next Column
Set myChtObj = Sheets(DrawSheetName).ChartObjects.Add(Left:=Left, Width:=Width, Top:=Top, Height:=HeightGraph)
Dim srsNew1 As Series
' Add the chart
With myChtObj.Chart
.ChartArea.Fill.Visible = False
.ChartArea.Border.LineStyle = xlNone
.PlotArea.Format.Fill.Solid
.PlotArea.Format.Fill.Transparency = 1
.HasTitle = True
.ChartTitle.text = Title
.HasLegend = False
.Axes(xlValue).TickLabels.NumberFormat = "0%"
.Axes(xlCategory, xlPrimary).HasTitle = False
'add data table
.HasDataTable = True
' Make Line chart
.ChartType = xlLine
' Add series
Set srsNew1 = .SeriesCollection.NewSeries
With srsNew1
.Values = Ratio
.XValues = labels
.Name = "Ratio"
.Interior.Color = clr3 'RGB(194, 84, 57)
End With
End With

How to assign XValues for excel chart using VBA

I have this VBA function for drawing charts in Excel 2013:
Sub DrawChart2(obj_worksheetTgt As Worksheet, ByVal XLabels As Range, ByVal DataValues As Range, ByVal chartTitle As String, a As Integer, b As Integer)
'
'obj_worksheetTgt - Object worksheet on which to be placed the chart
'XLabels - Data range for X labels
'DataValues - Data range for Y values
'chartTitle - Chart title
'a - left border position of chart in pixels
'b - top border position of chart in pixels
With obj_worksheetTgt.ChartObjects.Add(a, b, 900, 300) ' Left, Top, Width, Height
With .Chart
.ChartType = xlBarClustered
Set .SeriesCollection(1).XValues = XLabels ' Here is the error
Set .SeriesCollection(1).Values = DataValues
.Legend.Position = -4107
.HasTitle = True
.chartTitle.Text = chartTitle
.chartTitle.Font.Size = 12
With .Axes(1).TickLabels
.Font.Size = 8
.Orientation = 90
End With
End With
End With
End Sub
I call the function this way:
ChartsWorksheet = "Summary"
Queryname = "query1"
chartTitle = "Values"
With .Worksheets("LastDayData").ListObjects(Queryname)
Set chart_labels = .ListColumns(2).DataBodyRange
Set chart_values = .ListColumns(6).DataBodyRange
End With
Call DrawChart2(.Worksheets(ChartsWorksheet), chart_labels, chart_values, chartTitle, 10, 10)
And I receive an error:
Runtime Error '1004':
Invalid Parameter
When I click debug it marks the row "Set .SeriesCollection(1).XValues = XLabels" in the function above.
In the documentation is written:
The XValues property can be set to a range on a worksheet or to an
array of values, but it cannot be a combination of both
So it should be able to take the given range as values for XValues, but I can't understand why this error appears.
Before you can set the Values and XValues of a series, you will need to add the series first. This is simple to do using the SeriesCollection.NewSeries method as show below:
With ActiveSheet.ChartObjects.Add(a, b, 900, 300) ' Left, Top, Width, Height
With .Chart
.ChartType = xlBarClustered
' need to add the series before you can assign the values/xvalues
' calling the "NewSeries" method add one series each time you call it.
.SeriesCollection.NewSeries
' now that the series is added, you may assign (not set) the values/xvalues
.SeriesCollection(1).XValues = XLabels
.SeriesCollection(1).Values = DataValues
End With
End With
You cannot use named ranges for .XValues or .Values, but you can change the .Formula property by replacing the series formula
For more information on editing the series formula see http://peltiertech.com/change-series-formula-improved-routines/
Note of caution, there is a bug in Excel 2013 that prevents you from using named ranges starting with "R" or "C" when using VBA to change the series formula; see http://answers.microsoft.com/en-us/office/forum/office_2013_release-excel/named-range-use-in-chart-series-formulas-causes/c5a40317-c33f-4a83-84db-0eeee5c8827f/?auth=1&rtAction=1466703182593

Excel 2010 Chart axis not shown up

I am converting a EXCEL 2003 application to EXCEL 2010. Data are shown up fine but the axis does not show any more. which function to show the axis with automatic scale?
For Example: If you plot the following series in an Excel Line Chart. [0.22,0.33,0.44,0.55,0.66,0.77,0.88,0.99,1.1,1.21,1.32,1.43,1.54,1.65,1.76,1.87,1.98,2.09,2.2] Excel determines that the y-axis values should be [0,0.5,1,1.5,2,2.5] [How does Excel determine the axis values for charts?1. How to make the y-axis with the automatic values [0,0.5,1,1.5,2,2.5] shown in the chart?
Thanks
Updated with related codes -
With ActiveChart
.SeriesCollection(2).Select
'.SeriesCollection(2).AxisGroup = 2
.HasTitle = True
.ChartTitle.Text = OutputTitle & Chr(10) & ChartTitle2
.Axes(xlValue).HasTitle = True
.Axes(xlValue).AxisTitle.Text = AxisTitle1
.Axes(xlValue).AxisTitle.Font.Bold = False
.HasAxis(Excel.XlAxisType.xlCategory, Excel.XlAxisGroup.xlPrimary) = True
.Export Filename:=ExportFile, FilterName:="GIF"
End with
If I uncomment '.SeriesCollection(2).AxisGroup = 2, I will get the y axis to show but the x axis labels are messed up with mismatch with the Values.
Current chart -
Desired chart with scaled axis shown -
To make sure the axis is on use this:
With xlApp.ActiveChart
.HasAxis(Excel.XlAxisType.xlCategory, Excel.XlAxisGroup.xlPrimary) = True
End With
Range values are automatic unless otherwise specified like this:
' Set Axis Scales
With xlApp.Charts("Chart Name").Axes(2)
.MinimumScale = 100
.MaximumScale = 2000
.MajorUnit = 1000
.MinorUnit = 100
End With
Just to be a little more complete try explicitly addressing each value and category and see if that helps.
With xlApp.ActiveChart
.SeriesCollection(1).XValues = "='sheet name'!R21C4:R46C4"
.SeriesCollection(1).Values = "='sheet name'!R21C5:R46C5"
.SeriesCollection(1).Name = "='series name'"
.SeriesCollection(1).axisgroup = Excel.XlAxisGroup.xlPrimary
.HasAxis(Excel.XlAxisType.xlCategory, Excel.XlAxisGroup.xlPrimary) = True
.HasAxis(Excel.XlAxisType.xlValue, Excel.XlAxisGroup.xlPrimary) = True
.Axes(Excel.XlAxisType.xlCategory, Excel.XlAxisGroup.xlPrimary).HasTitle = True
.Axes(Excel.XlAxisType.xlCategory, Excel.XlAxisGroup.xlPrimary).AxisTitle.Characters.Text = "x axis"
.Axes(Excel.XlAxisType.xlValue, Excel.XlAxisGroup.xlPrimary).HasTitle = True
.Axes(Excel.XlAxisType.xlValue, Excel.XlAxisGroup.xlPrimary).AxisTitle.Characters.Text = "y axis"
End With
I see your axes group is set to 2, are you using dual axis?
Set it like this:
.SeriesCollection(2).axisgroup = Excel.XlAxisGroup.xlPrimary
*Edit*
To set autoscale on the axis:
.Axes(xlValue).MinimumScaleIsAuto = True
.Axes(xlValue).MaximumScaleIsAuto = True
.Axes(xlValue).MinorUnitIsAuto = True
.Axes().MajorUnitIsAuto = True
.Axes().DisplayUnit = xlHundreds

Excel 2010 VBA - Storing decimal in array and using to chart percentage of stored value

SO Community,
I'm looking to automate some of the metrics that are used at my work using VBA. I am currently trying to read through an array that I have my ticketing raw data stored in and then either store the value as a decimal or percentage. After this is stored in the array, I am attempting to create or update a chart series with the array and display this value as a percent. I suspect that I'm just missing some syntax for this, but I have checked SO, MSDN and Excel help and have had no luck. I have attached the relevant code below:
FUNCTION
Function calcTopApplications(iArray As Variant)
Dim m_counter As Long, r_counter As Long, placeholder As Double
Dim fNav_counter As Long, pmoNav_counter As Long, rmgr_counter As Long, wlm_counter As Long, total_counter As Long
ReDim tkt_month_arr(12), tkt_fnav_arr(12)
For m_counter = 1 To 12
fNav_counter = 0
pmoNav_counter = 0
rmgr_counter = 0
wlm_counter = 0
total_counter = 0
For r_counter = 2 To UBound(iArray, 1)
If iArray(r_counter, 1) <> iArray(r_counter - 1, 1) Then
If CDate(iArray(r_counter, 5)) >= DateAdd("m", -m_counter, DateSerial(Year(Date), Month(Date), 1)) Then
If CDate(iArray(r_counter, 5)) < DateAdd("m", (1 - m_counter), DateSerial(Year(Date), Month(Date), 1)) Then
total_counter = total_counter + 1
If StrConv(iArray(r_counter, 7), vbLowerCase) = "franchise navigator" Then
fNav_counter = fNav_counter + 1
End If
End If
End If
End If
Next r_counter
placeholder = FormatNumber(fNav_counter / total_counter, 2)
tkt_month_arr(12 - (m_counter - 1)) = CLng(DateAdd("m", -m_counter, DateSerial(Year(Date), Month(Date), 1)))
tkt_fnav_arr(12 - (m_counter - 1)) = placeholder
Next m_counter
End Function
SUBROUTINE
If Me.ChartObjects("Top 4 Applications Ticket Volume") Is Nothing Then
On Error GoTo 0
With Me.Shapes.AddChart
.Left = Me.Range("A16").Left
.Top = Me.Range("A16").Top
.Width = Me.Range("A16:S16").Width
.Height = Me.Range("A16:A30").Height
.Select
End With
With ActiveChart
.ChartType = xlLine
.ChartStyle = 42
.HasDataTable = True
.HasTitle = True
.Parent.Name = "Top 4 Applications Ticket Volume"
.ChartTitle.Caption = "Open/Close Ticket Volume by Month (Top 4 Applications)"
.Axes(xlCategory).TickLabels.NumberFormat = "mmm yyyy"
End With
With ActiveChart.SeriesCollection
.NewSeries
.Item(1).Name = "Franchise Navigator"
.Item(1).XValues = tkt_month_arr
.Item(1).Values = tkt_fnav_arr
End With
Else
With Me.ChartObjects("Top 4 Applications Ticket Volume")
.Select
End With
With ActiveChart.SeriesCollection
.Item(1).XValues = tkt_month_arr
.Item(1).Values = tkt_fnav_arr
End With
End If
This gives me the values for the percentage but does not behave on the chart as a percentage (have the % symbol).
When you create a chart "normally" and add a data table, all you have to do is format the source cells, and the table follows. But the method you use means you don't have "source cells"; and as far as I was able to tell, Microsoft doesn't provide a method to modify the formatting of the data table when it's generated with your method.
An "ugly workaround" is to create a hidden sheet, put the data there, and format it correctly. Point to this data when you create the data table, and the formatting will be correct.
It would be nice if Microsoft provided the flexibility you are asking for... sigh