SCHC
Spatially constrained hierarchical clustering is a special form of constrained clustering, where the constraint is based on contiguity (common borders). The method builds up the clusters using agglomerative hierarchical clustering methods: single linkage, complete linkage, average linkage and Ward’s method (a special form of centroid linkage). Meanwhile, it also maintains the spatial contiguity when merging two clusters.
function schc(
WeightResult w,
Number k,
Array vals,
Number min_bound,
Array bound_vals,
String scale_method,
String distance_type)
Name | Type | Description |
weights | WeightsResult | The weights object WeightsResult |
k | Number | The number of clusters |
values | Array | The list of numeric vectors of selected variable |
method | String | The method of agglomerative hierarchical clustering: {“single”, “complete”, “average”,”ward”}. |
min_bound | Number | The minimum value that the sum value of bounding variable int each cluster should be greater than |
bound_vals | Array | The numeric vector of selected bounding variable |
scale_method | String | The scaling method: {'raw', 'standardize', 'demean', 'mad', 'range_standardize', 'range_adjust'} |
distance_method | String | The distance method: {"euclidean", "manhattan"} |
Type | Description |
ClusteringResult | The Clustering object: {'total_ss', 'within_ss', 'between_ss', 'ratio', 'clusters'} |
Examples
Node.js
React
const jsgeoda = require('jsgeoda');
const fs = require('fs');
// load data
const data = fs.readFileSync('./data/natregimes.geojson').buffer;
// create jsgeoda instance
const geoda = await jsgeoda.New();
// load geojson in jsgeoda
const nat = geoda.read_geojson(data);
// create a queen contiguity weights
const w = geoda.queen_weights(nat);
// get values
const hr60 = geoda.get_col(nat, "HR60");
const ue60 = geoda.get_col(nat, "UE60");
// set minimum bound
const po60 = geoda.get_col(nat, "PO60");
// apply skater
const schc = geoda.schc(w, 10, [hr60, ue60], 'ward', 17845200, po60);
import React, { Component } from "react";
import ReactDOM from "react-dom";
import DeckGL from "@deck.gl/react";
import { GeoJsonLayer } from "@deck.gl/layers";
import { StaticMap } from "react-map-gl";
import colorbrewer from "colorbrewer";
import jsgeoda from "jsgeoda";
// Set your mapbox access token here
const MAPBOX_TOKEN =
"pk.eyJ1IjoibGl4dW45MTAiLCJhIjoiY2locXMxcWFqMDAwenQ0bTFhaTZmbnRwaiJ9.VRNeNnyb96Eo-CorkJmIqg";
// The geojson data
const DATA_URL = `https://webgeoda.github.io/data/natregimes.geojson`;
class App extends Component {
constructor() {
super();
this.state = {
mapId: "",
layer: null,
viewPort: {
longitude: -100.4,
latitude: 38.74,
zoom: 2.5,
maxZoom: 20
}
};
}
// load spatial data when mount this component
loadSpatialData(geoda) {
fetch(DATA_URL)
.then((res) => res.arrayBuffer())
.then((data) => {
// load geojson in jsgeoda, an unique id (string) will be returned for further usage
const nat = geoda.read_geojson(data);
const w = geoda.queen_weights(nat);
const hr60 = geoda.get_col(nat, "HR60");
const ue60 = geoda.get_col(nat, "UE60");
const po60 = geoda.get_col(nat, "PO60");
const redcap = geoda.skater(w, 10, [hr60, ue60], 17845200, po60);
//const redcap = geoda.schc(w, 10, [hr60, ue60], 'ward', 17845200, po60);
//const redcap = geoda.redcap(w, 10, [hr60, ue60], "fullorder-wardlinkage", 17845200, po60);
//const redcap = geoda.azp_tabu(w, 20, [hr60, ue60], 10, 10, 1, [], [po60],[17845200]);
//const redcap = geoda.azp_sa(w, 20, [hr60, ue60], 0.85, 1, 1, [], [po60],[17845200]);
//const redcap = geoda.maxp_greedy(w, [hr60, ue60], 1, [po60],[17845200]);
const colors = colorbrewer["Paired"][10].map((c) =>
c
.toLowerCase()
.match(/[0-9a-f]{2}/g)
.map((x) => parseInt(x, 16))
);
// Viewport settings
const view_port = geoda.get_viewport(
nat,
window.innerHeight,
window.innerWidth
);
// Create GeoJsonLayer
const layer = new GeoJsonLayer({
id: "GeoJsonLayer",
data: DATA_URL,
filled: true,
getFillColor: (f) => this.getFillColor(f, redcap.clusters, colors),
stroked: true,
pickable: true
});
// Trigger to draw map
this.setState({
mapId: nat,
layer: layer,
viewPort: view_port
});
});
}
componentDidMount() {
// jsgeoda.New() function will create an instance from WASM
jsgeoda.New().then((geoda) => {
this.loadSpatialData(geoda);
});
}
// Determine which color for which geometry
getFillColor(f, clusters, colors) {
const i = f.properties.POLY_ID - 1;
const c = clusters[i] - 1;
return colors[c];
}
render() {
return (
<div>
<DeckGL
initialViewState={this.state.viewPort}
layers={[this.state.layer]}
controller={true}
getTooltip={({ object }) =>
object && `${object.properties.NAME}: ${object.properties.HR60}`
}
>
<StaticMap mapboxApiAccessToken={MAPBOX_TOKEN} />
</DeckGL>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
Try it yourself in the playground (jsgeoda + deck.gl):
Last modified 2yr ago