mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-02 06:22:25 +00:00
first commit
This commit is contained in:
3
.venv/Lib/site-packages/pydeck/data_utils/__init__.py
Normal file
3
.venv/Lib/site-packages/pydeck/data_utils/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .viewport_helpers import compute_view # noqa
|
||||
from .type_checking import has_geo_interface, is_pandas_df, records_from_geo_interface # noqa
|
||||
from .color_scales import assign_random_colors # noqa
|
61
.venv/Lib/site-packages/pydeck/data_utils/binary_transfer.py
Normal file
61
.venv/Lib/site-packages/pydeck/data_utils/binary_transfer.py
Normal file
@ -0,0 +1,61 @@
|
||||
from collections import defaultdict
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
# Grafted from
|
||||
# https://github.com/maartenbreddels/ipyvolume/blob/d13828dfd8b57739004d5daf7a1d93ad0839ed0f/ipyvolume/serialize.py#L219
|
||||
def array_to_binary(ar, obj=None, force_contiguous=True):
|
||||
if ar is None:
|
||||
return None
|
||||
if ar.dtype.kind not in ["u", "i", "f"]: # ints and floats
|
||||
raise ValueError("unsupported dtype: %s" % (ar.dtype))
|
||||
# WebGL does not support float64, case it here
|
||||
if ar.dtype == np.float64:
|
||||
ar = ar.astype(np.float32)
|
||||
# JS does not support int64
|
||||
if ar.dtype == np.int64:
|
||||
ar = ar.astype(np.int32)
|
||||
# make sure it's contiguous
|
||||
if force_contiguous and not ar.flags["C_CONTIGUOUS"]:
|
||||
ar = np.ascontiguousarray(ar)
|
||||
return {
|
||||
# binary data representation of a numpy matrix
|
||||
"value": memoryview(ar),
|
||||
# dtype convertible to a typed array
|
||||
"dtype": str(ar.dtype),
|
||||
# height of np matrix
|
||||
"length": ar.shape[0],
|
||||
# width of np matrix
|
||||
"size": 1 if len(ar.shape) == 1 else ar.shape[1],
|
||||
}
|
||||
|
||||
|
||||
def serialize_columns(data_set_cols, obj=None):
|
||||
if data_set_cols is None:
|
||||
return None
|
||||
layers = defaultdict(dict)
|
||||
# Number of records in data set
|
||||
length = {}
|
||||
for col in data_set_cols:
|
||||
accessor_attribute = array_to_binary(col["np_data"])
|
||||
if length.get(col["layer_id"]):
|
||||
length[col["layer_id"]] = max(length[col["layer_id"]], accessor_attribute["length"])
|
||||
else:
|
||||
length[col["layer_id"]] = accessor_attribute["length"]
|
||||
# attributes is deck.gl's expected argument name for
|
||||
# binary data transfer
|
||||
if not layers[col["layer_id"]].get("attributes"):
|
||||
layers[col["layer_id"]]["attributes"] = {}
|
||||
# Add new accessor
|
||||
layers[col["layer_id"]]["attributes"][col["accessor"]] = {
|
||||
"value": accessor_attribute["value"],
|
||||
"dtype": accessor_attribute["dtype"],
|
||||
"size": accessor_attribute["size"],
|
||||
}
|
||||
for layer_key, _ in layers.items():
|
||||
layers[layer_key]["length"] = length[layer_key]
|
||||
return layers
|
||||
|
||||
|
||||
data_buffer_serialization = dict(to_json=serialize_columns, from_json=None)
|
34
.venv/Lib/site-packages/pydeck/data_utils/color_scales.py
Normal file
34
.venv/Lib/site-packages/pydeck/data_utils/color_scales.py
Normal file
@ -0,0 +1,34 @@
|
||||
from collections import OrderedDict
|
||||
import random
|
||||
|
||||
|
||||
def get_random_rgb():
|
||||
"""Generate a random RGB value
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of float
|
||||
Random RGB array
|
||||
"""
|
||||
return [round(random.random() * 255) for _ in range(0, 3)]
|
||||
|
||||
|
||||
def assign_random_colors(data_vector):
|
||||
"""Produces lookup table keyed by each class of data, with value as an RGB array
|
||||
|
||||
Parameters
|
||||
---------
|
||||
data_vector : list
|
||||
Vector of data classes to be categorized, passed from the data itself
|
||||
|
||||
Returns
|
||||
-------
|
||||
collections.OrderedDict
|
||||
Dictionary of random RGBA value per class, keyed on class
|
||||
"""
|
||||
deduped_classes = list(set(data_vector))
|
||||
classes = sorted([str(x) for x in deduped_classes])
|
||||
colors = []
|
||||
for _ in classes:
|
||||
colors.append(get_random_rgb())
|
||||
return OrderedDict([item for item in zip(classes, colors)])
|
24
.venv/Lib/site-packages/pydeck/data_utils/type_checking.py
Normal file
24
.venv/Lib/site-packages/pydeck/data_utils/type_checking.py
Normal file
@ -0,0 +1,24 @@
|
||||
def is_pandas_df(obj):
|
||||
"""Check if an object is a Pandas DataFrame
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Returns True if object is a Pandas DataFrame and False otherwise
|
||||
"""
|
||||
return obj.__class__.__module__ == "pandas.core.frame" and obj.to_records and obj.to_dict
|
||||
|
||||
|
||||
def has_geo_interface(obj):
|
||||
return hasattr(obj, "__geo_interface__")
|
||||
|
||||
|
||||
def records_from_geo_interface(data):
|
||||
"""Un-nest data from object implementing __geo_interface__ standard"""
|
||||
flattened_records = []
|
||||
for d in data.__geo_interface__.get("features"):
|
||||
record = d.get("properties", {})
|
||||
geom = d.get("geometry", {})
|
||||
record["geometry"] = geom
|
||||
flattened_records.append(record)
|
||||
return flattened_records
|
176
.venv/Lib/site-packages/pydeck/data_utils/viewport_helpers.py
Normal file
176
.venv/Lib/site-packages/pydeck/data_utils/viewport_helpers.py
Normal file
@ -0,0 +1,176 @@
|
||||
"""
|
||||
Functions that make it easier to provide a default centering
|
||||
for a view state
|
||||
"""
|
||||
import math
|
||||
from ..bindings.view_state import ViewState
|
||||
from .type_checking import is_pandas_df
|
||||
|
||||
|
||||
def _squared_diff(x, x0):
|
||||
return (x0 - x) * (x0 - x)
|
||||
|
||||
|
||||
def euclidean(y, y1):
|
||||
"""Euclidean distance in n-dimensions
|
||||
|
||||
Parameters
|
||||
----------
|
||||
y : tuple of float
|
||||
A point in n-dimensions
|
||||
y1 : tuple of float
|
||||
A point in n-dimensions
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> EPSILON = 0.001
|
||||
>>> euclidean((3, 6, 5), (7, -5, 1)) - 12.369 < EPSILON
|
||||
True
|
||||
"""
|
||||
if not len(y) == len(y1):
|
||||
raise Exception("Input coordinates must be of the same length")
|
||||
return math.sqrt(sum([_squared_diff(x, x0) for x, x0 in zip(y, y1)]))
|
||||
|
||||
|
||||
def geometric_mean(points):
|
||||
"""Gets centroid in a series of points
|
||||
|
||||
Parameters
|
||||
----------
|
||||
points : list of list of float
|
||||
List of (x, y) coordinates
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple
|
||||
The centroid of a list of points
|
||||
"""
|
||||
avg_x = sum([float(p[0]) for p in points]) / len(points)
|
||||
avg_y = sum([float(p[1]) for p in points]) / len(points)
|
||||
return (avg_x, avg_y)
|
||||
|
||||
|
||||
def get_bbox(points):
|
||||
"""Get the bounding box around the data,
|
||||
|
||||
Parameters
|
||||
----------
|
||||
points : list of list of float
|
||||
List of (x, y) coordinates
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Dictionary containing the top left and bottom right points of a bounding box
|
||||
"""
|
||||
xs = [p[0] for p in points]
|
||||
ys = [p[1] for p in points]
|
||||
max_x = max(xs)
|
||||
max_y = max(ys)
|
||||
min_x = min(xs)
|
||||
min_y = min(ys)
|
||||
return ((min_x, max_y), (max_x, min_y))
|
||||
|
||||
|
||||
def k_nearest_neighbors(points, center, k):
|
||||
"""Gets the k furthest points from the center
|
||||
|
||||
Parameters
|
||||
----------
|
||||
points : list of list of float
|
||||
List of (x, y) coordinates
|
||||
center : list of list of float
|
||||
Center point
|
||||
k : int
|
||||
Number of points
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
Index of the k furthest points
|
||||
|
||||
Todo
|
||||
---
|
||||
Currently implemently naively, needs to be more efficient
|
||||
"""
|
||||
pts_with_distance = [(pt, euclidean(pt, center)) for pt in points]
|
||||
sorted_pts = sorted(pts_with_distance, key=lambda x: x[1])
|
||||
return [x[0] for x in sorted_pts][: int(k)]
|
||||
|
||||
|
||||
def get_n_pct(points, proportion=1):
|
||||
"""Computes the bounding box of the maximum zoom for the specified list of points
|
||||
|
||||
Parameters
|
||||
----------
|
||||
points : list of list of float
|
||||
List of (x, y) coordinates
|
||||
proportion : float, default 1
|
||||
Value between 0 and 1 representing the minimum proportion of data to be captured
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
k nearest data points
|
||||
"""
|
||||
if proportion == 1:
|
||||
return points
|
||||
# Compute the medioid of the data
|
||||
centroid = geometric_mean(points)
|
||||
# Retain the closest n*proportion points
|
||||
n_to_keep = math.floor(proportion * len(points))
|
||||
return k_nearest_neighbors(points, centroid, n_to_keep)
|
||||
|
||||
|
||||
def bbox_to_zoom_level(bbox):
|
||||
"""Computes the zoom level of a lat/lng bounding box
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bbox : list of list of float
|
||||
Northwest and southeast corners of a bounding box, given as two points in a list
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
Zoom level of map in a WGS84 Mercator projection (e.g., like that of Google Maps)
|
||||
"""
|
||||
lat_diff = max(bbox[0][0], bbox[1][0]) - min(bbox[0][0], bbox[1][0])
|
||||
lng_diff = max(bbox[0][1], bbox[1][1]) - min(bbox[0][1], bbox[1][1])
|
||||
|
||||
max_diff = max(lng_diff, lat_diff)
|
||||
zoom_level = None
|
||||
if max_diff < (360.0 / math.pow(2, 20)):
|
||||
zoom_level = 21
|
||||
else:
|
||||
zoom_level = int(-1 * ((math.log(max_diff) / math.log(2.0)) - (math.log(360.0) / math.log(2))))
|
||||
if zoom_level < 1:
|
||||
zoom_level = 1
|
||||
return zoom_level
|
||||
|
||||
|
||||
def compute_view(points, view_proportion=1, view_type=ViewState):
|
||||
"""Automatically computes a zoom level for the points passed in.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
points : list of list of float or pandas.DataFrame
|
||||
A list of points
|
||||
view_propotion : float, default 1
|
||||
Proportion of the data that is meaningful to plot
|
||||
view_type : class constructor for pydeck.ViewState, default :class:`pydeck.bindings.view_state.ViewState`
|
||||
Class constructor for a viewport. In the current version of pydeck,
|
||||
users most likely do not have to modify this attribute.
|
||||
|
||||
Returns
|
||||
-------
|
||||
pydeck.Viewport
|
||||
Viewport fitted to the data
|
||||
"""
|
||||
if is_pandas_df(points):
|
||||
points = points.to_records(index=False)
|
||||
bbox = get_bbox(get_n_pct(points, view_proportion))
|
||||
zoom = bbox_to_zoom_level(bbox)
|
||||
center = geometric_mean(points)
|
||||
instance = view_type(latitude=center[1], longitude=center[0], zoom=zoom)
|
||||
return instance
|
Reference in New Issue
Block a user