Repository navigation
ENH: Method for building Spatiotemporal Graphs #873
Description
Activity
Thanks for the redirect @martinfleis , and so sorry for the confusion..
What I was thinking, we can put(Graph.multiply)as the primitive, with build_spatiotemporal as a thin convenience wrapper on top.The reason for multiply as primitive the same compositional style as the existing intersection - so, you build two graphs independently, then combine them with one operation:
# the current pattern: st_graph = temporal_g.intersection(spatial_g)
# the new pattern: st_graph = temporal_g.multiply(spatial_g)
The math behind it:
$$W_{ST}(i,j) = W_S(i,j) \cdot W_T(i,j)$$ Using separate bandwidths
$h_S$ and$h_T$ for space and time avoids the steepness issue that arises when spatial and temporal distances are collapsed into a single Euclidean metric. (the results related to same method are in the pysal/spml#69 issue's discussion)For users who don't need complete/fine-grained control,
build_spatiotemporalautomates the process easily with:# full control: spatial_g = Graph.build_kernel(gdf, bandwidth=500, kernel="gaussian") temporal_g = Graph.build_kernel(t_dists, metric="precomputed", bandwidth=365, kernel="gaussian") st_graph = temporal_g.multiply(spatial_g) # one-liner: st_graph = Graph.build_spatiotemporal(gdf, t=t_values, spatial_bandwidth=500, temporal_bandwidth=365)
please let me know your opinion on this proposal.. I'd love to navigate and work upon building the correct, developer friendly spatiotemporal graph building api.. maybe i'm thinking of too much of a bloated api right now considering two methods. please correct me wherever i'm wrong.
I'd like to hear what others think because some folks here have way more experience with spatiotemporal analysis than I do.
I am not a big fan of
build_spatiotemporalas it implicitly reduces spatiotemporal graphs to a kernel graphs on the spatial side. I think we should avoid that and ensure that you can turn any spatial graph spatiotemporal by intersecting (or multiplying) it with time. The current way of doing that via any spatial graph, kernel builder to get temporal one and an intersection works until you want to make use of weights, as intersection reduces those to binary. The multiply option could be a step forward, though I am still not convinced that multiplication is always the best way of combining the weights.Maybe we should expand capabilities of our algebraic ops to do the actual algebra on weights rather than doing that only on index. That way you could technically write your own formula. In practice, it is just an operation on adjacency tables. Not sure, let's wait for other ideas.
edit: for a reference, here they're using the product of spatial and temporal weights https://www.sciencedirect.com/science/article/pii/S0167587799001117
As discussed in
GWLearn's issue: pysal/spml#69
To perform spatiotemporal regression (GTWR), users currently have to manually build separate spatial and temporal graphs, intersect them, and handle the weight multiplication themselves. This is very complicated.
There's a need for adding a first class method to handle spatio-temporal weight construction.$W_{ST} = W_S \times W_T$ ). This allows independent bandwidths for space and time.
We can likely use the "Product of Kernels" approach (
Few options I'd like to mention (totally open for more, for a more cleaner API)
Graph.build_spatiotemporalA dedicated constructor that takes both spatial and temporal data.
Graph.multiply)A more general method that allows scaling weights of one graph by those of another (assuming matching edges/indices).
Moving this to libpysal ensures:
A clean, "PySAL-ic" API that favors composition.
Cross-package compatibility (e.g., gwlearn and esda can both use the same ST graph logic). Libraries like
gwlearnoresdacan focus on modeling/statistics rather than.. having an unnecessary graph intersection logics