Skip to content

Commit

Permalink
fix: several bugs (#125)
Browse files Browse the repository at this point in the history
* fix: match numerical and string IDs properly

* fix: ensure that the domain and histograms match

* fix: ignore `NaN`s when computing histograms

* fix: warn when data contains `NaN`s and replace them with zeros

* fix: show correct color encoding in legend

* fix: ensure the widget's x and y scale domains update properly

* fix: ensure the widget's color, opacity, and size titles update properly

* fix: ensure the widget's axes titles are updated properly

* fix: typo in docstring of `color()`

* fix: include normalization in data dimension name

* fix: allow rendering a single axis

* refactor: simplify `createNumericalBinGetter()`

* fix: typo in channel badge element's class name

* fix: rely on pre-normalized data to get bin id

* fix: connect order

* fix: adjust test to new data dimension ID

The data dimension now include the scale function as jscatter can only store two pre-scaled data dimensions for encoding

* refactor: remove length restriction to fix tests

* fix: ensure connection order dtype is `int`

* fix: connection order test and remove invalid test

* ci: drop py37. add py311+py312. bump node to 20

* docs: fix changelog

* chore: bump regl-scatterplot

* test: remove unsupported scales

* fix: x/y scale domain bug

* fix: add support for time scale and fix connected points bugs
  • Loading branch information
flekschas authored Jun 5, 2024
1 parent 702296d commit 3d55b86
Show file tree
Hide file tree
Showing 32 changed files with 914 additions and 208 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
python-version: [3.7, 3.8, 3.9, '3.10']
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand All @@ -42,7 +42,7 @@ jobs:

strategy:
matrix:
node-version: [16.x]
node-version: [20.x]

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "18.x"
node-version: "20.x"
- run: |
cd js
npm ci
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,24 @@ Additionally, the following helper methods are removed as they are unnecessary.

**Other Changes**:

- Feat: Add basic support for x/y time scale via `Scatter(data=df, x='x', x_scale='time', y='y', y_scale='time')`
- Docs: Add API documentation for `scatter.widget`
- Docs: Add description for x/y scales
- Docs: Add description for connected scatterplots
- Fix: Match numerical and string IDs properly in `compose(match_by='XYZ')`
- Fix: Ensure that the domain and histograms match by avoiding missing categorical indices
- Fix: Ignore `NaN`s when computing histograms
- Fix: Warn when data contains `NaN`s and replace them with zeros
- Fix: Show correctly ordered color encoding in legend
- Fix: Ensure the widget's x and y scale domains are updated properly
- Fix: Ensure the widget's color, opacity, and size titles are updated properly
- Fix: Ensure the widget's axes titles are updated properly
- Fix: Include normalization in data dimension name
- Fix: Allow rendering a single axis instead of enforcing either none or both axis
- Fix: Rely on pre-normalized data to get bin ID
- Fix: Connect order
- Fix: X/Y scale domain bug
- Fix: Connected point bugs

## v0.15.1

Expand Down
4 changes: 3 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ export default defineConfig({
{ text: 'Selections', link: '/selections' },
{ text: 'Link Multiple Scatter Plots', link: '/link-multiple-plots' },
{ text: 'Axes & Legends', link: '/axes-legends' },
{ text: 'Tooltip', link: '/tooltip' }
{ text: 'Tooltip', link: '/tooltip' },
{ text: 'Scales', link: '/scales' },
{ text: 'Connected Scatterplots', link: '/connected-scatterplots' }
]
},
// {
Expand Down
4 changes: 2 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ Get or set the x and y coordinate. This is just a convenience function to animat

- `x` is either an array-like list of coordinates or a string referencing a column in `data`.
- `y` is either an array-like list of coordinates or a string referencing a column in `data`.
- `x_scale` is either a string (`linear`, `log`, `pow`), a tuple defining the value range that's map to the extent of the scatter plot, or an instance of `matplotlib.colors.LogNorm` or `matplotlib.colors.PowerNorm`.
- `y_scale` is either a string (`linear`, `log`, `pow`), a tuple defining the value range that's map to the extent of the scatter plot, or an instance of `matplotlib.colors.LogNorm` or `matplotlib.colors.PowerNorm`.
- `x_scale` is either a string (`linear`, `time`, `log`, `pow`), a tuple defining the value range that's map to the extent of the scatter plot, or an instance of `matplotlib.colors.LogNorm` or `matplotlib.colors.PowerNorm`.
- `y_scale` is either a string (`linear`, `time`, `log`, `pow`), a tuple defining the value range that's map to the extent of the scatter plot, or an instance of `matplotlib.colors.LogNorm` or `matplotlib.colors.PowerNorm`.
- `kwargs`:
- `skip_widget_update` allows to skip the dynamic widget update when `True`. This can be useful when you want to animate the transition of multiple properties at once instead of animating one after the other.

Expand Down
121 changes: 121 additions & 0 deletions docs/connected-scatterplots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Connected Scatterplot

If your data represents series of variables, it can be useful to visualize the
evolution the variables through point connections.

::: info
Connected scatterplots are most commonly used for temporal or timeseries data.
:::

## Basics

By default, points are plotted linearly along the x and y coordinate.

```py
import datetime
import jscatter
import math
import numpy as np
import pandas as pd

def get_web_timestamp(month):
return math.floor(
datetime.datetime(2023, month, 1).timestamp() * 1000
)

dates = [get_web_timestamp(month) for month in range(1, 13)]

df = DataFrame({
'date': dates * 3,
'value': np.concatenate(
(
np.random.rand(12),
np.random.rand(12) + 0.5,
np.random.rand(12) + 1
),
axis=0
),
'group': ['A'] * 12 + ['B'] * 12 + ['C'] * 12,
})

scatter = jscatter.Scatter(
data=df,
x='date',
x_scale='time',
y='value',
connect_by='group'
)
scatter.show()
```

<div class="img basics"><div /></div>

## Styling

Similar to how you can visually style and encode the point color, opacity, and
size, you can visually style and encode the color, opacity, and size of the
point connections.

Since one might often want to use the point color as the line color, the
connection color can be set to `'inherit'` in order to inherit the color
encoding.

```py{4-6}
scatter.color(by='group')
scatter.size(10)
scatter.opacity(1)
scatter.connection_color('inherit')
scatter.connection_size(5)
scatter.connection_opacity(0.5)
```

<div class="img styled"><div /></div>

In additional, it's also possible to color the point connections by line
segments to visually distinguish the start and end of connected points.

```py
scatter.color(by='date', map='coolwarm')
scatter.connection_color(by='segment', map='coolwarm')
```

<div class="img segments"><div /></div>

<style scoped>
.img {
max-width: 100%;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}

.img.basics {
width: 460px;
background-image: url(/images/connected-scatterplot-basics-light.png)
}
.img.basics div { padding-top: 56.52173913% }

:root.dark .img.basics {
background-image: url(/images/connected-scatterplot-basics-dark.png)
}

.img.styled {
width: 460px;
background-image: url(/images/connected-scatterplot-styled-light.png)
}
.img.styled div { padding-top: 56.52173913% }

:root.dark .img.styled {
background-image: url(/images/connected-scatterplot-styled-dark.png)
}

.img.segments {
width: 460px;
background-image: url(/images/connected-scatterplot-segments-light.png)
}
.img.segments div { padding-top: 56.52173913% }

:root.dark .img.segments {
background-image: url(/images/connected-scatterplot-segments-dark.png)
}
</style>
3 changes: 3 additions & 0 deletions docs/public/images/connected-scatterplot-basics-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/connected-scatterplot-basics-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/connected-scatterplot-segments-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/connected-scatterplot-segments-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/connected-scatterplot-styled-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/connected-scatterplot-styled-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/scale-linear-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/scale-linear-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/scale-log-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/scale-log-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/scale-pow-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/scale-pow-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/scale-time-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions docs/public/images/scale-time-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
119 changes: 119 additions & 0 deletions docs/scales.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Scales

In the following we'll go over all supported X/Y scale functions.

::: info
We'll add support for more scale functions in the future, so make sure to check
back from time to time.
:::

## Linear

By default, points are plotted linearly along the x and y coordinate.

```py
jscatter.plot(x=np.random.rand(500), y=np.random.rand(500))
```

<div class="img linear"><div /></div>

## Time

While technically identical to linear scaling, if you have temporal data, you
can render out axes with nice tick marks by setting the X or Y scale to `time`.

```py{8}
jscatter.plot(
x=np.random.randint(
low=1672549200000, # Jan 1, 2023 00:00:00
high=1704085200000, # Jan 1, 2024 00:00:00
size=500
),
y=np.random.rand(500),
x_scale='time',
)
```

<div class="img time"><div /></div>

::: warning
For the time scale to work, the data needs to be in the form of [timestamps given as the number of milliseconds since the beginning of the Unix epoch](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_epoch_timestamps_and_invalid_date)!
:::

## Log

If your data is following, you can plot points

```py{4}
jscatter.plot(
x=np.random.rand(500),
y=np.random.rand(500),
x_scale='log',
)
```

<div class="img log"><div /></div>

## Power

Similarly, you can also plot points according to a power scale along the x or y
axis.

```py{4}
jscatter.plot(
x=np.random.rand(500),
y=np.random.rand(500),
x_scale='pow',
)
```

<div class="img pow"><div /></div>

<style scoped>
.img {
max-width: 100%;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}

.img.linear {
width: 460px;
background-image: url(/images/scale-linear-light.png)
}
.img.linear div { padding-top: 56.52173913% }

:root.dark .img.linear {
background-image: url(/images/scale-linear-dark.png)
}

.img.time {
width: 460px;
background-image: url(/images/scale-time-light.png)
}
.img.time div { padding-top: 56.52173913% }

:root.dark .img.time {
background-image: url(/images/scale-time-dark.png)
}

.img.log {
width: 460px;
background-image: url(/images/scale-log-light.png)
}
.img.log div { padding-top: 56.52173913% }

:root.dark .img.log {
background-image: url(/images/scale-log-dark.png)
}

.img.pow {
width: 460px;
background-image: url(/images/scale-pow-light.png)
}
.img.pow div { padding-top: 56.52173913% }

:root.dark .img.pow {
background-image: url(/images/scale-pow-dark.png)
}
</style>
Loading

0 comments on commit 3d55b86

Please sign in to comment.