Forum Discussion

patBear's avatar
patBear
Helper I
4 years ago
Solved

Deneb - 3x3 subplot

I'm wanting to create a chart (I'm calling it a 3x3 subplot, but this may not be the correct name) in Deneb: for each categorical x and y value, I want a stacked bar chart of the project names. [Eventually, colors will be added from a z value, highlighting based on group or tier, etc.] I'm using this sample data

and the code below. Currently, the bars are layered rather than stacked. I think I need to incorporate this transformation (measure cnt = COUNT('Table'[name]))

  "transform": [
    {
      "stack": "cnt",
      "as": ["ymin", "ymax"],
      "groupby": ["x", "y"]
    }
  ]

but I'm not sure how/where to insert it into the code below.

{
  "data": {"name": "dataset"},
  "layer": [
    {
      "mark": {
        "type": "bar",
        "stroke": "black",
        "strokeWidth": 1,
        "tooltip": true
      }
    },
    {
      "mark": "text",
      "encoding": {
        "text": {
          "field": "Name",
          "type": "nominal"
        }
      }
    }
  ],
  "encoding": {
    "x": {
      "field": "x",
      "type": "nominal",
      "scale": {
        "domain": ["low", "med", "high"]
      },
      "axis": {
        "title": "X Level",
        "labelAngle": 0
      }
    },
    "y": {
      "field": "y",
      "type": "nominal",
      "scale": {
        "domain": ["high", "med", "low"]
      },
      "axis": {"title": "Y Level"}
    }
  }
}

 To be clear, I'm aiming for this sort of chart where names have been removed. 

 
  • Hi patBear,

    This should be achievable with a stack transform at the top-level, as you have guessed. To make the stack work better, I've added a window / row_number transform beforehand to group and sort each square by z:

     

      "transform": [
        {
          "window": [
            {
              "op": "row_number",
              "as": "group_row_number"
            }
          ],
          "groupby": ["x", "y"],
          "sort": [{"field": "z"}]
        },
        {
          "stack": "group_row_number",
          "groupby": ["x", "y"],
          "sort": [{"field": "z"}],
          "as": ["y1", "y2"]
        }
      ]

     

    This has the effect of assigning a sequence to each row as follows:

    Name group_row_number
    AA 2
    BB 1
    CC 1
    DD 1
    EE 2
    FF 1
    GG 2
    HH 1

    We then use the same group approach in the stack, and use the group_row_number as the field to stack on.

    Once this is done, I just need to use a yOffset in the encoding with the generated y1 field:

     

        "yOffset": {
          "field": "y1",
          "scale": {"reverse": true}
        }

     

    One thing I've done is reverse the scale so that marks are drawn from the bottom of each square.

    The resulting visual looks as follows:

    For completeness, here's the full spec:

     

    {
      "data": {"name": "dataset"},
      "transform": [
        {
          "window": [
            {
              "op": "row_number",
              "as": "group_row_number"
            }
          ],
          "groupby": ["x", "y"],
          "sort": [{"field": "z"}]
        },
        {
          "stack": "group_row_number",
          "groupby": ["x", "y"],
          "sort": [{"field": "z"}],
          "as": ["y1", "y2"]
        }
      ],
      "layer": [
        {
          "mark": {
            "type": "bar",
            "stroke": "black",
            "strokeWidth": 1,
            "tooltip": true
          }
        },
        {
          "mark": "text",
          "encoding": {
            "text": {
              "field": "Name",
              "type": "nominal"
            }
          }
        }
      ],
      "encoding": {
        "x": {
          "field": "x",
          "type": "nominal",
          "scale": {
            "domain": ["low", "med", "high"]
          },
          "axis": {
            "title": "X Level",
            "labelAngle": 0
          }
        },
        "y": {
          "field": "y",
          "type": "nominal",
          "scale": {
            "domain": ["high", "med", "low"]
          },
          "axis": {"title": "Y Level"}
        },
        "yOffset": {
          "field": "y1",
          "scale": {"reverse": true}
        }
      }
    }

     

    Hopefully this is what you're expecting, but should be possible to tweak to your liking if not.

    Good luck (and thanks for considering Deneb)!

    Daniel

2 Replies

  • dm-p's avatar
    dm-p
    Super User

    Hi patBear,

    This should be achievable with a stack transform at the top-level, as you have guessed. To make the stack work better, I've added a window / row_number transform beforehand to group and sort each square by z:

     

      "transform": [
        {
          "window": [
            {
              "op": "row_number",
              "as": "group_row_number"
            }
          ],
          "groupby": ["x", "y"],
          "sort": [{"field": "z"}]
        },
        {
          "stack": "group_row_number",
          "groupby": ["x", "y"],
          "sort": [{"field": "z"}],
          "as": ["y1", "y2"]
        }
      ]

     

    This has the effect of assigning a sequence to each row as follows:

    Name group_row_number
    AA 2
    BB 1
    CC 1
    DD 1
    EE 2
    FF 1
    GG 2
    HH 1

    We then use the same group approach in the stack, and use the group_row_number as the field to stack on.

    Once this is done, I just need to use a yOffset in the encoding with the generated y1 field:

     

        "yOffset": {
          "field": "y1",
          "scale": {"reverse": true}
        }

     

    One thing I've done is reverse the scale so that marks are drawn from the bottom of each square.

    The resulting visual looks as follows:

    For completeness, here's the full spec:

     

    {
      "data": {"name": "dataset"},
      "transform": [
        {
          "window": [
            {
              "op": "row_number",
              "as": "group_row_number"
            }
          ],
          "groupby": ["x", "y"],
          "sort": [{"field": "z"}]
        },
        {
          "stack": "group_row_number",
          "groupby": ["x", "y"],
          "sort": [{"field": "z"}],
          "as": ["y1", "y2"]
        }
      ],
      "layer": [
        {
          "mark": {
            "type": "bar",
            "stroke": "black",
            "strokeWidth": 1,
            "tooltip": true
          }
        },
        {
          "mark": "text",
          "encoding": {
            "text": {
              "field": "Name",
              "type": "nominal"
            }
          }
        }
      ],
      "encoding": {
        "x": {
          "field": "x",
          "type": "nominal",
          "scale": {
            "domain": ["low", "med", "high"]
          },
          "axis": {
            "title": "X Level",
            "labelAngle": 0
          }
        },
        "y": {
          "field": "y",
          "type": "nominal",
          "scale": {
            "domain": ["high", "med", "low"]
          },
          "axis": {"title": "Y Level"}
        },
        "yOffset": {
          "field": "y1",
          "scale": {"reverse": true}
        }
      }
    }

     

    Hopefully this is what you're expecting, but should be possible to tweak to your liking if not.

    Good luck (and thanks for considering Deneb)!

    Daniel

    • patBear's avatar
      patBear
      Helper I

      Thanks for this.  It's exactly the structure I was needing to move forward!