Creating a 2d stacked column chart with priority - graph

What I'm trying to do is create a 2d stacked chart where the position of my series means something like where they are in a queue (position 1 - being the upermost section of the stacked column is last to get served and position 2- is the bottom section of the stacked column will be first up).
I've formatted my data to looks like this (but this can be easily changed if the solution needs it to be):
Task 1 Task 2 Task 3 <- x-axis
A 100 B 400 B 510 <- This row is position 1
B 200 A 200 A 300 <- This row is position 2
^-Legend
The issue I'm having is that I want all tasks on the same chart and excel isn't recognizing at every x the position of A and B. It simply is assuming from Column 1 that Row 2 is A and Row 3 is B and is not adjusting in each subsequent column based on the A/B keys. I'm wondering if there's a way to do this.
As a recap, is it possible to get a 2d stacked chart with multiple x-values that recognizes the position of your legend keys (whether it should be at the top or bottom of the column) at each unique x-value. Any solution either VBA or in-sheet formula I haven't had any luck with.Thanks in advance.

'Run this macro from the sheet containing your data, after highlightling the data.
Sub Macro3()
'The below code assumes that you have already selected
'the columns containing your data and that the first column,
'and every 2nd column after that contains your legend keys.
Dim rng As Range
Set rng = Selection
Dim colNum As Integer
Dim rowNum As Integer
Dim strLegend As String
Dim rowStart As Integer
Dim colStart As Integer
Dim strSeries As String
Dim i As Integer
Dim seriesNum As Integer
Dim shtName As String
rowStart = rng.Row
colStart = rng.Column
shtName = ActiveSheet.Name & "!"
'Creates an empty chart...
ActiveSheet.Shapes.AddChart.Select
'...of type StackedColumn.
ActiveChart.ChartType = xlColumnStacked
seriesNum = 0
'Select all the cells that match the legend in the first column.
For rowNum = 0 To rng.Rows.Count - 1
strLegend = Cells(rowStart + rowNum, colStart).Value
strSeries = "=" & shtName & Cells(rowStart + rowNum, colStart + 1).Address
For colNum = 2 To rng.Columns.Count - 1 Step 2
For i = 0 To rng.Rows.Count - 1
If Cells(rowStart + i, colStart + colNum).Value = strLegend Then
strSeries = strSeries & "," & shtName & Cells(rowStart + i, colStart + colNum + 1).Address
Exit For
End If
Next
Next
'Create a new series.
ActiveChart.SeriesCollection.NewSeries
seriesNum = seriesNum + 1
'Set the legend.
ActiveChart.SeriesCollection(seriesNum).Name = strLegend
'Set the X axis labels to nothing, so the default is used.
ActiveChart.SeriesCollection(seriesNum).XValues = ""
'Set the series data.
ActiveChart.SeriesCollection(seriesNum).Values = strSeries
Next
'An extra series gets added automatically???
'This code removes it.
If ActiveChart.SeriesCollection.Count > rng.Rows.Count Then
ActiveChart.SeriesCollection(rng.Rows.Count + 1).Delete
End If
End Sub
This code requires that your legend values and number values each be in separate columns like shown below. The labels 'Task 1', etc. are not used in this example.
A | 100 | B | 400 | B | 510
B | 200 | A | 200 | A | 300

Related

ASP/VB: Counting and ordering array values

I am new to writing in asp and vb and am stuck on a piece of logic where I need to retrieve data from a web form, count the number of entries and then order them alphanumerically.
I have a webform with the multiple text boxes that can be filled out and submitted that looks a little like this: (excuse the spreadsheet, it's a visual aid only)
I have made an array that contains their values like this:
myArray = array(town, medal, record, sport)
I would like to count and order (everything alphanumerically) the total medals, how many of each medal each town won and the number of records set by each town.
My psuedocode looks a little like this, hopefully I am a little on the right track in terms of logic. The main area I am a little short in is knowing what statements would be good and where, especially to order them alphanumerically.
'this is the psuedocode for the total medals per town
tally = 0 'Set tally to 0
for myArray(town) 'For each town
for myArray(medal) 'For each medal
tally = tally + 1 'Add 1 to the total tally
response.write(myArray(town) "has" tally "medals" & "<br>")
next
next
'this is the pseudocode for the individual medals
for myArray(town) 'For each town
for myArray(medal) 'For each medal
goldTally = 0
silverTally = 0
bronzeTally = 0
if medal = "G"
goldTally = goldTally + 1
elseif medal = "S"
silverTally = silverTally + 1
else medal = "B"
bronzeTally = bronzeTally + 1
response.write(myArray(town) "has:" goldTally "gold medals" &"<br>"
silverTally "silver medals" &"<br>"
bronzeTally "bronze medals" &"<br>"
next
next
Any help you can give would be greatly appreciated thanks heaps.
The VBScript tool for counting/grouping/classifying is the Dictionary. Some use cases: Set ops, word list, split file.
Simple Arrays can be sorted via using an ArrayList. [Array vs Arraylist], fancy sorting7.
For tabular data, use a disconnected recordset.
Inline demo:
Option Explicit
' simple sample data
Dim a : a = Split("b c a b b c a a b")
' use a dictionary for counting/grouping
Dim d : Set d = CreateObject("Scripting.Dictionary")
Dim e
For Each e In a
d(e) = d(e) + 1
Next
WScript.Echo Join(d.Keys)
WScript.Echo Join(d.Items)
' use an ArrayList for sorting simple arrays
Dim l : Set l = CreateObject("System.Collections.ArrayList")
For Each e in a
l.Add e
Next
l.Sort
WScript.Echo Join(l.ToArray())
' use a disconnected recordset for tabular data
Const adVarChar = 200
Const adInteger = 2
Const adClipString = 2
Dim r : Set r = CreateObject("ADODB.Recordset")
r.Fields.Append "k", adVarChar, 50
r.Fields.Append "n", adInteger
r.Open
For Each e In d.Keys
r.AddNew
r.Fields("k").value = e
r.Fields("n").value = d(e)
r.Update
Next
r.MoveFirst
Do Until r.EOF
WScript.Echo r.Fields("k").value, r.Fields("n").value
r.MoveNext
Loop
r.Sort = "k DESC"
WScript.Echo r.GetString(adClipString, , ", ", "; ", "null")
output:
cscript 39305170.vbs
b c a
4 2 3
a a a b b b b c c
a 3
c, 2; b, 4; a, 3;
BTW: Even in a pseudo code language,
for myArray(town) 'For each town
and
response.write(myArray(town) "has:" goldTally "gold medals" ...
can't work at the same time.

Iterating through a data set and merging specific pairs of rows where data is null in R or excel

I have a data set with several hundred rows. Most rows have complete information, but in some cases two rows share the same key while some attributes are repeated, others are not. Here is an example:
Key Campaign Message Stat1 Stat2 Stat3 Stat4
123 Fun yay 1 2
123 temp yay 3 4
Intended result
123 Fun yay 1 2 3 4
Issues:
Needs to search the entire dataframe of hundreds of records, most of which are not duplicates. Ignore the non-duplicates
Has to specify that when combining rows to accept the Campaign data that is NOT "temp"
All other columns where data matches is ok
Columns where one value is null will result in the non-null value being used in the new record
I am open to solutions in R, SQL or excel (vba)
Appreciate any help!
Turned out to be a bit more involved than I thought, but here it is. I am using a collection to merge duplicate keys. Change IGNORE_TEMP constant to include or exclude temp records.
Sub mergeNonNulls()
' change this constant to ignore or include temp results
Const IGNORE_TEMP As Boolean = True
' temporary store of merged rows
Dim cMerged As New Collection
' data part of the table
Dim data As Range
Set data = ActiveSheet.[a2:g3]
Dim rw As Range ' current row
Dim r As Range ' temporary row
Dim c As Range ' temporary cell
Dim key As String
Dim arr() As Variant
Dim v As Variant
Dim vv As Variant
Dim i As Long
Dim isChanged As Boolean
For Each rw In data.Rows
key = rw.Cells(1) ' the first column is key
If IGNORE_TEMP And rw.Cells(2) = "temp" Then
DoEvents ' pass temp if enabled
Else
If Not contains(cMerged, key) Then
' if this is new key, just add it
arr = rw
cMerged.Add arr, key
Else
' if key exists - extract, merge nulls and replace
arr = cMerged(key)
' iterate through cells in current and stored rows,
' identify blanks and merge data if current is empty
i = 1
isChanged = False
For Each c In rw.Cells
If Len(Trim(arr(1, i))) = 0 And Len(Trim(c)) > 0 Then
arr(1, i) = c
isChanged = True
End If
i = i + 1
Next
' collections in vba are immutable, so if temp row
' was changed, replace it in collection
If isChanged Then
cMerged.Remove key
cMerged.Add arr, key
End If
End If
End If
Next
' output the result
Dim rn As Long: rn = 1 ' output row
Dim numRows As Long
Dim numCols As Long
With ActiveSheet.[a6] ' output start range
For Each v In cMerged
numRows = UBound(v, 1) - LBound(v, 1) + 1
numCols = UBound(v, 2) - LBound(v, 2) + 1
.Cells(rn, 1).Resize(numRows, numCols).Value = v
rn = rn + 1
Next
End With
End Sub
' function that checks if the key exists in a collection
Function contains(col As Collection, key As String) As Boolean
On Error Resume Next
col.Item key
contains = (Err.Number = 0)
On Error GoTo 0
End Function

how do i total the sum column and row from data table vb.net

Category projectName[1] projectName[2] ... total<br/>
cat[1] 0 1 ??<br/>
cat[2] 2 3 ??<br/>
.. .... ..... .....<br/>
total ?? ?? ??<br/>
Here is my code to display my category of projectName and cat row. The problems is I dont know how to get value to sum of total in the data table.
ExportDT.Columns.Add("Category")
For Each ProjectRow As Data.DataRow In TempProjectDT.Rows
ExportDT.Columns.Add(ProjectRow.Item("NameOfProject").ToString.Trim)
Next
Dim NewRow As Data.DataRow
Dim ComplaintDV As New Data.DataView(TempComplaintDT)
For Each CategoryRow As Data.DataRow In TempComplaintCatDT.Rows
NewRow = ExportDT.NewRow
NewRow.Item("Category") = CategoryRow.Item("Category").ToString
For nLoop As Integer = 1 To ExportDT.Columns.Count - 1
ComplaintDV.RowFilter = "ComplaintCategory='" & CategoryRow.Item("Category").ToString & "' AND NameOfProject='" & ExportDT.Columns(nLoop).ColumnName & "'"
If ComplaintDV.Count > 0 Then
NewRow.Item(ExportDT.Columns(nLoop).ColumnName) = ComplaintDV.Count
Else
NewRow.Item(ExportDT.Columns(nLoop).ColumnName) = "0"
End If
Next
ExportDT.Rows.Add(NewRow)
Next
Let's make an assumption on this one:
For i As Integer = 0 To 2
yourTable.Rows(i).Cells(2).Value = yourTable.Rows(i).Cells(0).Value + yourTable.Rows(i).Cells(1).Value
Next
It lets you calculate the columns to Column3. The Column3 will show the sum.

count the unique values in one column in EXCEL 2010 or R with 1 million rows

After searching the forum, I did not find a good solution for this question. If I missed it, please tell me.
I need to count the unique values in one column in EXCEL 2010.
The worksheet has 1 million rows and 10 columns. All cell values are string or numbers.
I used the solution at Count unique values in a column in Excel
=SUMPRODUCT((A2:A1000000<>"")/COUNTIF(A2:A100000,A2:A1000000&""))
But, it runs so long time that the EXCEL is almost frozen. And, it generates 25 processes in Win 7.
Are there more efficient ways to do it?
Also, in the column, all values have for format of
AX_Y
here, A is a character, X is an integer, Y is an integer from 1 to 10.
For example, A5389579_10
I need to cut off the part after (including) undersocre. for the example,
A5389579
This is what I need to count as unique values in all cells in one column.
For example, A5389579_10
A1543848_6
A5389579_8
Here, the unique value has 2 after removing the part after underscore.
How to do it in EXCEL VBA and R (if no efficient solution for EXCEL)?
If you want to do this by VBA, you can take advantage of the Collection object. Since collections can only contain unique values, trying to add all of your input data to a collection will result in an array of unique values. The code below takes all the variables in a selected range and then outputs an array with distinct values to an other sheet (in this case a sheet named Output).
Sub ReturnDistinct()
Dim Cell As Range
Dim i As Integer
Dim DistCol As New Collection
Dim DistArr()
Dim OutSht As Worksheet
Dim LookupVal As String
Set OutSht = ActiveWorkbook.Sheets("Output") '<~~ Define sheet to putput array
If TypeName(Selection) <> "Range" Then Exit Sub
'Add all distinct values to collection
For Each Cell In Selection
If InStr(Cell.Value, "_") > 0 Then
LookupVal = Mid(Cell.Value, 1, InStr(Cell.Value, "_") - 1)
Else
LookupVal = Cell.Value
End If
On Error Resume Next
DistCol.Add LookupVal, CStr(LookupVal)
On Error GoTo 0
Next Cell
'Write collection to array
ReDim DistArr(1 To DistCol.Count, 1 To 1)
For i = 1 To DistCol.Count Step 1
DistArr(i, 1) = DistCol.Item(i)
Next i
'Outputs distinct values
OutSht.Range("A1:A" & UBound(DistArr)).Value = DistArr
End Sub
Note that since this code writes all the distinct values to a single column in the OutSht-sheet, this will return an error if there are more than 1,048,576 distinct values in your dataset. In that case you would have to split the data to be filled into multiple output columns.
For your specific request to count, use the below in a formula like =COUNTA(GetUniques(LEFT("A1:A100000",FIND("_","A1:A100000")-1)) entered as an array formula with Ctrl+Shift+Enter.
It also accepts multiple ranges / values (e.g. GetUniques("A1:A10","B2:E4"))
Function GetUniques(ParamArray args())
Dim arg, ele, arr, i As Long
Dim c As Collection
Set c = New Collection
For Each arg In args
If TypeOf arg Is Range Then
If arg.Count = 1 Then
arr = array(arg.value)
Else
arr = arg.Value
End If
ElseIf VarType(arg) > vbArray Then
arr = arg
Else
arr = Array(arg)
End If
For Each ele In arr
On Error Resume Next
c.Add ele, VarType(ele) & "|" & CStr(ele)
On Error GoTo 0
Next ele
Next arg
If c.Count > 0 Then
ReDim arr(0 To c.Count - 1)
For i = 0 To UBound(arr)
arr(i) = c(i + 1)
Next i
Set c = Nothing
GetUniques = arr
End If
End Function
edit: added a performance optimisation for ranges (loads them at once into an array - much faster than enumerating through a range)
In R:
# sample data
df <- data.frame(x=1:1000000,
y=sample(1e6:(1e7-1),1e6,replace=T))
df$y <- paste0("A",df$y,"_",sample(1:10,1e6,replace=T))
# this does the work...
length(unique(sub("_[0-9]+","",df$y)))
# [1] 946442
# and it's fast...
system.time(length(unique(sub("_[0-9]+","",df$y))))
# user system elapsed
# 2.01 0.00 2.02
In excel 2010... in the next column add (if original data was in A:A add in B1)
= 1/COUNTIF(A:A,A1) and copy down col B to the bottom of your data. Depending on your PC it may chug away calculating for a long time, but it will work. Then copy col B & paste values over itself.
Then SUM col B

Convert GridView table to Html Table but Rows should be columns and columns should be Rows

I have Dataset ds filled up with values Until now I was displaying values in GridView. Now I want that all the rows should be columns and columns should be rows.
I have 2 options: Either 1 I can directly convert grid to columns and display it, or
2 I can convert the GridView to html and then write loops to convert. I was trying the 2nd option but I cant figure out how I should do that. Below is my code:
For Each dr In dt.Rows
htmlTable = htmlTable + "<TR>"
For Each dc In dt.Columns
htmlTable = htmlTable + "<TD>" + ds.Tables(0).Columns(j).ToString() + ""
j = j + 1
Next
i = i + 1
Next
With this code I am still getting same as GridView. Please help me for converting rows to columns and vice versa.
It looks like your attempting to write an HTML table from the DataTable in the DataSet, flipping rows and columns. The code you posted has several issues, so I'm using it more as pseudo-code for my answer.
The reason you're getting the same thing (in terms of rows and columns) as the GridView is because you loop through each row, and in each row you loop through all the columns - you're not flipping columns and rows at all.
Try something like this:
Dim htmlTable As StringBuilder = new StringBuilder()
Dim numberRows As Integer = ds.Tables(0).Rows.Count - 1
Dim numberCols As Integer = ds.Tables(0).Columns.Count - 1
htmlTable.Append("<table>")
' Loop through each column first
For i As Integer = 0 To numberCols
htmlTable.Append("<tr>")
' Now loop through each row, getting the current columns value
' from each row
For j As Integer = 0 To numberRows
htmlTable.Append("<td>")
htmlTable.Append(ds.Tables(0).Rows(j)(i))
htmlTable.Append("</td>")
Next
htmlTable.Append("</tr>")
Next
htmlTable.Append("</table>")
' To get the value of the StringBuilder, call ToString()
Dim resultHtml = htmlTable.ToString()
For example, if you have a table like this:
col1 col2 col3
a b c
d e f
g h i
j k l
The result would be:
a d g j
b e h k
c f i l

Resources