Creating charts in Excel VBA - Missing Labels - excel

UPDATE 2:
Trying to define the Variable with the column range in the VBA.
Can anyone guess what's wrong in this code ?
Thanks in advance...
UPDATE 1: I have been successful in generating the chart. Thanks to you guys for the constructive criticism.
I'm a newbie to VBA, just learning :)
My challenge now is to define the row selected for the graph as a variable.
ie. The user gives an input for the ROW and the macro generates the chart for the intended Row.
Find the UPDATED CODE BELOW.
Thanks to all
I need to write a macro to create a individual performance chart in Excel. I have a few lines of code recorded, but the resultant chart does not have any labels on the X & Y Axis.
My requirement is to create a chart with the following features:
Option to choose the row no. in the beginning of the macro (for which row the chart needs to be prepared) - some input box
Comparison Feature to compare Row 1 with Row 2. (some input box)
Data Series Label (X Axis)
Chart Title
MY EXCEL LOOKS LIKE THIS:
Sales Achieved |Clients Met| Client Responsiveness|
Employee 1 | 6 | 7 | 8 |
Employee 2 | 6 | 7 | 8 |
Employee 3 | 6 | 7 | 8 |
Employee 4 | 6 | 7 | 8 |
Sub generatecharts()
Dim xValRange As Range
Dim r
r = irow
irow = InputBox("Which Chart do you want to generate?")
With ActiveSheet
Set xValRange = ActiveSheet.Range("B" & r & ":" & "Q" & r)
End With
With ActiveSheet
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlColumnClustered
ActiveChart.SeriesCollection.NewSeries
ActiveChart.SeriesCollection(1).Values = xValRange
ActiveChart.SeriesCollection(1).XValues = "=Sheet2!$B$1:$Q$2"
ActiveChart.SeriesCollection(1).Name = "=Sheet2!$A$" & r
With ActiveChart.Parent
.Height = 400
.Width = 800
End With
End With
End Sub

Sub Macro4()
Dim xValRange As Range
Dim r As Integer
Range("A30").Select 'selected a blank cell on purpose to avoid undefined charts
r = InputBox("Enter the Row Number to generate Chart")
With ActiveSheet
Set xValRange = ActiveSheet.Range("$B$" & r & ":" & "$T$" & r)
ActiveSheet.Shapes.AddChart.Select
ActiveChart.ChartType = xlColumnClustered
ActiveChart.HasTitle = True
ActiveChart.ChartTitle.Text = Cells(r, 1)
ActiveChart.SeriesCollection.NewSeries
ActiveChart.SeriesCollection(1).Values = xValRange
ActiveChart.SeriesCollection(1).XValues_
=ActiveSheet.Range("=KRAs!$B$1:$T$2")
ActiveChart.SeriesCollection(1).Name = ActiveSheet.Range("=KRAs!$A$" & r)
ActiveChart.SetElement (msoElementDataLabelInsideEnd) 'to add the data labels
End With
With ActiveChart.Parent
.Height = 400
.Width = 800
.Top = 150 ' reposition
.Left = 200 'reposition
End With
End Sub

Related

Paste Pictures and Organize

I have the following code, which sorts some pictures down according to a value in Sheet1. Horizontally the images are aligned in the required columns. But not in the Rows.
I tried to do it with another FOR loop that I called J but doesn't work, it puts the images on top of each other.
How can I align the pictures in a column and a row that I choose?
What am I doing wrong? or what am I missing?
Thank you.
-------------CODE-------------
Sub CommandButton2_Click()
Dim firma_pic As Picture
Dim pic_location As String
Dim identifier_pic As String
'For j = 14 To 23
'Next
'Worksheets("Sheet3").Cells(14, 23).Select
For i = 2 To 11
identifier_pic = Worksheets("Sheet1").Cells(i, 11).Value
pic_location = "C:\Users\User\Downloads\Docs\img\" & Worksheets("Sheet1").Cells(i, 2).Value & ".png"
With Worksheets("Sheet3").Cells(i, 24)
Set firma_pic = ActiveSheet.Pictures.Insert(pic_location)
firma_pic.Top = .Top
firma_pic.Left = .Left
firma_pic.ShapeRange.LockAspectRatio = msoFalse
firma_pic.Placement = xlMoveAndSize
firma_pic.ShapeRange.Width = 70
firma_pic.ShapeRange.Height = 30
End With
Next
Worksheets("Sheet3").Cells(i, 23).Select
End Sub
Width and Height need to consider your cell too. You may resize images and force them to be W=70 and H=30 where the cell begins, but it could lead to images overlapping between columns. I'd suggest you to stick to the same workflow as you did for Top and Left
firma_pic.ShapeRange.Width = .Width
firma_pic.ShapeRange.Height = .Height

Unable to plot values on scatterplot using vba

I am relatively new to VBA. I am attempting to automate the generation of a graph containing Beta values using the following steps:
Step 1 - Generate a list of random numbers in Column A
Step 2 - Based on values in Column A, apply the Beta.Dist formula in Column B
Step 3 - Generate a scatterplot based on values in Column B, the scatterplot should look like a U-shaped distribution curve
I've tried using the following code but I am only seeing 1 value in the scatter plot (i.e., the scatterplot only has one dot).
Here's the code:
Option Explicit
Option Base 1
Sub Macro1()
Application.ScreenUpdating = False
Dim i%, j%
Range("A1").Select
Selection = "0.99"
Selection.Offset(0, 1) = "Beta(" & Selection & ", " & Selection & ") Density"
For i = 1 To 301
Selection.Offset(i, 0).Formula = "= rand()"
Selection.Offset(i, 1).Formula = "=BETA.DIST(A2:A302,0.99,0.99,False)"
Next i
j = ActiveSheet.ChartObjects.Count
ActiveSheet.Shapes.AddChart2(-1, xlXYScatter, 98 + j * 10, 16.5 + j * 10, 319, 296.5).Select
ActiveChart.SetSourceData Source:=Sheets("Sheet1").Range("B2:B302")
ActiveSheet.ChartObjects(j + 1).Activate
ActiveChart.SetElement (msoElementChartTitleAboveChart)
Selection.Caption = "="
ActiveChart.Axes(xlCategory).MaximumScale = 1
ActiveChart.Axes(xlCategory).Format.Line.Visible = msoFalse
ActiveChart.Axes(xlCategory).MajorGridlines.Delete
ActiveChart.Axes(xlValue).MaximumScale = 1.1
ActiveChart.Axes(xlValue).TickLabels.NumberFormat = "0.000#"
ActiveChart.Axes(xlValue).Format.Line.Visible = msoFalse
ActiveChart.Axes(xlValue).MajorGridlines.Delete
Range("E1") = ""
Range("I1") = ""
Range("B1").Select
Application.ScreenUpdating = True
End Sub
I'm hoping to get a U-shaped beta distribution curve in the scatter plot. Appreciate any form of help and thank you in advance!
You're not setting the chart data correctly:
ActiveChart.SetSourceData Source:=Sheets("Sheet1").Range("A2:B302")

Why my VBA code creates line plot instead of scatter plot?

I'm trying to generate scatter plot from 2 data-columns, but instead I get a line plot where data of the first column is ignored (i.e.: if I have 1000 points, on x-axis I see values from 1 to 1000, regardless of the data stored in 1st column). I can't find the error in my code. What's wrong?
Public Sub Graph_Refresh()
Dim cht As Chart
Dim i As Integer
Dim seriesIndex As Integer
Set cht = Sheets("Graph").ChartObjects("Chart 1").Chart
seriesIndex = 0
' ***** CLEAR OLD CONTENT *****
cht.ChartArea.ClearContents
' ***** NON CHANGEABLE PARAMETERS *****
'Format Font Type and Size
cht.ChartType = xlXYScatterLinesNoMarkers ' scatter plot
cht.ChartArea.Format.TextFrame2.TextRange.Font.Name = "Arial"
cht.ChartArea.Format.TextFrame2.TextRange.Font.Size = 12
cht.HasTitle = False ' No chart title
'cht.SetElement (msoElementPrimaryValueGridLinesMajor) 'Gridlines
'Adjust x-axis
cht.HasAxis(xlCategory, xlPrimary) = True
cht.Axes(xlCategory, xlPrimary).HasTitle = True
cht.Axes(xlCategory).AxisTitle.Text = "Frequency [MHz]"
cht.Axes(xlCategory).MinimumScale = Sheets("Graph").Range("AI7").Value
cht.Axes(xlCategory).MaximumScale = Sheets("Graph").Range("AI8").Value
'Adjust y-axis
cht.HasAxis(xlValue, xlPrimary) = True
cht.Axes(xlValue, xlPrimary).HasTitle = True
cht.Axes(xlValue).AxisTitle.Text = "S-Parameters [dB]"
cht.Axes(xlValue).MinimumScale = Sheets("Graph").Range("AI9").Value
cht.Axes(xlValue).MaximumScale = Sheets("Graph").Range("AI10").Value
cht.Axes(xlValue).CrossesAt = -100
' Data Series
For i = 1 To 5
seriesIndex = seriesIndex + 1
cht.SeriesCollection.NewSeries
With Sheets("Graph")
cht.SeriesCollection(seriesIndex).Name = .Cells(6 + (i - 1) * 4).Value & " S11"
End With
cht.SeriesCollection(seriesIndex).XValues = "='" & Sheets("Data" & CStr(i)).Name & "'!$K$4:$K$10004"
cht.SeriesCollection(seriesIndex).Values = "='" & Sheets("Data" & CStr(i)).Name & "'!$L$4:$L$10004"
' Set line size and color
With cht.SeriesCollection(seriesIndex)
.Format.Line.Weight = 2.25
.Format.Line.Visible = msoFalse
.Format.Line.Visible = msoTrue
.Format.Line.ForeColor.RGB = RGB(255,0,0)
.MarkerStyle = xlMarkerStyleNone
End With
Next i
' Legend
End Sub
Data are stored in sheets "Data1" - "Data5", and the range should be ok. Chart "Chart 1" already exists (that's why I don't create it).
Thanks in advance for help!
After some more investigation, I fund the answer. I leave it here for anyone who might be interested.
The problem was due to the fact that the data range that I provided included empty cells (or, to be more precise, cells with functions returning blanks).
Resizing XValues and Values to include only cells with data solved the problem.

Find the reference row number of specific point in a scatter plot

Objective: I'm looking to find the reference row number of data points from filtered series that have been scatter plotted from two separate sheets.
I'm following these guides, with little success:
Excel VBA loop through visible filtered rows
Excel vba - find row number where colum data (multiple clauses)
Scenario: I have two Sheets containing data in identical tabulated format:
+-----+-------------+---------+---------+-------+
| Row | Description | X-value | Y-value | Score |
+-----+-------------+---------+---------+-------+
| 1 | "Something" | 3.4 | 4.5 | 7.0 |
| 2 | "Something" | 2.3 | 2.4 | 5.6 |
| ... | ... | ... | ... | ... |
| 100 | "Something" | 6.5 | 4.2 | 8.0 |
+-----+-------------+---------+---------+-------+
x-val and y-val from each sheet has been scatter plotted as separate series on the same chart.
I have a VBA script that on mouse hover on the chart returns the series index, x, and y coordinates of the specific data point (Arg1, ser.Values, ser.XValues):
Private Sub Chart_MouseMove(ByVal Button As Long, ByVal Shift As Long, ByVal x As Long, ByVal y As Long)
Dim ElementID As Long
Dim Arg1 As Long
Dim Arg2 As Long
Dim chart_data As Variant
Dim chart_label As Variant
Dim last_point As Long
Dim chrt As Chart
Dim ser As Series
Dim score As Double
Dim desc As String
On Error Resume Next
Me.GetChartElement x, y, ElementID, Arg1, Arg2
Application.ScreenUpdating = False
Set chrt = ActiveChart
Set ser = ActiveChart.SeriesCollection(Arg1)
'x and y values
chart_data = ser.Values
chart_label = ser.XValues
If the list is unfiltered it seems the series' point index matches the row number so I can get a reference to the row and extract info quite easily:
If Arg1 = 1 Then
score = Sheet1.Cells(Arg2 + 1, "E").Value
desc = Sheet1.Cells(Arg2 + 1, "B").Value
End If
If Arg1 = 2 Then
score = Sheet2.Cells(Arg2 + 1, "E").Value
desc = Sheet2.Cells(Arg2 + 1, "B").Value
End If
Complexity: Each sheet filters on score and dynamically update the chart, so the resulting row numbers in each sheet may not contiguous. Some rows are hidden.
The above indices no longer match the correct row, so my code returns the wrong information.
Eg. Scores > 6
+-----+-------------+---------+---------+-------+
| Row | Description | X-value | Y-value | Score |
+-----+-------------+---------+---------+-------+
| 1 | "Something" | 3.4 | 4.5 | 7.0 |
| 100 | "Something" | 6.5 | 4.2 | 8.0 |
+-----+-------------+---------+---------+-------+
Outcome: I would like to use the x, y values to search the visible list on each sheet and retrieve the row number. So that I can then retrieve the description and score to pipe into my mouse-over pop-up message.
I'm a novice in VBA and guidance is appreciated.
Update 1: Showing code to do mouse-hover and adopting DisplayName's answer. It does not work for all data points, and displays a blank box. Currently trying to debug. When comparing to my original code with no filtering on rows.
Clarification: X values (and Y) could be the same. Where there are duplicate X and Y returning the first match would be ok.
Set txtbox = ActiveSheet.Shapes("hover")
If ElementID = xlSeries And Arg1 <= 2 Then
' Original code that only works on un-filtered rows in Sheet 1 & 2
' If Arg1 = 1 Then
' score = Sheet1.Cells(Arg2 + 1, "E").Value
' desc = Sheet1.Cells(Arg2 + 1, "B").Value
' ElseIf Arg1 = 2 Then
' score = Sheet2.Cells(Arg2 + 1, "E").Value
' desc = Sheet2.Cells(Arg2 + 1, "B").Value
' End If
' Code from DisplayName
With Worksheets(Choose(Arg1, Sheet1.Name, Sheet2.Name)) ' reference Sheet1 if Arg1=1 and Sheet2 if Arg1=2
With .Range("C2", .Cells(.Rows.Count, "C").End(xlUp)).Find(what:=chart_label(Arg2), LookIn:=xlValues, lookat:=xlWhole) ' search reference referenced sheet x-values range for current x-value
If .Offset(, 1).Value = chart_data(Arg2) Then 'check y-value
score = .Offset(, 2).Value ' assign 'score' the value of found cell offset two columns to the right
desc = .Offset(, -1).Value ' assign 'desc' the value of found cell offset one column to the left
End If
End With
End With
If Err.Number Then
Set txtbox = ActiveSheet.Shapes.AddTextbox _
(msoTextOrientationHorizontal, x - 150, y - 150, 300, 50)
txtbox.Name = "hover"
txtbox.Fill.Solid
txtbox.Fill.ForeColor.SchemeColor = 9
txtbox.Line.DashStyle = msoLineSolid
chrt.Shapes("hover").TextFrame.Characters.Text = "Y: " & Application.WorksheetFunction.Text(chart_data(Arg2), "?.?") & _
", X: " & Application.WorksheetFunction.Text(chart_label(Arg2), "?.?") & _
", Score: " & Application.WorksheetFunction.Text(score, "?.?") & ", " & desc
With chrt.Shapes("hover").TextFrame.Characters.Font
.Name = "Arial"
.Size = 12
.ColorIndex = 16
End With
last_point = Arg2
End If
txtbox.Left = x - 150
txtbox.Top = y - 150
Else
txtbox.Delete
End If
Application.ScreenUpdating = True
End Sub
Update 2: As Tim Williams noted there is no way to get around this without looping through the range. I combined his pseudocode with DisplayName's example to get the desired behavior where x, y is compared to get the score and description. Here is the code that worked:
With Worksheets(Choose(Arg1, Sheet1.Name, Sheet2.Name))
For Each row In .Range("C2", .Cells(.Rows.Count, "C").End(xlUp)).SpecialCells(xlCellTypeVisible)
If row.Value = chart_label(Arg2) And row.Offset(, 1).Value = chart_data(Arg2) Then
score = row.Offset(, 2).Value
desc = row.Offset(, -1).Value
Exit For
End If
Next row
End With
I wish I could split the bounty between Tim Williams and Display Name. As I can only choose one the award goes to Tim.
You can do something like this:
'called from your event class using Arg1 and Arg2
Sub HandlePointClicked(seriesNum As Long, pointNum As Long)
Dim vis As Range, c As Range, i As Long, rowNum As Long
Dim sht As Worksheet
' which sheet has the source data?
Set sht = GetSheetFromSeriesNumber(seriesMum)
'Get only the visible rows on the source data sheet
' (adjust to suit your specific case...)
Set vis = sht.Range("A2:A100").SpecialCells(xlCellTypeVisible)
'You can't index directly into vis
' eg. vis.Cells(pointNum) may not work as you might expect
' so you have (?) to do something like this loop
For Each c In vis.Cells
i = i + 1
If i = pointNum Then rowNum = c.Row
Next c
Debug.Print rowNum '<< row number for the activated point
End Sub
As reparation of my earlier attempt to answer without going into details of your question and to prevent my deleted answer to be viewed by experts, I am offering another solution. But before going into codes and all, I must acknowledge that the best solution is already provided by #Tim Williams and think only his answer is worthy to be accepted (till date). I found no other option to get row numbers without looping.
I only attempting to put the pieces together and integrating with your code. I have taken following liberties
Used class module as directly coding Chart_MouseMove may become troublesome while modifying/working with chart.
Chart is placed on the worksheet only
Used a stationary Textbox already placed on the chart to avoid deleting & recreating the same. It may cause problem in run time error
Avoided disabling Screen update and Error bypass.
You may please modify the code according to your requirement.
Now first insert a class module named CEvent. In the class module add
Public WithEvents Scatter As Chart
Private Sub Scatter_MouseMove(ByVal Button As Long, ByVal Shift As Long, ByVal X As Long, ByVal Y As Long)
Dim ElementID As Long
Dim Arg1 As Long
Dim Arg2 As Long
Dim chart_data As Variant
Dim chart_label As Variant
Dim last_point As Long
Dim chrt As Chart
Dim Ser As Series
Dim score As Double
Dim desc As String
Dim VRng, Cl As Range, SerStr As String, part As Variant, Txt As Shape
'On Error Resume Next
Set chrt = ActiveChart
chrt.GetChartElement X, Y, ElementID, Arg1, Arg2
'Application.ScreenUpdating = False
'x and y values
If ElementID = xlSeries And Arg1 <= 2 Then
Set Ser = ActiveChart.SeriesCollection(Arg1)
SerStr = Ser.Formula
part = Split(SerStr, ",")
Set VRng = Range(part(1)).SpecialCells(xlCellTypeVisible)
Vrw = 0
For Each Cl In VRng.Cells
Vrw = Vrw + 1
If Vrw = Arg2 Then
Exit For
End If
Next
score = Cl.Offset(, 2).Value
desc = Cl.Offset(, -1).Value
chart_data = Cl.Value
chart_label = Cl.Offset(, 1).Value
Set Txt = ActiveSheet.Shapes("TextBox 2")
'Txt.Name = "hover"
Txt.Fill.Solid
Txt.Fill.ForeColor.SchemeColor = 9
Txt.Line.DashStyle = msoLineSolid
Txt.TextFrame.Characters.Text = "Y: " & chart_label & ", X: " & chart_data & ", Score: " & score & ", " & vbCrLf & desc
With Txt.TextFrame.Characters.Font
.Name = "Arial"
.Size = 12
.ColorIndex = 16
End With
last_point = Arg2
'Txtbox.Left = X - 150
'Txtbox.Top = Y - 150
Else
'Txt.Visible = msoFalse
End If
'Application.ScreenUpdating = True
End Sub
Then in a standard module
Dim XCEvent As New CEvent
Sub InitializeChart()
Set XCEvent.Scatter = Worksheets(1).ChartObjects(1).Chart
Worksheets(1).Range("I25").Value = "Scatter Scan Mode On"
Worksheets(1).ChartObjects("Chart 1").Activate
End Sub
Sub ReleaseChart()
Set XCEvent.Scatter = Nothing
Worksheets(1).Range("I25").Value = "Scatter Scan Mode Off"
End Sub
The sub InitializeChart() & ReleaseChart() may be assigned to buttons placed on the worksheet near the chart. May please modify Sheet names, addresses, Chart name, Textbox names etc suitably. It is working with make shift filtered data
Hope It will be useful
you have to find the cell with the current x-value and then offset from it
so substitute:
If Arg1 = 1 Then
score = Sheet1.Cells(Arg2 + 1, "E").Value
desc = Sheet1.Cells(Arg2 + 1, "B").Value
End If
If Arg1 = 2 Then
score = Sheet2.Cells(Arg2 + 1, "E").Value
desc = Sheet2.Cells(Arg2 + 1, "B").Value
End If
with:
With Worksheets(Choose(Arg1, Sheet1.Name, Sheet2.Name)) ' reference Sheet1 if Arg1=1 and Sheet2 if Arg1=2
With .Range("C2", .Cells(.Rows.Count, "C").End(xlUp)).Find(what:=chart_label(Arg2), LookIn:=xlValues, lookat:=xlWhole) ' search reference referenced sheet x-values range for current x-value
score = .Offset(, 2).Value ' assign 'score' the value of found cell offset two columns to the right
desc = .Offset(, -1).Value ' assign 'desc' the value of found cell offset one column to the left
End With
End With

how to add data label to bubble chart in excel

Hi I want to add customize data label to my bubble chart. my code is below. at the moment data label refer to XValues. I would like my data label fill with bubble size. would u mind help me how I can customize below code?
I tried to add .DataLabel.Text = "txt" but I received below
error:
Run-time error '438':Object doesn't support this property or method
Public Sub CreateMultiSeriesBubbleChart()
If (Selection.Columns.Count <> 4 Or Selection.Rows.Count < 3) Then
MsgBox "Selection must have 4 columns and at least 2 rows"
Exit Sub
End If
Dim red, green, blue As Integer
Dim bubbleChart As ChartObject
Set bubbleChart = ActiveSheet.ChartObjects.Add(Left:=Selection.Left, Width:=600, Top:=Selection.Top, Height:=400)
bubbleChart.Chart.ChartType = xlBubble
Dim r As Integer
For r = 2 To Selection.Rows.Count
With bubbleChart.Chart.SeriesCollection.NewSeries
.Name = "=" & Selection.Cells(r, 1).Address(External:=True)
.XValues = Selection.Cells(r, 2).Address(External:=True)
.Values = Selection.Cells(r, 3).Address(External:=True)
.BubbleSizes = Selection.Cells(r, 4).Address(External:=True)
.Format.Fill.Solid
.Format.Fill.ForeColor.RGB = RGB(61, 161, 161)
' .DataLabel.Text = "txt"
End With
Next
bubbleChart.Chart.SetElement (msoElementPrimaryCategoryAxisTitleAdjacentToAxis)
bubbleChart.Chart.Axes(xlCategory, xlPrimary).AxisTitle.Text = "=" & Selection.Cells(1, 2).Address(External:=True)
bubbleChart.Chart.SetElement (msoElementPrimaryValueAxisTitleRotated)
bubbleChart.Chart.Axes(xlValue, xlPrimary).AxisTitle.Text = "=" & Selection.Cells(1, 3).Address(External:=True)
bubbleChart.Chart.SetElement (msoElementPrimaryCategoryGridLinesMajor)
bubbleChart.Chart.Axes(xlCategory).MinimumScale = 0
End Sub
My input sample:
Label Hour Day count
01-SUNDAY 14 1 1
01-SUNDAY 19 1 1
02-MONDAY 12 2 1
02-MONDAY 13 2 1
02-MONDAY 14 2 2
02-MONDAY 16 2 2
Without using VBA, right click on the bubbles and select Add Data Labels. Then, right click on the data labels and click Format Data Labels. Under Label Options, select Value From Cells and specify the cells containing the labels you'd like to use.
DataLabel.Text is a method for a Point, not the NewSeries
This code:
For r = 2 To Selection.Rows.Count
With bubbleChart.Chart.SeriesCollection.NewSeries
[...]
.DataLabel.Text = "txt"
End With
Next
...attempts to label the series, and fails.
Recognizing this code as being from another famous example of "multi-series Bubble Charts", it is a logical assumption that we only need to handle 1 data point per series, which makes the following code the solution:
For r = 2 To Selection.Rows.Count
With bubbleChart.Chart.SeriesCollection.NewSeries
[...]
.Points(1).HasDataLabel = True
.Points(1).DataLabel.Text = "txt"
End With
Next

Resources