Scatter Plot in Excel using VBA - excel

I have one column that contains my x data and two columns that contain my y data. I would like to plot y against x, but the length of each column is dependent on a counter variable, i. I do not have experience with vba, but do with coding. Can someone outline the correct syntax to perform this task?
Please and Thank you!

You could goto your 'Developer' tab, and record a macro. While in record mode, create the graph using the data and specifications you desire.
End recording of the macro, and you will then have a template of VBA code (ALT+F11) to work with as a foundation.

It sounds like the best solution is to simply make the X-Y scatter plot ranges longer than you ever expect the data to be (Excel will ignore the blank rows).
But if you want to get fancy, you can populate your scatter plot data with dynamic named ranges that automatically adjust to the length of your data without bothering with VBA:
Create a dynamic named range (AKA named formula) representing your X data column.
Example (assuming column A, and data starting at A1):
=$A$1:INDEX($A$1:$A$1000,MATCH(TRUE,ISBLANK(Sheet1!$A$1:$A$1000),0)-1)
Change $A$1 so that it corresponds to the first row of your data. Change $A$1000 to a row number that is longer than you ever expect your data to be.
Call the named formula above "XColumn" or something similar. To make a named range, do Formulas->Define Name.
Set the X range of your scatter plot equal to:
=Sheet1!XColumn
Note that Excel's plotting window will give an error (devoid of any helpful information regarding how to fix the problem!) if you try to input the named range without the sheet name, even if the named range is scoped to the entire workbook, and even if it's on the same worksheet! -- Annoying, right?
Do this for each column and scatter plot range (Y1 = Y1Column, Y2 = Y2Column, etc.).
Note that this will not work correctly if your columnar data contains text, blanks, errors, etc., but the method can be modified to handle these issues.
To test and make sure your dynamic named ranges are being created as expected, in any cell you can enter:
=SUMPRODUCT(XColumn)
Then do Formulas-->Evaluate Formula-->Evaluate to make sure the XColumn array contains the data you want it to contain.
Optional tip: create another named range called FirstColumn set to the location of the first column, e.g. $A:$A. Make another named range called FirstRow and set it to the location of the first row, e.g. $1:$1. Make yet another named range called MaxRow and set it to the maximum length you ever expect your data to be, e.g. $1000:$1000. Finally:
Replace all instances of $A$1 above with:
INDEX(FirstRow,0,COLUMN(FirstColumn)+<DATA TABLE COLUMN NUMBER - 1>)
Replace all instances of $A$1000 with:
INDEX(MaxRow,0,COLUMN(FirstColumn)+<DATA TABLE COLUMN NUMBER - 1>)
<DATA TABLE COLUMN NUMBER - 1> will be different for each named formula (e.g. 0 for XColumn, 1 for Y1Column, 2 for Y2Column, etc etc).
Now if you ever want to create new data tables and scatter plots in different locations, instead of doing all of the work over again you can just copy and paste your named ranges and you only have to change one or two things instead of 15! Additionally, if the data ever gets longer than you expected, you only have to change one thing instead of 3.

Excel's built-in Table functionality is a great way to automatically increase a range-size when new data is added. Simply select your data, then either press Ctrl + t or go to Insert > Table. Then when you reference your new Table from the Chart it will increase as your data does.

Related

Excel: Flashfill Offset Horizontal + Vertical

So I'm not a fan of VBA and I recently learned that OFFSET can be used with COUNTA to flashfill a range as far at it is as long as you aim for a longer range than you have data. Now I want to be able to achieve this both for columns and rows at the same time, where the rows are averaged. Could this be done? I am banging my head against the wall to find some logic to do it, but can only manage to combine it in a way that multiplies the rows with the number of the column.. which is not desired, of course.
I have posted a Minimal Reproducible Example in Excel Online:
https://onedrive.live.com/view.aspx?resid=63EC0594BD919535!1491&ithint=file%2cxlsx&authkey=!ALmV0VtFb7QZCvI
If you see Cell J9 and J11 you will see what I want to combine. The three rows in J11 and down, I want to average in J10, and spill/flashfill (like J9 and 11 does automatically because of the formula already) them from to the right, for as many columns as there data in the range A1-G4..
So I have raw data of numbers with titles in A1-G4, and by writing =OFFSET($A$1:$A$1,0,0,1,COUNTA($A$1:$EV$1)-1) in J9 I get all the titles of the columns filled from left to right, and by writing =OFFSET($A$1,1,0,COUNTA($A:$A)-1) in J11 I get the rows of the first column filled from top to bottom. They can also be combined, by writing OFFSET(Days,1,0,COUNTA($A:$A)-1,COUNTA(Days)), where "Days" is =OFFSET($A$1:$A$1,0,0,1,COUNTA($A$1:$EV$1)-1) (in a named range for readability) or OFFSET($A$1:$A$1,0,0,1,COUNTA($A$1:$EV$1)-1) without using a named range
As a thought, though I'm not sure how to implement it, maybe this could somehow be used in some form to get the column reference for the horizontal part in combination with =AVERAGE(OFFSET($A$1,1,0,COUNTA($A:$A)-1))
=MID(ADDRESS(ROW(),COLUMN()),2,SEARCH("$",ADDRESS(ROW(),COLUMN()),2)-2)
..found at https://superuser.com/questions/1259506/formula-to-return-just-the-column-letter-in-excel/1259507
Now, based on your explanation, here is the screenshot of my test:
Section A1:Exxx
I have converted that section into a Table, called «TblData», having numerous avantages:
It expands automatically without any additional efforts/formula
We can identify Data by its Columns attributed automatically by the Table [#1], [#2],[#3], [#4], [#5]
Section J9:N9
As a replica of the table name, I have used the following formula to retrieve it:
=INDEX(TblData[#Headers],1,COLUMN(A1)) '<--- This is for J9
=INDEX(TblData[#Headers],1,COLUMN(E1)) '<--- This is for N9
Section J11:Nxx
As a replica of the Table Content, I have used the following formula to populate the content:
=INDEX(TblData,ROW($A1),MATCH(J$9,TblData[#Headers],0)) '<--- This is on J11
=INDEX(TblData,ROW($A3),MATCH(N$9,TblData[#Headers],0)) '<--- This is on N13
Section J10:N10
Now this is the interesting part of the Average, so here is the formula I used for it:
=AVERAGE(TblData[1]) '<--- This is on J10
=AVERAGE(TblData[5]) '<--- This is on N10
NB: (1) Instead of using the Content below J10:N10, I prefer to reuse the Table as it expands automatically as more rows are added.
(2) Unless it is really necessary, I feel it is a double work as well to replicate again A1:Exxx from J9:Nxxx, because you can use the Table for whatever you need, with less maintenance.
Kindly find attached the file as well after I updated those items:
File Link: https://drive.google.com/open?id=1wRbpUxg0XLpfGqdvMF4fNKXDrL7xPPWs
We can correspond more below for further info. Hoping you to strech more your compentence :)
Sorry, mate, I can't figure out what you want to calculate. If it makes sense to add J9+J11 then you could just concatenate the two formulas in J9 and J11 with a plus sign. After much deliberation I decided to assume that your question is not one of formula but of formula-writing - "referencing" for short. Therefore I prepared this answer for you, hoping that it will prove helpful.
Building on your named range Days I suggest you create a dynamic named range Data with this formula.
[Data] =OFFSET(Sheet1!$A$1,0,0,COUNTA(Sheet1!$A:$A),COUNTA(Sheet1!$1:$1))
The range thus defined is dynamic in both directions. However, bearing in mind that OFFSET is volatile (slows down your worksheet) you may like to keep its use limited to this one formula and perhaps start the range at A2, but I shall tempt you to break the rule. Now you can use the INDEX function to refer to the Data range.
= INDEX(Data, [Row number], [Column number])
defines a single cell. But by setting either column or row to zero you can define an entire column or row. =INDEX(Data,0,1) defines column 1 of the Data range, =INDEX(Data,1,0) defines its first row.
=INDEX(OFFSET(Data,1,0),0,1) defines the first column of a range moved down by one row from its original position. I recommend the alternative and start the Data range from A2 and perhaps declare another range for the first row if needed.
=AVERAGE(INDEX(Data,0,1)) would draw the same average you already have in your sheet, provided that Data was defined starting at A2. For fun's sake, =AVERAGE(INDEX(OFFSET(Data,1,0),0,1)) would do the same without the change in the range's definition.
=COLUMN() returns the number of the column this formula resides in. So, you could enter =COLUMN()-6 in column G, copy to the right and get a count starting from 1. (You can do the same vertically with the ROW() function.) Applied to your formula, =AVERAGE(INDEX(Data,0,COLUMN()-6)) would return the average from column 1 if entered in column G, and from columns 2, 3 4, etc as copied to the right.
As I said, I don't understand enough of your request to bring this idea to a conclusion but I think that using the method described above will provide you with a tool to copy formulas into the table your sample has at its right. If you would elaborate on your requirement I might be able to assist more.

Use a named range in a scatter chart

I would like to plot data that is not consecutively written in a column in an Excel 2010 scatter chart. In order to do it I named two ranges that look like =A5,A10...A1000as name1 and name2. I had to use names as the ranges were to big to be handled by the "select data" windows, despite the amount of data is perfectly plotted if they were consecutively written in a column. Then I tried to write the defined names when selecting the data, but an error was prompted. I could avoid the error by writing ={"name1"}, which worked just for the X axis data. When I use the same syntax for y axis, it is transformed to ={0}.
I have read about many dynamic charts using the offset function, but I think this is too much for what I wanted: simple fixed named ranges.
Is there any way to assign named ranges to a scatter chart without using the offset function?
Very good question, I've asked this myself but never bothered to get the answer Before! For some reason, charts do not seem to take workbook global variables for input. You have to use sheet local named ranges, as I show below:
Edit: it works with named ranges of non-consecutive cells too, as the question asks for

Perl Excel::Writer::XLSX - set column format/merge cells dynamically

I am using Excel::Writer::XLSX to create an Excel file from an array of arrays. Right now I'm trying to create a formatted table from the data (as much as I can, as opposed to just spitting it back into another file).
First off, when I use set_column() to set the background color, that color is formatted for the entire column. Is there a way to specify to only go as far as the content in the file goes? Unfortunately, when the program is run it is dynamic each time and unknown what the final row in the table should be.
Second, is there a way to merge cells based on the content inside of them? This has to do with the dynamic problem again, there is an optimal output if all the data I am gathering is online. If that were the case I could easily set a range of what these merged cells should be. But for example, if I have 10 rows of column 2 saying 'A' and then 10 rows of column 2 saying 'B', I would like to merge the A's and B's together. The issue is that is is unknown if it will always have 10 rows with that value inside of it.
Thanks for your input!
First off, when I use set_column() to set the background color, that color is formatted for the entire column. Is there a way to specify to only go as far as the content in the file goes?
No. You will have to have to add the format to the cells as you write them.
But for example, if I have 10 rows of column 2 saying 'A' and then 10 rows of column 2 saying 'B', I would like to merge the A's and B's together.
This isn't possible with Excel::Writer::XLSX. (In fact I don't think it is possible in Excel without using macros).
Since both of your issues relate to not knowing the size and value of the data beforehand then perhaps you could first read your data into an array of arrays, process it to find the required format dimensions and merge ranges and then write them out.

Dynamic chart range using INDIRECT: That function is not valid (despite range highlighted)

I'm trying to create a chart with a range built dynamically using the INDIRECT function. Excel does recognize the range I am creating using INDIRECT as it highlights the corresponding range on the sheet:
However when saving the chart, I get an error message saying the function is not valid:
Does anybody know what the problem is / how to create a dynamic chart range from a specific start to specific end point?
PS: You can download the above spreadsheet here. The formula I was using:
=INDIRECT("sheet!"&E2&":"&E3)
The way you are trying to do it is not possible. Chart data range has to have a fixed address.
There is a way around this, and that's using named ranges
Put the number of rows you want in your data in a cell (e.g., E1)
So, using your example, I put Number of Rows in D1 and 6 in E1
In name manager, define the names for your data and titles
I used xrange and yrange, and defined them as:
xrange: =OFFSET(Sheet1!$A$2,0,0,Sheet1!$E$1)
yrange: =OFFSET(Sheet1!$B$2,0,0,Sheet1!$E$1)
now, to your chart - you need to know the name of the workbook (once you have it set up, Excel's function of tracking changes will make sure the reference remains correct, regardless of any rename)
Leave the Chart data range blank
for the Legend Entries (Series), enter the title as usual, and then the name you defined for the data (note that the workbook name is required for using named ranges)
for the Horizontal (Category) Axis Labels, enter the name you defined for the labels
now, by changing the number in E1, you will see the chart change:
Mine is similar to Sean's excellent answer, but allows a start and end day. First create two named ranges that use Index/Match formulas to pick the begin and end days based on E2 and E3:
rngDay
=INDEX(Sheet1!$A:$A,MATCH(Sheet1!$E$2,Sheet1!$A:$A,0)):INDEX(Sheet1!$A:$A,MATCH(Sheet1!$E$3,Sheet1!$A:$A,0))
rngValue
=INDEX(Sheet1!$B:$B,MATCH(Sheet1!$E$2,Sheet1!$A:$A,0)):INDEX(Sheet1!$B:$B,MATCH(Sheet1!$E$3,Sheet1!$A:$A,0))
You can then click the series in the chart and modify the formula to:
=SERIES(Sheet1!$B$1,Sheet1!rngDay,Sheet1!rngValue,1)
Here's a nice Chandoo post on how to use dynamic ranges in charts.
Just another answer for bits and googles..
If you still want to refer to your start and end cells, you'll need to add a separate formula for your Day Range and your Values Range. Formulas are below and the screenshot shows the formulas used.
Day Range:
="Sheet1!"&$F$2&":"&ADDRESS(ROW(INDIRECT($F$3)),COLUMN(INDIRECT($F$2)))
Values Range:
="Sheet1!"&ADDRESS(ROW(INDIRECT($F$2)),COLUMN(INDIRECT($F$3)))&":"&$F$3
Then add two ranges referencing the INDIRECT values of those cells
Press Ctrl+F3, Click New, and add a new range with the name "chart_days", referring to =INDIRECT(Sheet1!$F$4); and a new range with the name "chart_values", referring to =INDIRECT(Sheet1!$F$5)
Finally, in your chart, add a series that refers to =nameOfYourWorkbook!chart_values
and Edit the category to refer to =nameOfYourWorkbook!chart_days
I use OFFSET to create a defined name formula so that I may define all the ranges for the data, allowing me to have a starting a ending date (or the beginning and ending position of any data set).
For a simple graph, I define the name CategoryLabels as follows:
= OFFSET($A$5; (InicitialMonth-1); 0; LastMonth - (InitialMonth-1))
and DataCars as follows:
= OFFSET($B$5; (InicitialMonth-1); 0; LastMonth - (InitialMonth-1))
You will have to define as many names as Series you want to include, following the same procedure. In this simple case, I only included Car sales.
Initial Month and Last Month are Range Names defined for single cells used to indicate which months of the graph will be included (from starting to ending months).
Remember, as explained by Sean Cheshire and others, that to use the names for the chart values, the name of the spreadsheet must be included.
According to the formula you have shown: =INDIRECT("sheet!"&E2&":"&E3)
you are not naming the sheet correctly.
I would have thought it would be Sheet1! or Sheet2! etc.
Your formula resolves to =sheet!E2:E3 which is not a valid address. The error message you are getting means Excel cannot resolve the input to INDIRECT. INDIRECT is a valid function so the argument you offer it must be invalid.
All of the above answers which state the sheet name have corrected your error but do not mention it... ;)
Named formula with Indirect functions DOES NOT WORK IN CHARTS. It works in other froms as your desired dynamic source will be highlighted, but when you it in chart, it would not be evaluated. Hope Microsoft put a fix on this.
When a line chart's range is a named variable, and the variable has INDIRECT() references through a cell to a range, then the variable must have at least 2 INDIRECT()s separated by a comma.

Modifying a Dynamic Name with VBA

I have a spreadsheet that links to several other spreadsheets to pull data that compiled into one sheet; I am tasked with modifying the sheet to graph this data. There is a row of 12 slots for each month in a person's report; if a person was working in a month then the month will be displayed in the appropriate slot. For the graph I have figured out to use a dynamic name range so that the number of months in the graph is equal the number of months present in the line; the only problem is that the start pointing changes based on what month a person started.
I am able to make a rather large nested if statement but that doesn't seem like a very good way to approach resolving my problem. Is there a way I can have a VBA script change the value of the dynamic name range or just modify the chart's value directly? If this isn't a viable option is there any other way to approach this problem other than a dynamic named range?
I can't change how this report sheet behaves as this is a spreadsheet related to work and that's how they want it to be for reporting reasons; I'm simply looking to add the graphing functionality.
EXAMPLES:
https://www.dropbox.com/s/zn6yt4l6kjvwq33/Example1.xlsx
https://www.dropbox.com/s/j88tgoik68s4lhx/Example2.xlsx
In the first example you see an example of the problem when you select "Second" from the dropbox next to Agent Name. It's including pointless data in the graph.
In the 2nd example I have resolved the problem by adding an if statement to the dynamic named ranges used in the chart; but as you can see to include 2 months it's now massive and to include all 12 it would be pretty much impossible. If I were to add a 3rd person who started in March they would also not work in the 2nd example.
Is there a way I can make it so the values in the Dynamic name range are changeable through VBA? Can I modify the chart's values directly using VBA? Is there a way to bypass this problem without even using VBA?
Thanks.
(I'm not 100% certain I've understood the problem, but this might at least help to get closer...)
Try defining three names, something like this:
start_month
data
chart_data
Where start_month contains the number of the column that you want to be charted, data is all twelve columns of data, something like =OFFSET($C$1,0,0,COUNT($A:$A),12) and chart_data selects the column you want from the data, e.g. =INDEX(data, , start_month)
Now set your chart's series to WorkbookName!chart_data, using whatever your workbook's called. As the start_month value changes, so should the values displayed in the chart.
You can add or modify a Name in VBA like:
Dim nm As Name
Dim rng As Range
'Here, you could use a more complicated expression'
' to derive the proper address dynamically.'
Set rng = Sheets("Sheet1").Range("A2:A25")
'Now, add a Name to the workbook:'
Set nm = ActiveWorkbook.Names.Add("My_Name", rng)
With some string functions/variables, you could derive/caclulate the range address in VBA.

Resources