BookmarkSubscribeRSS Feed

How to create variable view stacked bar charts in SAS Visual Analytics with #D3Thursday

Started ‎10-25-2018 by
Modified ‎03-25-2019 by
Views 4,016

This #D3Thursday we will look at how you can use Visual Analytics (VA) selections to create a variable view stacked bar chart!

 

Variable View Stacked Bar ChartVariable View Stacked Bar Chart

 

 This post’s topics include:

  • Using selections to create a variable view chart
  • Creating dynamic axes

Creating a Variable View Chart

Sometimes a single view of our chart doesn't tell the entire story of the data. A stacked bar chart is a good example of this. A stacked bar chart makes it simple to compare totals, but makes it difficult to compare values across categories. We can alleviate this issue by allowing the user to switch between a left aligned view and a center aligned view as shown above.

 

Modifying the Data

Currently, only one type of message is sent to the Data-Driven Content (DDC) object: the data message. That means we have to use VA selections in order to tell our chart which view to use. This has the unfortunate consequence that our chart can no longer receive selections from VA. It can, however, send selection messages if we choose to program it to do so (we haven't in this example).

 

Imagine we want to visualize the following data set using a stacked bar chart:

 

Year Country Population (in millions)
1960 Andorra 10.6
1961 Andorra 11.2

.

.

.

.

.

.

.

.

.

1960 Belgium 5.7
1961 Belgium 6.1

.

.

.

.

.

.

.

.

.

 

 

In order to allow the user to switch between views we must add a column to indicate the views. We can make this modification easily with a SAS data step.

 

data YOUR_NEW_LIB.YOUR_NEW_DS;
  set YOUR_ORIG_LIB.YOUR_ORIG_DS;
  if mod(_N_, 2) eq 0 then View = "Center Aligned";
  else View = "Left Aligned";
run;

Our new data set will look like the following:

 

 

Year Country Population (in millions) View
1960 Andorra 10.6 Center Aligned
1961 Andorra 11.2 Left Aligned

.

.

.

.

.

.

.

.

.

.

.

.

1960 Belgium 5.7 Center Aligned
1961 Belgium 6.1 Left Aligned

.

.

.

.

.

.

.

.

.

.

.

.

 

Now all we have to do is create a button bar (or other VA control object) with the role View that has a linked selection to our DDC object. This will create the brush column that we use to determine which view is selected by the user. 

 

Year Country Population (in millions) View Brush
1960 Andorra 10.6 Center Aligned 1
1961 Andorra 11.2 Left Aligned 0

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

1960 Belgium 5.7 Center Aligned 1
1961 Belgium 6.1 Left Aligned 0

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

 

For your convenience I've included a sample data set (Sample_Data_8.sas7bdat) as well as the SAS code (8_Sample_Data_Generator.sas) used to generate the sample data set. You can use this sample data set to try out the chart, or modify the SAS code to fit your own purposes.

 

Accommodating Variable Views

Now that we have our data set prepared, we can move on to the work of accommodating multiple views in our chart. The first step is to validate the data roles we are receiving and determine which view to use.

// Validate data roles
if (
!va.contentUtil.validateRoles(messageFromVA, [
"string",
"string",
"number",
"string",
"number"
])
) {
va.messagingUtil.postInstructionalMessage(
VA_RESULT_NAME,
"D3 Variable View Stacked Bar Chart expects columns to be assigned in this order:\n" +
" 1. Y Category (string)\n" +
" 2. Group By Category (string)\n" +
" 3. Measure (number)\n" +
" 4. View (string)\n" +
" 5. View Selection (from linked selection) (number)"
);
return;
}

// Determine whether or not data should be centered based on selection
CENTERED =
(VA_MESSAGE.data[0][3] == "Center Aligned" && VA_MESSAGE.data[0][4] == 1) ||
(VA_MESSAGE.data[0][3] == "Left Aligned" && VA_MESSAGE.data[0][4] == 0);

First, notice that we are now making the brush column a required role. This means our chart will not render if the linked selection is not set up. You could make this an optional role and have a default view when the role isn't specified, but there isn't much point using this chart if you aren't supporting variable views. In that case you would be better off using the standard VA stacked bar chart.

 

Secondly, we are only supporting two options for views here, "Center Aligned" or "Left Aligned". You could create a graph that supports more than two views, but doing so causes a few problems. 

  1. Each new view will significantly complicate the code necessary for transitions.
  2. You will have to loop over multiple entries in order to determine which view is selected. With two view options, you can tell which is selected from the first row.
  3. You would have to ensure that no filter actions occur that remove all entries for a certain view. Even if we every entry in our data set had the value of "Center Aligned" for the View column, we could still support both views.

 

The next step to accommodating multiple views is creating transitions for these views. By now you are quite familiar with how to create D3.js transitions so we will keep this snippet brief.

DATA_BARS.enter()
...
.attr("x", function() {
// Get previous element
const prev = getNeighborElement(this, "previousElementSibling");

if (!prev) {
// Return 0 if no previous element
return 0;
} else {
// Otherwise, use stored data to determine return
const prevD = JSON.parse(d3.select(prev).attr("data-d"));

if (CENTERED) {
// Iterate to find max from previous category, then return right edge of max
const prevArr = d3.selectAll("." + prevD.categoryNoSpace).nodes();
let max = prevArr[0];

for (let i = 1; i < prevArr.length; i++) {
if (prevArr[i].width.baseVal.value > max.width.baseVal.value) {
max = prevArr[i];
}
}

return max.x.baseVal.value + max.width.baseVal.value + 1;
} else {
// Return right edge position of previous
return prev.x.baseVal.value + prev.width.baseVal.value + 1;
}
}
});
...

The key thing to notice here is that you must check which view is selected, and then create logic for all of your transitions for each possible view. As you can imagine, supporting more than two views could quickly become cumbersome.

 

Dynamic Axes

Finally, let's take a look at how to create dynamic axes with D3.js. The problem with how we created axes for our original bar charts in the second and third post in this series is that the same number of ticks are used no matter what the dimensions of the graph are. This can cause ticks to overlap as the graph frame is shrunk.

 

Static AxesStatic Axes

 The solution is to tell D3.js exactly which ticks to create to ensure that they don't overlap.

Y_AXIS.enter()
...
.call(
d3
.axisLeft(Y_SCALE)
.tickValues(
getTickValues(
d3.min(DATA, function(d) {
return d.year;
}),
d3.max(DATA, function(d) {
return d.year;
}) + 1,
parseInt(CHART_HEIGHT / Y_TICK_HEIGHT),
1
)
)
.tickFormat(d3.format("d"))
.tickSizeOuter(0)
);

...

// Compute array of readable tick values between min (inclusive) and max (exclusive) of length less than count
function getTickValues(min, max, count, minInterval) {
const pattern = [5, 2, 1];
let tickValues;
let range = max - min;
let pow = Math.floor(Math.log10(range));
let p = 0;
let interval = pattern[p] * Math.pow(10, pow);

do {
tickValues = d3.range(min, max, interval);

if (p == 2) {
p = 0;
pow--;
} else {
p++;
}

interval = pattern[p] * Math.pow(10, pow);
} while (
d3.range(min, max, interval).length <= count &&
interval >= minInterval
);

return tickValues;
}

 

Now instead of having ticks assigned by D3.js, we determine exactly how many ticks can fit on our axis and then determine a suitable range of ticks that is within that limit.

 

Dynamic AxesDynamic Axes

 

Additional Resources

Next Post

Next post we will take a look at how we can apply the variable view chart technique to last week's radar chart!D3Thursday Logo.png

 

Remember to follow these articles on the SAS Communities Library and the #D3Thursday Twitter hashtag. Comment below or Tweet your questions and comments!

Version history
Last update:
‎03-25-2019 10:02 AM
Updated by:
Contributors

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!

Free course: Data Literacy Essentials

Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning  and boost your career prospects.

Get Started

Article Labels
Article Tags