Multiple X axis in plotly timeseries

Viewed 113

I'm trying to create a timseries in Plotly.js that has multiple X axis to visually group the quarters of a game. Crude depiction of desired end state:

Crude Depiction of Desired End State

I know that Plotly.js supports multiple x-axis, but I'm encountering some strange behavior in which the labels aren't rendering correctly in the graph. I based my attempt on the example provided by plotly for handling multiple axis - the core differences are that my data is time-based and the is x-oriented: https://plotly.com/javascript/multiple-axes/

Here's is my code and the strange behavior:

Codepen

data = [
    {
        "x": [
            "2020-01-08 00:00:29",
            "2020-01-08 00:00:40",
            "2020-01-08 00:00:55",
            "2020-01-08 00:01:38",
            "2020-01-08 00:01:52",
            "2020-01-08 00:02:17",
            "2020-01-08 00:02:25",
            "2020-01-08 00:02:41",
            "2020-01-08 00:03:12",
            "2020-01-08 00:03:28",
            "2020-01-08 00:03:59",
            "2020-01-08 00:04:07",
            "2020-01-08 00:04:49",
            "2020-01-08 00:05:06",
            "2020-01-08 00:05:23",
            "2020-01-08 00:06:14",
            "2020-01-08 00:07:53",
            "2020-01-08 00:08:04",
            "2020-01-08 00:09:42",
            "2020-01-08 00:10:04",
            "2020-01-08 00:10:25",
            "2020-01-08 00:11:24"
        ],
        "y": [
            -2,
            1,
            -1,
            -4,
            -2,
            0,
            -2,
            0,
            -1,
            1,
            3,
            1,
            -2,
            0,
            -2,
            -7,
            -7,
            -9,
            -13,
            -16,
            -14,
            -10
        ],
        "mode": "lines",
        "name": "Q1",
        "xaxis": "xaxis"
    },
    {
        "x": [
            "2020-01-08 00:12:42",
            "2020-01-08 00:13:43",
            "2020-01-08 00:14:03",
            "2020-01-08 00:14:37",
            "2020-01-08 00:14:48",
            "2020-01-08 00:16:15",
            "2020-01-08 00:16:31",
            "2020-01-08 00:16:59",
            "2020-01-08 00:17:23",
            "2020-01-08 00:17:55",
            "2020-01-08 00:18:34",
            "2020-01-08 00:19:07",
            "2020-01-08 00:19:38",
            "2020-01-08 00:20:11",
            "2020-01-08 00:21:03",
            "2020-01-08 00:21:23",
            "2020-01-08 00:21:59",
            "2020-01-08 00:22:33",
            "2020-01-08 00:23:43"
        ],
        "y": [
            -8,
            -6,
            -8,
            -10,
            -8,
            -5,
            -8,
            -6,
            -8,
            -6,
            -8,
            -6,
            -4,
            -2,
            -4,
            -6,
            -4,
            -5,
            -4
        ],

        "mode": "lines",
        "name": "Q2",
        "xaxis": "xaxis2"
    },
    {
        "x": [
            "2020-01-08 00:24:41",
            "2020-01-08 00:24:58",
            "2020-01-08 00:25:21",
            "2020-01-08 00:25:46",
            "2020-01-08 00:26:12",
            "2020-01-08 00:26:32",
            "2020-01-08 00:27:19",
            "2020-01-08 00:28:01",
            "2020-01-08 00:28:34",
            "2020-01-08 00:29:08",
            "2020-01-08 00:30:34",
            "2020-01-08 00:31:05",
            "2020-01-08 00:32:02",
            "2020-01-08 00:32:21",
            "2020-01-08 00:32:55",
            "2020-01-08 00:33:42",
            "2020-01-08 00:34:09",
            "2020-01-08 00:34:22",
            "2020-01-08 00:34:58",
            "2020-01-08 00:35:09"
        ],
        "y": [
            -2,
            -4,
            -6,
            -9,
            -12,
            -10,
            -13,
            -10,
            -7,
            -5,
            -5,
            -3,
            -4,
            -6,
            -9,
            -7,
            -9,
            -7,
            -5,
            -7
        ],
        "line": {
            "shape": "linear"
        },
        "mode": "lines",
        "name": "Q3",
        "xaxis": "xaxis3"
    },
    {
        "x": [
            "2020-01-08 00:36:46",
            "2020-01-08 00:37:44",
            "2020-01-08 00:38:23",
            "2020-01-08 00:39:02",
            "2020-01-08 00:40:29",
            "2020-01-08 00:40:59",
            "2020-01-08 00:41:07",
            "2020-01-08 00:41:28",
            "2020-01-08 00:42:00",
            "2020-01-08 00:42:13",
            "2020-01-08 00:42:50",
            "2020-01-08 00:43:29",
            "2020-01-08 00:43:59",
            "2020-01-08 00:44:23",
            "2020-01-08 00:44:53",
            "2020-01-08 00:44:59",
            "2020-01-08 00:45:29",
            "2020-01-08 00:46:14",
            "2020-01-08 00:46:55",
            "2020-01-08 00:47:49",
            "2020-01-08 00:47:57"
        ],
        "y": [
            -9,
            -11,
            -5,
            -8,
            -11,
            -13,
            -10,
            -12,
            -14,
            -12,
            -14,
            -12,
            -10,
            -11,
            -11,
            -10,
            -7,
            -4,
            -1,
            1,
            -1
        ],
        "line": {
            "shape": "linear"
        },
        "mode": "lines",
        "name": "Q4",
        "xaxis": "xaxis4"
    }
]

layout = {
    "title": {
        "text": "Game data"
    },
    "xaxis": {
        "title": {
            "text": "q1"
        },
        "tickvals": [
            "2020-01-08 00:00:00",
            "2020-01-08 00:02:00",
            "2020-01-08 00:04:00",
            "2020-01-08 00:06:00",
            "2020-01-08 00:08:00",
            "2020-01-08 00:12:00"
        ],
        "tickformat": "%M:%S",
        "type": "date",
    },
    "xaxis2": {
        "title": {
            "text": "q2"
        },
        "tickvals": [
            "2020-01-08 00:12:00",
            "2020-01-08 00:14:00",
            "2020-01-08 00:16:00",
            "2020-01-08 00:18:00",
            "2020-01-08 00:20:00",
            "2020-01-08 00:22:00",
            "2020-01-08 00:24:00"
        ],
        "tickformat": "%M:%S",
        "type": "date",
    },
    "xaxis3": {
        "title": {
            "text": "q3"
        },
        "tickvals": [
            "2020-01-08 00:24:00",
            "2020-01-08 00:26:00",
            "2020-01-08 00:28:00",
            "2020-01-08 00:30:00",
            "2020-01-08 00:32:00",
            "2020-01-08 00:34:00",
            "2020-01-08 00:36:00"
        ],
        "tickformat": "%M:%S",
        "type": "date",
    },
    "xaxis4": {
        "title": {
            "text": "q4"
        },
        "tickvals": [
            "2020-01-08 00:36:00",
            "2020-01-08 00:38:00",
            "2020-01-08 00:40:00",
            "2020-01-08 00:42:00",
            "2020-01-08 00:44:00",
            "2020-01-08 00:46:00",
            "2020-01-08 00:48:00"
        ],
        "tickformat": "%M:%S",
        "type": "date",
    },
    "yaxis": {
        "type": "linear",
        "autorange": true
    }
}

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

I don't think a multiple x-axis is quite what you are looking for as those typically mean you have another x-axis on a different scale opposite of the main x-axis, but in your desired output you only have one x-axis.

Instead, you could use a multi-categorical x-axis, but then I don't think you can specify the x-axis as "type": "date" because one category will be a date, but the other category will be a string like "Q1".

Therefore, I think the best way to represent your multicategorical x-axis would be to simply add the strings "Q1", ... "Q4" as text annotations, and place them below the date ticks by positioning them using paper coordinates.

Then using Plotly shapes, you can draw lines around each of the quarter time tickmarks to make your xaxis appear to have a multicategory.

You can view the codepen here.

category_annotations = [];
text_annotations = []
quarter_names = ["Q1","Q2","Q3","Q4"]
quarter_positions = [0.125,0.375,0.625,0.875]
quarter_times = [
  "2020-01-08 00:00:00",
  "2020-01-08 00:12:00",
  "2020-01-08 00:24:00",
  "2020-01-08 00:36:00",
  "2020-01-08 00:48:00"
]

for (let i = 0; i < quarter_times.length; i++) {
  category_annotations.push(
    {
        type: 'line',
        x0: quarter_times[i],
        x1: quarter_times[i],
        y0: -0.20,
        y1: 0.05,
        xref: 'x',
        yref: 'paper',
        line: {
          color: 'black',
          width: 1
        }
    }
  )
}

category_annotations.push(
  {
        type: 'line',
        x0: 0,
        x1: 1,
        y0: 0.05,
        y1: 0.05,
        xref: 'paper',
        yref: 'paper',
        line: {
          color: 'black',
          width: 1
        }
    }
)

for (let i = 0; i < quarter_names.length; i++) {
  text_annotations.push(
    {
      x: quarter_positions[i],
      y: -0.15,
      xref: 'paper',
      yref: 'paper',
      text: quarter_names[i],
      showarrow: false,
    }
  )
}

data = [
    {
        "x": [
            "2020-01-08 00:00:29",
            "2020-01-08 00:00:40",
            "2020-01-08 00:00:55",
            "2020-01-08 00:01:38",
            "2020-01-08 00:01:52",
            "2020-01-08 00:02:17",
            "2020-01-08 00:02:25",
            "2020-01-08 00:02:41",
            "2020-01-08 00:03:12",
            "2020-01-08 00:03:28",
            "2020-01-08 00:03:59",
            "2020-01-08 00:04:07",
            "2020-01-08 00:04:49",
            "2020-01-08 00:05:06",
            "2020-01-08 00:05:23",
            "2020-01-08 00:06:14",
            "2020-01-08 00:07:53",
            "2020-01-08 00:08:04",
            "2020-01-08 00:09:42",
            "2020-01-08 00:10:04",
            "2020-01-08 00:10:25",
            "2020-01-08 00:11:24"
        ],
        "y": [
            -2,
            1,
            -1,
            -4,
            -2,
            0,
            -2,
            0,
            -1,
            1,
            3,
            1,
            -2,
            0,
            -2,
            -7,
            -7,
            -9,
            -13,
            -16,
            -14,
            -10
        ],
        "mode": "lines",
        "name": "Q1",
    },
    {
        "x": [
            "2020-01-08 00:12:42",
            "2020-01-08 00:13:43",
            "2020-01-08 00:14:03",
            "2020-01-08 00:14:37",
            "2020-01-08 00:14:48",
            "2020-01-08 00:16:15",
            "2020-01-08 00:16:31",
            "2020-01-08 00:16:59",
            "2020-01-08 00:17:23",
            "2020-01-08 00:17:55",
            "2020-01-08 00:18:34",
            "2020-01-08 00:19:07",
            "2020-01-08 00:19:38",
            "2020-01-08 00:20:11",
            "2020-01-08 00:21:03",
            "2020-01-08 00:21:23",
            "2020-01-08 00:21:59",
            "2020-01-08 00:22:33",
            "2020-01-08 00:23:43"
        ],
        "y": [
            -8,
            -6,
            -8,
            -10,
            -8,
            -5,
            -8,
            -6,
            -8,
            -6,
            -8,
            -6,
            -4,
            -2,
            -4,
            -6,
            -4,
            -5,
            -4
        ],

        "mode": "lines",
        "name": "Q2",
    },
    {
        "x": [
            "2020-01-08 00:24:41",
            "2020-01-08 00:24:58",
            "2020-01-08 00:25:21",
            "2020-01-08 00:25:46",
            "2020-01-08 00:26:12",
            "2020-01-08 00:26:32",
            "2020-01-08 00:27:19",
            "2020-01-08 00:28:01",
            "2020-01-08 00:28:34",
            "2020-01-08 00:29:08",
            "2020-01-08 00:30:34",
            "2020-01-08 00:31:05",
            "2020-01-08 00:32:02",
            "2020-01-08 00:32:21",
            "2020-01-08 00:32:55",
            "2020-01-08 00:33:42",
            "2020-01-08 00:34:09",
            "2020-01-08 00:34:22",
            "2020-01-08 00:34:58",
            "2020-01-08 00:35:09"
        ],
        "y": [
            -2,
            -4,
            -6,
            -9,
            -12,
            -10,
            -13,
            -10,
            -7,
            -5,
            -5,
            -3,
            -4,
            -6,
            -9,
            -7,
            -9,
            -7,
            -5,
            -7
        ],
        "line": {
            "shape": "linear"
        },
        "mode": "lines",
        "name": "Q3",
    },
    {
        "x": [
            "2020-01-08 00:36:46",
            "2020-01-08 00:37:44",
            "2020-01-08 00:38:23",
            "2020-01-08 00:39:02",
            "2020-01-08 00:40:29",
            "2020-01-08 00:40:59",
            "2020-01-08 00:41:07",
            "2020-01-08 00:41:28",
            "2020-01-08 00:42:00",
            "2020-01-08 00:42:13",
            "2020-01-08 00:42:50",
            "2020-01-08 00:43:29",
            "2020-01-08 00:43:59",
            "2020-01-08 00:44:23",
            "2020-01-08 00:44:53",
            "2020-01-08 00:44:59",
            "2020-01-08 00:45:29",
            "2020-01-08 00:46:14",
            "2020-01-08 00:46:55",
            "2020-01-08 00:47:49",
            "2020-01-08 00:47:57"
        ],
        "y": [
            -9,
            -11,
            -5,
            -8,
            -11,
            -13,
            -10,
            -12,
            -14,
            -12,
            -14,
            -12,
            -10,
            -11,
            -11,
            -10,
            -7,
            -4,
            -1,
            1,
            -1
        ],
        "line": {
            "shape": "linear"
        },
        "mode": "lines",
        "name": "Q4",
    }
];

layout = {
    "title": {
        "text": "Game data"
    },
    "xaxis": {
      "tickformat": "%M:%S",
      "type": "date",
      "tickvals": [
            "2020-01-08 00:00:00",
            "2020-01-08 00:02:00",
            "2020-01-08 00:04:00",
            "2020-01-08 00:06:00",
            "2020-01-08 00:08:00",
            "2020-01-08 00:10:00",
            "2020-01-08 00:12:00",
            "2020-01-08 00:14:00",
            "2020-01-08 00:16:00",
            "2020-01-08 00:18:00",
            "2020-01-08 00:20:00",
            "2020-01-08 00:22:00",
            "2020-01-08 00:24:00",
            "2020-01-08 00:26:00",
            "2020-01-08 00:28:00",
            "2020-01-08 00:30:00",
            "2020-01-08 00:32:00",
            "2020-01-08 00:34:00",
            "2020-01-08 00:36:00",
            "2020-01-08 00:38:00",
            "2020-01-08 00:40:00",
            "2020-01-08 00:42:00",
            "2020-01-08 00:44:00",
            "2020-01-08 00:46:00",
            "2020-01-08 00:48:00"
      ],
    },
    "yaxis": {
        "tickmode": "linear",
        "tick0": -1,
        "dtick": 1.00,
        "zeroline": false
    },
    "annotations": text_annotations,
    "shapes": category_annotations
}

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

enter image description here

Related