Разбиение области на ячейки и присвоение меток с началом и концом
Имеется следующий датафрейм с началом и концом поездок:
Необходимо написать класс, который будет разбивать заданную зону на сектора:
А также будет создавать два новых признака, где началась поездка и где закончилась. Алгоритм прост, вычисляем границы секторов, на их основе получаем их центры. Но вот с присвоением меток зон, возникла проблема, которую не могу решить. И как можно векторизовать вычисления, для ускорения расчетов?
import pandas as pd
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin
from ipyleaflet import Map, Marker, basemaps, basemap_to_tiles, Polygon, CircleMarker, LayerGroup
class MapGridTransformer(BaseEstimator, TransformerMixin):
def __init__(self,loc, col, row):
self.location_mh = loc
self.col = col
self.row = row
def create_box_(self, loc):
polygon_loc = [
[(loc[0] - self.walls[0] / 2), loc[1] - self.walls[1] / 2],
[loc[0] + self.walls[0] / 2, loc[1] - self.walls[1] / 2],
[loc[0] + self.walls[0] / 2, loc[1] + self.walls[1] / 2],
[loc[0] - self.walls[0] / 2, loc[1] + self.walls[1] / 2]
]
return polygon_loc
def show_map(self):
self.walls, self.circles_loc = self._fit()
_map = Map(center=((self.location_mh[:, 0]).mean(), (self.location_mh[:, 1]).mean()),
zoom=11, basemap=basemaps.Esri.NatGeoWorldMap)
circles = [(CircleMarker(location=(lat, long), fill_color='red',
fill_opacity=1, radius=5, stroke=False)) for lat, long in self.circles_loc]
polygons = [(Polygon(locations=self.create_box_(loc), fill_opacity=0)) for loc in self.circles_loc]
circles_layer = LayerGroup(layers=circles)
polygon_layer = LayerGroup(layers=polygons)
_map.add_layer(circles_layer)
_map.add_layer(polygon_layer)
return _map
def _fit(self):
self.walls = [(self.location_mh[:, 0].max() - self.location_mh[:, 0].min()) / self.col, \
(self.location_mh[:, 1].max() - self.location_mh[:, 1].min()) / self.row]
self.circles_loc = np.array([(((self.location_mh[:, 0].min() + i * (self.walls[0] / 2))), \
(self.location_mh[:, 1].min() + j * (self.walls[1] / 2)) ) \
for i in range(1, (self.col) * 2, 2) for j in range(1, (self.row) * 2, 2)])
return self.walls, self.circles_loc
def _transform(self, X):
_, self.circles_loc = self._fit()
circles_dict = {ind:loc for ind, loc in enumerate(self.circles_loc)}
X['start_circle'] = ((X['pickup_latitude'] > self.circles_loc[:, 0].min()) & (X['pickup_latitude'] < self.circles_loc[:, 0].max()) & \
(X['pickup_longitude'] > self.circles_loc[:, 1].min()) & (X['pickup_longitude'] < self.circles_loc[:, 1].max())).map({True: 0, False : -1})
X['end_circle'] = ((X['dropoff_latitude'] > self.circles_loc[:, 0].min()) & (X['dropoff_latitude'] < self.circles_loc[:, 0].max()) & \
(X['dropoff_longitude'] > self.circles_loc[:, 1].min()) & (X['dropoff_longitude'] < self.circles_loc[:, 1].max())).map({True: 0, False : -1})
for index in X.index:
row = X.loc[index, ['pickup_latitude', 'pickup_longitude', 'dropoff_latitude','dropoff_longitude']]
for key, value in circles_dict.items():
box = np.array(self.create_box_(value))
if ((box[:, 0].min() < row[0]) & (row[0] < box[:, 0].max())) & \
((box[:, 1].min() < row[1]) & (row[1] < box[:, 1].max())):
X.loc[index, 'start_circle'] = key
if ((box[:, 0].min() < row[2]) & (row[2] < box[:, 0].max())) & \
((box[:, 1].min() < row[3]) & (row[3] < box[:, 1].max())):
X.loc[index, 'end_circle'] = key
return X
mh = np.array([
[40.703314, -74.018608],
[40.703314, -73.934241],
[40.791438, -73.934241],
[40.791438, -74.018608]
])
a = MapGridTransformer(mh, 5, 4)
test_cut = a._transform(test1)
Тестовый датафрейм:
test1 = pd.DataFrame({'pickup_latitude': {824746: 40.7422103881836,
645821: 40.74176025390625,
691846: 40.75072479248047,
1147931: 40.77690124511719,
183569: 40.76437377929688},
'pickup_longitude': {824746: -73.99698638916014,
645821: -73.98992919921875,
691846: -73.97236633300781,
1147931: -73.98236083984375,
183569: -73.9737777709961},
'dropoff_latitude': {824746: 40.71065902709961,
645821: 40.783599853515625,
691846: 40.76838684082031,
1147931: 40.76092147827149,
183569: 40.761024475097656},
'dropoff_longitude': {824746: -73.9877395629883,
645821: -73.97727966308595,
691846: -73.86183166503906,
1147931: -73.97512817382812,
183569: -73.96695709228516}})
Желаемый результат:
Ответы (1 шт):
Автор решения: MaxU
→ Ссылка
Векторизированное решение:
from scipy.spatial.distance import cdist
COL_NAMES = dict(
pick_lat="pickup_latitude",
pick_lon="pickup_longitude",
drop_lat="dropoff_latitude",
drop_lon="dropoff_longitude"
)
class MapGridTransformer(BaseEstimator, TransformerMixin):
def __init__(self, loc, col, row, col_names=COL_NAMES):
self.location_mh = loc
self.col = col
self.row = row
self.col_names = col_names
self.pick_lat = col_names["pick_lat"]
self.pick_lon = col_names["pick_lon"]
self.drop_lat = col_names["drop_lat"]
self.drop_lon = col_names["drop_lon"]
self.lat_min, self.lat_max = loc[:, 0].min(), loc[:, 0].max()
self.lon_min, self.lon_max = loc[:, 1].min(), loc[:, 1].max()
...
def get_sector_idx(self, X):
pickup_idx = cdist(X.loc[:, [self.pick_lat, self.pick_lon]],
a.circles_loc).argmin(axis=1)
dropoff_idx = cdist(X.loc[:, [self.drop_lat, self.drop_lon]],
a.circles_loc).argmin(axis=1)
qry = f"@self.lat_min <= {self.pick_lat} <= @self.lat_max \
and @self.lon_min <= {self.pick_lon} <= @self.lon_max"
pickup_valid = X.eval(qry).to_numpy()
pickup_idx[~pickup_valid] = -1
qry = f"@self.lat_min <= {self.drop_lat} <= @self.lat_max \
and @self.lon_min <= {self.drop_lon} <= @self.lon_max"
dropoff_valid = X.eval(qry).to_numpy()
dropoff_idx[~dropoff_valid] = -1
return pickup_idx, dropoff_idx
def transform(self, X):
return (X,) + self.get_sector_idx(X)
тест:



