Customise hover of parcats plotly.js chart
Asked Answered
T

2

9

I've used a plotly.js parcats chart to make a sequential sankey (eg https://github.com/EE2dev/sequence-explorer). Here are the example parcats that shows changes in grades between quizzes:

var trace1 = {
  type: 'parcats',
  hoveron: 'color',
  dimensions: [
    {
      label: 'Quiz 1',
      values: ['Black', 'Black', 'Black', 'Brown', 'Brown', 'Brown', 'Red', 'Brown'],
    },
    {
      label: 'Quiz 2',
      values: ['Brown', 'Brown', 'Brown', 'Brown', 'Brown', 'Blue', 'Blue', 'Blue'],
    },
    {
      label: 'Quiz 3',
      values: ['Female', 'Female', 'Female', 'Male', 'Female', 'Male', 'Male', 'Male'],
    },
  ],
};

var data = [ trace1 ];

var layout = {width: 600};

Plotly.newPlot('myDiv', data, layout);

When I hover over a path the full path from 'Quiz 1' to 'Quiz 3' highlights but what I'd like is for only the path between 'Quiz 1' to 'Quiz 2' to highlight. I tried hover on but nothing changed.

This is the fiddle

var trace1 = {
  type: 'parcats',
  hoveron: 'color',
  dimensions: [
    {
      label: 'Quiz 1',
      values: ['Black', 'Black', 'Black', 'Brown', 'Brown', 'Brown', 'Red', 'Brown'],
    },
    {
      label: 'Quiz 2',
      values: ['Brown', 'Brown', 'Brown', 'Brown', 'Brown', 'Blue', 'Blue', 'Blue'],
    },
    {
      label: 'Quiz 3',
      values: ['Female', 'Female', 'Female', 'Male', 'Female', 'Male', 'Male', 'Male'],
    },
  ],
};

var data = [ trace1 ];

var layout = {width: 600};

Plotly.newPlot('myDiv', data, layout);
<head>
  <!-- Plotly.js -->
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>

<body>
  <div id="myDiv"></div>
</body>

Is there a way to customize the hover functionality?

Transistor answered 11/6, 2021 at 9:16 Comment(1)
it seems you need a plot with splitted parts, and each part has its own hover state.Demonstrator
G
3

Unfortunately, I believe that's not possible on this specific parcats ploty chart.

When this parcats chart is created, its generate an svg path for each "ROW", and when i say "ROW" you can understand it being like this:

  • Row 1: Black - Brown - Female
  • Row 2: Brown - Brown - Female
  • Row 3: Brown - Brown - Male
  • Row 4: Brown - Blue - Male
  • Row 5: Red - Blue - Male

This way, we can say that your chart has 5 "ROWS".

And as I said before, each of this "ROW" is a SVG path, like this:

<path class="path" stroke-opacity="1" fill="#1f77b4" fill-opacity="0.6"
stroke="lightgray" stroke-width="0.2" d="M 40,0l16,0 C56,0 544,4 544,
4l16,0 C560,4 1048,4 1048,4l16,0 l0,95.25 l -16,0 C1048,99.25 560,
99.25 560,99.25l-16,0 C544,99.25 56,95.25 56,95.25l-16,0 Z"></path>

So, when you hover this path, it changes fill-opacity, stroke and stroke-width, like this:

<path class="path" stroke-opacity="1" fill="#1f77b4" fill-opacity="0.8"
stroke="white" stroke-width="0.3" d="M 40,0l16,0 C56,0 544,4 544,
4l16,0 C560,4 1048,4 1048,4l16,0 l0,95.25 l -16,0 C1048,99.25 560,
99.25 560,99.25l-16,0 C544,99.25 56,95.25 56,95.25l-16,0 Z"></path>

Therefore, its not possible to highlight only paths 'Quiz 1' to 'Quiz 2' when you hover it.

What you can do here, is use the sankey chart type instead:

var trace1 = {
  type: "sankey",
  orientation: "h",
  node: {  
   groups: ["Quiz 1", "Quiz 2", "Quiz 3"],
   label: ["Black", "Brown", "Red", "Brown", "Blue", "Female", "Male"],
   color: ["blue", "blue", "blue", "blue", "blue", "blue", "blue"]
  },
  link: {
    source: [0,1,1,1,2,3,3,3,4,4],
    target: [3,3,3,4,4,5,5,6,6,6],
    value:  [3,1,1,2,1,3,1,1,2,1]
  }
};

var data = [ trace1 ];

var layout = {
   title: {
      text:'SANKEY Title',
      font: {
         family: 'Courier New, monospace',
         size: 24
      },
   },
   annotations: [{
      text: 'QUIZ 1',
      x: 0.0,
      y: 1.1,
      showarrow: false,
      font: {size: 12}
    }, 
    {
      text: 'QUIZ 2',
      x: 0.5,
      y: 1.1,
      showarrow: false,
      font: {size: 12}
    }, 
    {
      text: 'QUIZ 3',
      x: 1.0,
      y: 1.1,
      showarrow: false,
      font: {size: 12}
    }],
   width: 600,
};

Plotly.newPlot('myDiv', data, layout);
<head>
  <!-- Plotly.js -->
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>

<body>
  <div id="myDiv"></div>
</body>

As you can see, isn't exactly like the parcats chart, but it does the job.

Some infos about this chart:

  • Unlike parcats, it creates an svg path for each part of the chart, so the highlight hover is separated for each path.

Just to be clear, the Row 1: Black - Brown - Female has now TWO svg paths, like:

<!-- Black - Brown PATH -->
<path class="sankey-link" d="M30,1.9895196601282805e-13C603.5,
1.9895196601282805e-13 603.5,15.000000000000114 1177, 15.000000000000114L1177,
105.00000000000011C603.5,105.00000000000011 603.5,90.0000000000002 30,90.0000000000002Z" 
style="stroke: rgb(68, 68, 68); stroke-opacity: 1; fill: rgb(0, 0, 0); fill-opacity: 0.2;
stroke-width: 0; opacity: 1;"></path>

<!-- Brown - Female PATH -->
<path class="sankey-link" d="M1207,15.000000000000114C1780.5,
15.000000000000114 1780.5,15.000000000000142 2354,15.000000000000142L2354,
105.00000000000014C1780.5,105.00000000000014 1780.5,
105.00000000000011 1207,105.00000000000011Z" style="stroke: rgb(68, 68, 68);
stroke-opacity: 1; fill: rgb(0, 0, 0); fill-opacity: 0.2; stroke-width: 0; opacity: 1;"></path>
  • This chart need to have the Link field, with the specific values (source, target and value). You can check more about this option here.

  • You can edit and customize the labels in this chart. You can check more about it here and here.

Gunpoint answered 9/7, 2021 at 21:48 Comment(5)
Is it possible to add a Quiz 1, Quiz 2, Quiz 3 label to each level like the parcats?Transistor
@orbital, I believe it's not possible to add a label to each level. But you can accomplished that with a workaround -> annotations property inside layout object. I've updated the chart code snippet and you can check it.Gunpoint
Thank you @luis-paulo-pintoTransistor
Trying to find out how to give you the bountyTransistor
i"m glad that you are satisfied with the solution! About the bounty, basically you just need to set the answer as accepted, click on the check mark beside the answer to toggle it from greyed out to filled in (more info here). And about the bounty you can check more about it here.Gunpoint
S
1

As far as I understand, the Parallel Categories Diagram is aimed at working with whole lines and it does not have the ability to select individual line segments.

However, there is another diagram that looks similar and yet focuses on individual segments. This is the Sankey Diagram. But it requires rearranging the original data to indicate exactly how the individual values change on each segment.

  1. Set the type parameter as sankey.
  2. In the label parameter, we sequentially list all the values that occur in our diagram. First for the first quiz (Black, Brown, Red), then for the second (Brown, Blue) and finally for the third (Female, Male).
  3. Thus, we turn all the values into array elements, and thanks to this we can access them by their indecies. For example, Brown from the first quiz has index 1, and Brown from the second quiz has index 3 (the indices in the array start from zero). Then we use these indices in the source and target parameters.
  4. The values in link describe all the segments in the diagram. For convenience, I have arranged them in columns. For example, the first column 0, 3, 3 means that Black in the Quiz 1 (index 0 in source) and Black in the Quiz 2 (index 3 in the target) have 3 participants (3 in value).

For example: https://jsfiddle.net/glebkema/subnqp8v/

var trace1 = {
  type: 'sankey',
  node: {
    pad: 15,
    thickness: 20,
    line: {
      color: "black", 
      width: 0.5
    },
    label: [
      'Black', 'Brown', 'Red',  // Quiz 1
      'Brown', 'Blue',          // Quiz 2
      'Female', 'Male'          // Quiz 3
    ],
    color: "blue",
  },
  link: {     
    //        Quiz 1 -> Quiz 2                  Quiz 2 -> Quiz 3 
    //        Quiz 1: Black Brown Red  Brown    Quiz 2: Brown  Brown Blue
    source: [         0,    1,    1,   2,               3,     3,    4   ],
    //        Quiz 2: Brown Brown Blue Blue     Quiz 3: Female Male  Male    
    target: [         3,    3,    4,   4,               5,     6,    6   ],
    value:  [         3,    2,    2,   1,               4,     1,    3   ],
  },
};

var data = [ trace1 ];

var layout = {width: 600};

Plotly.newPlot('myDiv', data, layout);
<!-- Plotly.js -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>

<div id="myDiv"></div>
Scheers answered 9/7, 2021 at 21:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.