datopian / datahub

🌀 Rapidly build rich data portals using a modern frontend framework
https://datahub.io/opensource
MIT License
2.19k stars 325 forks source link

Line Chart improvements #577

Open rufuspollock opened 3 years ago

rufuspollock commented 3 years ago

We already have a LineChart component.

LineChart supports

gradedSystem commented 2 weeks ago

Research Summary: Enhancements for LineChart Component

Current Capabilities:

Outstanding Features to Implement:

  1. Default to Temporal Type for X Axis:

    • Action Required: Ensure that the xAxisType defaults to 'temporal' when not explicitly provided.
  2. Use Table Schema Information if Present:

    • Action Required: Implement functionality to utilize schema information from the data prop to configure the chart appropriately.
  3. Auto Display All Series:

    • Action Required: Modify the component to handle multiple data series using Vega-Lite’s Fold Transform. This will involve:
      • Using the fold transform to convert multiple columns into a single column for visualization.
      • Automatically generating legends based on the series present in the data.

Reference for Auto Displaying All Series:

Suggested Changes to Component:

1. Default X Axis Type:

Key Changes:

Code Example:

export function LineChart({
  data,
  title = '',
  xAxis,
  xAxisType = 'temporal', // Default to 'temporal'
  xAxisTimeUnit = 'year',
  yAxis,
  yAxisType = 'quantitative',
}: LineChartProps) {
  // Component logic...
}

2. Possible configuration of LineChart component of spec object for a Vega-Lite:

Key Changes:

Transform:

Color Encoding:

const spec = {
  $schema: 'https://vega.github.io/schema/vega-lite/v5.json',
  title,
  width: 'container',
  height: 300,
  transform: values && values[0] && Object.keys(values[0]).length > 2 
    ? [{ fold: Object.keys(values[0]).filter(key => key !== xAxis && key !== yAxis) }] 
    : [], // Adding transform to handle multiple columns
  mark: {
    type: 'line',
    color: 'black',
    strokeWidth: 1,
    tooltip: true,
  },
  data: specData,
  selection: {
    grid: {
      type: 'interval',
      bind: 'scales',
    },
  },
  encoding: {
    x: {
      field: xAxis,
      timeUnit: xAxisTimeUnit,
      type: xAxisType,
    },
    y: {
      field: yAxis,
      type: yAxisType,
    },
    color: values && values[0] && Object.keys(values[0]).length > 2
      ? { field: 'key', type: 'nominal' } // Added color encoding for multiple series
      : undefined,
  },
} as any;

cc @olayway @rufuspollock

olayway commented 2 weeks ago

@gradedSystem

Original: The xAxisType was not explicitly set to a default value, potentially requiring users to specify it every time.

But it already defaults to temporal:

https://github.com/datopian/datahub/blob/1baebc3f3ca6fd2cba3b6b561b36c0a6a9ee5aff/packages/components/src/components/LineChart.tsx#L21-L26

olayway commented 2 weeks ago

Action Required: Implement functionality to utilize schema information from the data prop to configure the chart appropriately.

data prop serves different purpose in our portaljs components. Also, are you sure you understand this item provided in the description?

olayway commented 2 weeks ago
  1. Possible configuration of LineChart component of spec object for a Vega-Lite:

I think this is related to the 3rd point, not the second?

gradedSystem commented 2 weeks ago
  1. Possible configuration of LineChart component of spec object for a Vega-Lite:

I think this is related to the 3rd point, not the second?

Yeah That's Right

olayway commented 2 weeks ago

@gradedSystem Can you please replace the full spec example with only the parts that you're changing? And use ... for the parts that you're not changing/adding.

olayway commented 2 weeks ago

Using the fold transform to convert multiple columns into a single column for visualization.

Converting columns into a single column? What does it mean?

gradedSystem commented 2 weeks ago

Can you please replace the full spec example with only the parts that you're changing? And use ... for the parts that you're not changing/adding.

Here are the specs:

const spec = {
  ...,
  transform: values && values[0] && Object.keys(values[0]).length > 2 
    ? [{ fold: Object.keys(values[0]).filter(key => key !== xAxis && key !== yAxis) }] 
    : [], // Adding transform to handle multiple columns
  ...,
  encoding: {
    ...,
    color: values && values[0] && Object.keys(values[0]).length > 2
      ? { field: 'key', type: 'nominal' } // Added color encoding for multiple series
      : undefined,
  },
  ...
} as any;
gradedSystem commented 2 weeks ago

Using the fold transform to convert multiple columns into a single column for visualization.

Converting columns into a single column? What does it mean?

Folding turns many columns into two: one for labels, one for values, making it easier to visualize

gradedSystem commented 2 weeks ago

Loading from URL ✅

Yes, it supports loading from a URL. See the implementation here.

Default Temporal Type for X-Axis ✅

The X-axis defaults to the temporal type. You can see it here here

Uses Table Schema Info✅

It uses the table schema if I understood it correctly here and it also says that the format: { type: 'csv' } which matches the format here Vega supported format or also it could be the supported data types quantitative | temporal which Vega supports :

From Vega Official Website: If a field is specified, the channel definition must describe the encoded data’s type based on their level of measurement. The supported data types are: "quantitative", "temporal", "ordinal", "nominal", and "geojson".

Auto Display All Series â­•

Here I believe it mentions the legends to display all the series which uses this structure:

"encoding": {
    "x": {"field": "date", "type": "temporal"},
    "y": {"field": "value", "type": "quantitative"},
    "color": {"field": "key", "type": "nominal"}
  }

which we do not currently support and it could be added to the spec like this:

const spec = {
  ...,
encoding: {
    ...,
    color: values && values[0] && Object.keys(values[0]).length > 2
      ? { field: 'key', type: 'nominal' } // Added color encoding for multiple series
      : undefined,
  },
  ...
} as any;

cc @olayway

olayway commented 1 week ago

It uses the table schema if I understood it correctly here

I don't think this is the case. Look at what props does this component take in. There is no option to pass a full spec. The spec is generated within the component here, and only uses xAxis, yAxis and a bunch of other props passed to it. But I think allowing to pass a full spec is a no-go. If sb needs to pass a full spec, then he/she should just use VegaLite or Vega for this, as LineChart is just a simple wrapper around VegaLite, with easy to use but very basic interface.

Auto Display All Series â­•

Cool, let's do this!

gradedSystem commented 2 days ago

Currently testing LineChart component locally it looks something like this:

Image Things to Note:

  1. When dealing with multiple y-values, is it necessary to specify a legend title? In the current implementation, I have used the default title key.
  2. For scenarios with multiple y-values, should a specific yAxis value be included? Currently, my approach is to fold all y-values except the xAxis. If no yAxis is provided, if provided then I make that only that value to be displayed:

    Image

@olayway

olayway commented 2 days ago

@gradedSystem

When dealing with multiple y-values, is it necessary to specify a legend title? In the current implementation, I have used the default title key.

Yes.

For scenarios with multiple y-values, should a specific yAxis value be included? Currently, my approach is to fold all y-values except the xAxis. If no yAxis is provided, if provided then I make that only that value to be displayed:

I'm sorry, I don't understand the question.

gradedSystem commented 2 days ago

@olayway What is meant here on the second question is that for example I can make it go like this:

<LineChart
data = {{url.csv}}
xAxis = "something"
yAxis // skip this one and it will pick up all the values instead of xAxis so it will show all the values
>

like here: image