Каждый бизнес желает знать, где сидит фазан целевая аудитория

Всем привет! Геоаналитика преследует меня вот уже 3 года, скоро пойдет 4-й. Начиналось все в компании Тандер (Магнит), где я в роли одного из Дата Сайнтистов проекта прогнозировала потенциальный оборот магазинов, а продолжается в Билайне, где я теперь уже занимаюсь предиктивом не только для ритейла, но и для других форматов. Помимо этого, мне удалось применить геоаналитику для анализа инвестиционной привлекательности коммерческой недвижимости, а также поработать с микробизнесом и предпринимателями, которые не обладают миллионными бюджетами для геомаркетинговых исследований, но в то же время, не очень хотят терять миллионы после выбора неудачного места открытия. Присаживайтесь поудобнее, надеюсь, будет полезно и интересно (а именно: предпринимателям, малому бизнесу, где важен правильный выбор локации и аналитикам того самого бизнеса)

В этой статье рассмотрим кейс "Как выбрать помещение для открытия кофейни?". В целом, данный подход масштабируется на любые виды бизнеса, где важна локация и то, что на ней происходит/находится.

Содержание:

  • Введение в геоаналитику и геомаркетинг

  • Работа с гексагонами: познакомимся с гексагонами (Uber H3), научимся их выгружать, визуализировать

  • Выгрузка, визуализация и анализ гео данных из OSM

  • Где открыть кофейню?

    Бибилиотеки: geopandas, json, shapely, folium, osmnx, h3

    Волшебство: пересечение (пространственный join) полигонов и точек, визуализация гео данных (нанесение точек/полигонов на карту osm и ее различное форматирование), поиск ближайших объектов и др.


Введение в геоаналитику и геомаркетинг

Геома́ркетинг (географический маркетинг) — маркетинговая дисциплина, направленная на взаимодействие с локальной аудиторией, выделенной по географическому признаку, с целью планирования, продвижения и осуществления деятельности в области сбыта продукции (wikipedia)

Проще говоря, геоаналитика - это набор инструментов и методов по работе с пространственными данными, т. е. гео (координаты, геометрии (полигоны, линии и пр.)) + аналитика (обработка, визуализация, выводы), а геомаркетинг - это дисциплина, направленная на исследование локации на предмет состава проживающей/работающей/проходящей аудитории для открытия бизнеса/проведения маркетинговых кампаний

Области применения:

  • розничная торговля продуктами питания (FMCG)

  • здравоохранение (аптеки, частные клиники)

  • общепит (кафе, рестораны, бары)

  • банковский сектор и страхование

  • бьюти - индустрия (салоны красоты, магазины парфюмерии и косметики)

  • DIY (строительные магазины)

  • девелопмент торговых центров и др.

Типовые задачи:

  • выбор оптимального размещения нового объекта

  • оценка потенциального объема продаж, определение класса (масс-маркет, бизнес, люкс) и состава реализуемой продукции/услуг

  • определение эффективного пути использования имеющегося объекта

  • выбор оптимальной локации для размещения наружной рекламы и др. маркетинговых оффлайн активностей

4 принципа масштабирования сети:

4 принципа масштабирования сети
4 принципа масштабирования сети

1) Перехват трафика

Этот принцип подразумевает открытие сетевых точек в зонах с высоким пешеходным трафиком (важно: высокий пешеходный трафик не всегда равен большому количеству целевой аудитории (ЦА) => анализируем состав ЦА), а также вблизи уже открытых точек конкурентов (важно: конкурентное преимущество)

2) Синергия

Эффект синергии достигается благодаря открытию смежных ниш бизнеса. Например: рядом с детскими товарами открывается магазин с товарами для дома/мам и пр.

3) Доступность

Принцип наименьших усилий (подробнее) - это   принцип, который основан на том, что человек по природе своей стремится приложить как можно меньше усилий для получения желаемого. При наличии различных возможностей  – клиент пойдёт туда, где ближе/привычнее/комфортнее. Критерии: радиус охвата, пешеходная доступность, транспортная доступность

4) Кластеризация

Торговые точки должны быть кластеризованы (распределены на группы) как минимум по следующим категориям: бюджет района, тип населенного пункта (большой/малый, поселки и пр.), формат торговой точки. Это означает, что для каждой группы необходима индивидуальная стратегия масштабирования, ценообразования, ассортиментной политики и т.д.

Его величество, BIG DATA

Развитие технологий получения, обработки и хранения геоданных позволяют творить потрясающие вещи в рамках геомаркетинговых исследований.

  • Используя различные приложения вы делитесь своей геопозицией

  • Совершая звонки, смс, используя интернет, вы автоматом попадаете в базы данных телеком операторов, поисковых порталов, а также web-сервисов

  • Подключаясь к WIFI в кафе, ТЦ, вы тоже попадаете в чью-то базу

И это еще не весь список, но важно отметить, что в любом геомаркетинговом исследовании эти данные обезличены и выглядят примерно так: "в радиусе 500 м проживает 666 женщин и 999 мужчин"

Какую информацию может содержать геомаркетинговое исследование локации?

Трафик (автомобильный, пешеходный):

  • Активность (кол-во пешеходов, кол-во автомобилей, кол-во проживающих/работающих)

  • Социально - демографический профиль: пол, возраст, доходы

  • Экономическая активность: количество чеков по категориям покупок, средний чек (ОФД)

  • Интересы (основаны на поисковых запросах, часто посещаемых приложениях и пр.) и др.

Геоданные:

  • Количество магазинов, школ, остановок, офисов, ТЦ, пешеходных переходов, парков, достопримечательностей, в общем - все, что вы можете найти на карте

На сегодняшний день, услуги по геоаналитике, геомеркетингу, внутренним ГИС - системам предоставляют компании - владельцы таких данных, а также подрядчики, которые эти данные покупают у владельцев.

Это действительно один из самых мощных способов снизить риск "вложить кучу денег в открытие, но прогадать с местом", а автоматизация процесса поиска потенциальных мест открытия и их анализа помогает быстрее масштабировать сеть. Федеральные сети уже давно сформировали собственные отделы геоаналитики и активно закупают данные. Но как быть предпринимателям и микро бизнесу, который не готов тратить миллионы?

Если вы - аналитик, владеющий Python, который анализирует/прогнозирует потенциал локации или тот самый бизнес, который ищет место для открытия, а может просто любопытный прохожий, кот, бот - читайте до конца и дайте обратную связь в комментариях. Это моя первая статья, но, если понравится писать, будет продолжение=)

Гексагоны (H3: Uber’s Hexagonal Hierarchical Spatial Index)

Uber опубликовал open source проект, с помощью которого можно легко и просто нанести на карту красивые шестиугольники :) Подробнее.

Сделаем сразу импорт всех библиотек:

import geopandas as gpd
import pandas as pd
import numpy as np
import json
import h3
import folium
import osmnx as ox
from shapely import wkt
from folium.plugins import HeatMap
from shapely.geometry import Polygon

Посмотрим как выглядит гексагон для рандомной точки в г. Краснодар:

def visualize_hexagons(hexagons, color="red", folium_map=None):

    polylines = []
    lat = []
    lng = []
    for hex in hexagons:
        polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False)
        outlines = [loop for polygon in polygons for loop in polygon]
        polyline = [outline + [outline[0]] for outline in outlines][0]
        lat.extend(map(lambda v:v[0],polyline))
        lng.extend(map(lambda v:v[1],polyline))
        polylines.append(polyline)
    
    if folium_map is None:
        m = folium.Map(location=[sum(lat)/len(lat), sum(lng)/len(lng)], zoom_start=20, tiles='cartodbpositron')
    else:
        m = folium_map
        
    for polyline in polylines:
        my_PolyLine=folium.PolyLine(locations=polyline,weight=8,color=color)
        m.add_child(my_PolyLine)
    return m
  
h3_address = h3.geo_to_h3(45.035470, 38.975313,  9) # 9 - индекс, определяющий размер гексагона                                                                                                     
visualize_hexagons([h3_address])

Размеры гексагонов:

OSM

OSM или Open Street Map - это открытый ресурс с геоданными по всему миру. Подробнее о проекте.

Подробнее про объекты карты, которые мы будем выгружать и анализировать.

Теперь, с помощью osmnx и h3 сотворим магию и сгенерим гексагоны внутри полигона г. Краснодара:

1) Выгрузим границы г. Краснодара из OSM

def visualize_polygons(geometry):
    
    lats, lons = get_lat_lon(geometry)
    
    m = folium.Map(location=[sum(lats)/len(lats), sum(lons)/len(lons)], zoom_start=13, tiles='cartodbpositron')
    
    overlay = gpd.GeoSeries(geometry).to_json()
    folium.GeoJson(overlay, name = 'boundary').add_to(m)
    
    return m

# выводим центроиды полигонов
def get_lat_lon(geometry):
        
    lon = geometry.apply(lambda x: x.x if x.type == 'Point' else x.centroid.x)
    lat = geometry.apply(lambda x: x.y if x.type == 'Point' else x.centroid.y)
    return lat, lon
  
# выгрузим границы Краснодара из OSM
cities = ['Краснодар']
polygon_krd = ox.geometries_from_place(cities, {'boundary':'administrative'}).reset_index()
polygon_krd = polygon_krd[(polygon_krd['name'] == 'городской округ Краснодар')]
# посмотрим что получилось
visualize_polygons(polygon_krd['geometry'])

2) Сгенерим гексагоны внутри полигона:

def create_hexagons(geoJson):
    
    polyline = geoJson['coordinates'][0]

    polyline.append(polyline[0])
    lat = [p[0] for p in polyline]
    lng = [p[1] for p in polyline]
    m = folium.Map(location=[sum(lat)/len(lat), sum(lng)/len(lng)], zoom_start=13, tiles='cartodbpositron')
    my_PolyLine=folium.PolyLine(locations=polyline,weight=8,color="green")
    m.add_child(my_PolyLine)

    hexagons = list(h3.polyfill(geoJson, 8))
    polylines = []
    lat = []
    lng = []
    for hex in hexagons:
        polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False)
        # flatten polygons into loops.
        outlines = [loop for polygon in polygons for loop in polygon]
        polyline = [outline + [outline[0]] for outline in outlines][0]
        lat.extend(map(lambda v:v[0],polyline))
        lng.extend(map(lambda v:v[1],polyline))
        polylines.append(polyline)
    for polyline in polylines:
        my_PolyLine=folium.PolyLine(locations=polyline,weight=3,color='red')
        m.add_child(my_PolyLine)
        
    polylines_x = []
    for j in range(len(polylines)):
        a = np.column_stack((np.array(polylines[j])[:,1],np.array(polylines[j])[:,0])).tolist()
        polylines_x.append([(a[i][0], a[i][1]) for i in range(len(a))])
        
    polygons_hex = pd.Series(polylines_x).apply(lambda x: Polygon(x))
        
    return m, polygons_hex, polylines
# polygon_hex , polylines - геометрии гексагонов в разных форматах

# сгенерим гексагоны внутри полигона г. Краснодар
geoJson = json.loads(gpd.GeoSeries(polygon_krd['geometry']).to_json())
geoJson = geoJson['features'][0]['geometry']
geoJson = {'type':'Polygon','coordinates': [np.column_stack((np.array(geoJson['coordinates'][0])[:, 1],
                                                      np.array(geoJson['coordinates'][0])[:, 0])).tolist()]}

m, polygons, polylines = create_hexagons(geoJson)
m

Выгрузка, визуализация и анализ геоданных из OSM

Выгружаем объекты карты из OSM:

def osm_query(tag, city):
    gdf = ox.geometries_from_place(city, tag).reset_index()
    gdf['city'] = np.full(len(gdf), city.split(',')[0])
    gdf['object'] = np.full(len(gdf), list(tag.keys())[0])
    gdf['type'] = np.full(len(gdf), tag[list(tag.keys())[0]])
    gdf = gdf[['city', 'object', 'type', 'geometry']]
    print(gdf.shape)
    return gdf
  
 # Выгрузим интересующие нас категории объектов 
tags = [
        {'building' : 'apartments'}, {'building' : 'detached'}, 
        {'building' : 'dormitory'}, {'building' : 'hotel'}, 
        {'building' : 'house'}, {'building' : 'semidetached_house'}, 
        {'building' : 'terrace'},  {'building' : 'commercial'},
        {'building' : 'office'},  {'building' : 'terrace'},  
        {'building' : 'terrace'}, {'building':'retail'}, 
        {'building':'train_station'},
        {'highway' : 'bus_stop'}, {'footway':'crossing'},
        {'amenity':'cafe'}, {'amenity':'fast_food'}, 
        {'amenity':'restaurant'}, {'amenity':'college'}, 
        {'amenity':'language_school'},  {'amenity':'school'},  
        {'amenity':'university'},  {'amenity':'atm'},  
        {'amenity':'bank'},  {'amenity':'clinic'},  
        {'amenity':'hospital'},  {'amenity':'pharmacy'},  
        {'amenity':'theatre'},  {'amenity':'townhall'},  
        {'amenity':'bench'}, 
       ]
cities = ['Краснодар, Россия']

gdfs = []
for city in cities:
    for tag in tags:
        gdfs.append(osm_query(tag, city))
        
# посмотрим что получилось
data_poi = pd.concat(gdfs)
data_poi.groupby(['city','object','type'], as_index = False).agg({'geometry':'count'})

# добавим координаты/центроиды
lat, lon = get_lat_lon(data_poi['geometry'])
data_poi['lat'] = lat
data_poi['lon'] = lon

Spatial Join

Теперь, нам надо сджойнить полученные объекты с гексагонами:

# sjoin - spatial join - пересекаем гексагоны с объектами (определяем какие объекты находятся в разрезе каждого гексагона)

gdf_1 = gpd.GeoDataFrame(data_poi, geometry=gpd.points_from_xy(data_poi.lon, data_poi.lat))

gdf_2 = pd.DataFrame(polygons, columns = ['geometry'])
gdf_2['polylines'] = polylines
gdf_2['geometry'] = gdf_2['geometry'].astype(str)
geometry_uniq = pd.DataFrame(gdf_2['geometry'].drop_duplicates())
geometry_uniq['id'] = np.arange(len(geometry_uniq)).astype(str)
gdf_2 = gdf_2.merge(geometry_uniq, on = 'geometry')
gdf_2['geometry'] = gdf_2['geometry'].apply(wkt.loads)
gdf_2 = gpd.GeoDataFrame(gdf_2, geometry='geometry')

itog_table = gpd.sjoin(gdf_2, gdf_1, how='left', op='intersects')
itog_table = itog_table.dropna()
itog_table.head()

Посмотрим как по городу распределены кофейни:

def create_choropleth(data, json, columns, legend_name, feature, bins):
    
    lat, lon = get_lat_lon(data['geometry'])

    m = folium.Map(location=[sum(lat)/len(lat), sum(lon)/len(lon)], zoom_start=13, tiles='cartodbpositron')
    
    folium.Choropleth(
        geo_data=json,
        name="choropleth",
        data=data,
        columns=columns,
        key_on="feature.id",
        fill_color="YlGn",
        fill_opacity=0.7,
        line_opacity=0.2,
        legend_name=legend_name,
        nan_fill_color = 'black',
        bins = bins

    ).add_to(m)

    folium.LayerControl().add_to(m)

    return m
  
# подготовим данные 
itog_table['geometry'] = itog_table['geometry'].astype(str) #для groupby
itog_table['id'] = itog_table['id'].astype(str) #для Choropleth
agg_all = itog_table.groupby(['geometry','type','id'], as_index = False).agg({'lat':'count'}).rename(columns = {'lat':'counts'})
agg_all['geometry'] = agg_all['geometry'].apply(wkt.loads) #возвращаем формат геометрий

agg_all_cafe = agg_all.query("type == 'cafe'")[["geometry","counts",'id']]
agg_all_cafe['id'] = agg_all_cafe['id'].astype(str)
data_geo_1 = gpd.GeoSeries(agg_all_cafe.set_index('id')["geometry"]).to_json()

create_choropleth(agg_all_cafe, data_geo_1, ["id","counts"], 'Cafe counts', 'counts', 5)
    

Выгрузим доступную инфу о жилых многоэтажных зданиях:

gdf_aparts = ox.geometries_from_place(city, {'building' : 'apartments'}).reset_index()
# полнота building:levels (этажи)
print(np.round(len(gdf_aparts['building:levels'].dropna())/len(gdf_aparts['building:levels']), 2))

# полнота building:flats (квартиры)
print(np.round(len(gdf_aparts['building:flats'].dropna())/len(gdf_aparts['building:flats']), 2))parts['building:flats'].dropna())/len(gdf_aparts['building:flats']), 2)

Давайте посчитаем сколько людей проживает в этих домах, основываясь на следующих предположениях:

Ok, Гугл: сколько в среднем квартир на этаже?

Гугл вещает, что кол-во квартир напрямую зависит от класса жилья:

  • эконом - в среднем 10

  • комфорт - в среднем 6-8

  • бизнес - в среднем 4-6

Мы можем (как отчаянные дата - сатанисты) углубиться в эту тему и парсить объявления авито/циан и на основе стоимости 1 кв. м определять класс объекта, да и данных по объектам недвижимости будет больше, чем с OSM

Но мы здесь собрались не для того, чтобы парсить авито/циан, поэтому дальше будем использовать в среднем 10 квартир на этаж (доля бизнес и комфорт класса не так велика в Краснодаре)

# добавим фичу - население

lat_g, lon_g = get_lat_lon(gdf_aparts['geometry'])
gdf_aparts['lat'] = lat_g
gdf_aparts['lon'] = lon_g

itog_table_people = itog_table.merge(gdf_aparts[['lat', 'lon', 'building:levels']], on = ['lat', 'lon'], how = 'left')
itog_table_people['building:levels'] = itog_table_people['building:levels'].fillna(1)
itog_table_people = itog_table_people.rename(columns = {'building:levels' : 'levels'})

apartments = ['apartments' , 'dormitory']
houses = ['house', 'semidetached_house', 'detached', 'terrace']
people_ctn = []

# в среднем возьмем 3 чел. на семью

for i in range(len(itog_table_people)):
    
    if itog_table_people['type'].iloc[i] in apartments:
        
        people = int(itog_table_people['levels'].iloc[i])*10*3
        
    elif itog_table_people['type'].iloc[i] in houses:
        
        people = int(itog_table_people['levels'].iloc[i])*3
        
    else:
        people = 'not living area'
        
    people_ctn.append(people)
    
itog_table_people['count_people'] = people_ctn

table_people = itog_table_people.query("count_people != 'not living area'")
table_people['count_people'] = table_people['count_people'].astype(int)

Посмотрим что у нас вышло с плотностью "гипотетического" населения в Краснодаре:

def create_heatmap(data, lat_lon_feature):
    
    m = folium.Map(location=[sum(data['lat'])/len(data['lat']), sum(data['lon'])/len(data['lon'])], zoom_start=13, tiles='cartodbpositron')
    
    HeatMap(data[lat_lon_feature].groupby(lat_lon_feature[0:2]).sum().reset_index().values.tolist(), 
                    radius = 70, min_opacity = 0.05, max_val = int((data[lat_lon_feature[2]]).quantile([0.75])), blur=30).add_to(m)
    return m
  
# карта плотности населения
create_heatmap(table_people, ['lat', 'lon', 'count_people'])
ля какая красота (не сильно точные данные у OSM, но дареному коню в зубы не смотрят!)
ля какая красота (не сильно точные данные у OSM, но дареному коню в зубы не смотрят!)

Где открыть кофейню? Определяем лучшее место для поиска потенциального помещения

Где открыть кофейню? Вопрос для отдельной статьи, а сюда я пришла, чтобы показать вам пример как можно творить магию геаналитику на открытых данных. Логично, что одним из ключевых факторов будет много людей и мало конкурентов, т. е. я предлагаю вам рассчитать фичу население/кол-во кофеен в гексагоне. Так мы сможем выбрать ТОП гексагоны для поиска потенциальных мест для открытия кофейни. Итого:

  1. Добавим фичу население/кол-во кофеен в гексагоне.

  2. Выберем гексагон с наибольшим значением.

  3. Выберем этот гексагон и его 6 соседей (быстро это можно сделать с помощью KDTree библиотеки scipy), чтобы на всякий пожарный не упустить ближайшие области из-за каких-либо перекосов в данных OSM (опционально).

  4. Открываем авито/циан и ищем там помещение для кофейни.

Какие еще фичи можно рассчитать?

  • Количество якорей трафика (ТЦ, БЦ, фаст фуд рестораны, супермаркеты и т. д.) поделим на кол-во кофеен/на кол-во населения

  • Huff (а вот про модель Huff я расскажу позже :)

Моя первая статья на Habr. Не кидайте помидоры :)

Комментарии (35)


  1. PastorGL
    24.09.2021 17:50
    +1

    Привет, коллега. Неплохо для вводной статьи.


    Надеюсь, последуют ещё статьи, и вы расскажете, что для открытия кофейни в каком-нить райончике необходимо знать не только кол-во населения и плотность заведений, но и типичный паттерн перемещений в интересующей области. Гуляют ли там люди, или, например, проезжают мимо. И, главное, есть ли у них принципиальный интерес её посетить, — который, как показывает практика, вполне вычислим.


    Расскажете вашу версию? (Я вот, к сожалению, наш патентованный метод из-за NDA не могу озвучить.)


    1. TatianaLi Автор
      24.09.2021 18:01
      +2

      Привет! Спасибо большое за обратную связь! Планируется еще не 1 статья, так как тема действительно очень обширная и есть чем поделиться (эх, если бы и правда все было так просто - открыть там, где много людей:))


    1. TatianaLi Автор
      24.09.2021 18:36

      раскрыть всю внутренность как это происходит при работе с биг датой я конечно тоже не могу, но буду делиться другими фишками, которые можно использовать малому бизнесу)


      1. PastorGL
        24.09.2021 18:54
        +2

        Ну, мой проект уже 5 лет занимается задачами геоинформационной аналитики.


        Так что я могу со знанием дела подтвердить, что задачи на классификацию/типирование/скоринг/профилирование популяций и потоков людей — это крайне интересные и достаточно сложные задачи.


        Главное, никто до сих пор не научился решать их с приемлемым соотношением стоимость/качество (все overpromising, но underdelivering), поэтому любое знание тут ценно.


        1. sshikov
          24.09.2021 19:29

          >Главное, никто до сих пор не научился решать их с приемлемым соотношением стоимость/качество
          Да тут полно проблем. Минимум одна из которых вообще не решается одной аналитикой: для начала нужно иметь что анализировать. Т.е. исходные данные, если уж совсем просто (и даже не обязательно потоки людей, а просто статические данные). Ну т.е. если вернуться к кейсу «открыть кофейню», то очевидно нужны данные сразу нескольких видов:
          * где можно открыть (данные об аренде помещений в этом районе)
          * стоимость аренды
          * есть ли в этом месте достаточный поток потенциальных клиентов
          * что именно нужно этим клиентам
          * а есть ли конкуренты, и как они вообще себя чувствуют?

          И в общем априори очевидно, что это данные далеко не из одного источника. В итоге, их не просто сложно собрать, но еще и сложно сопосотавить друг с другом. Автор, как я понимаю, в Билайне (и как у сотового оператора, есть данные по людям и их перемещениям), я смотрел на это когда-то со стороны банка (и были данные по транзакциям), но даже если вы Билайн или банк с кучей клиентов — у вас все равно далеко не всегда есть все данные. Скажем — данные по конкурентам — тот же Билайн вероятно может их только купить (у Яндекса, Гугля, 2ГИС и т.п.). И даже если вы банк — как только конкурент обслуживается не у вас, вы уже очень мало про него знаете.

          И возможно, по именно этой причине у автора разбор кейса с кофейней (возможно, пока), заканчивается на самом интересном месте:

          Открываем авито/циан и ищем там помещение для кофейни


          Так а где сравнение помещений в пределах района? Может, я что-то упустил в тексте, но на мой взгляд, разница в посещаемости между двумя кофейнями (я привычно примеряю на свой район) в торговом центре у метро и в двух кварталах от него (т.е. 100 метров) — возможно где-то на порядок.


          1. PastorGL
            24.09.2021 19:48
            +1

            Да данные-то есть. Сотовые операторы, банки, рекламные сети, и все кто угодно продают их через агрегаторы в деперсонифицированном виде с превеликим удовольствием. Про это я писал в своей статье уже скоро пару лет назад, а с тех пор ничего не поменялось. Но стоят они дорого, а качество зачастую паршивое. Что тоже ни фига не меняется.


            Поэтому, чтобы их сопоставить, и извлечь полезные знания, приходится заниматься некоторой алгоритмической эквилибристикой. Очень большой простор для изобретения всяких эвристик и enrichment mechanics.


            Плюс ко всему, все теоретические модели хорошо работают только на том сэмпле, на котором их, собственно, и моделировали. Что хорошо работает в Америке, то в Европе показывает ерунду, и наоборот.


            Эх, если бы мне только можно было рассказать подробности наших методик, я б десяток статей тут настрочил. К сожалению, коммерческая тайна.


            1. sshikov
              24.09.2021 20:28
              +3

              >Но стоят они дорого, а качество зачастую паршивое.
              Так а я о чем? Данные есть в разных источниках, сопоставить их друг с другом сложно, потому что см. выше. Ну т.е. это одна из проблем — возможно решаемая, но реальная.

              >простор для изобретения всяких эвристик
              Чем наши сайентисты большую часть времени и занимались. Т.е. упрощенно — вот у вас есть транзакции, и вы хотите понять, чего потребитель купил там или там (чтобы оценить саму потребность в кофейне). И выясняется, что транзакции-то — они из VISA, например, и мерчант (и его адрес) там записан в некоторой латинской транскрипции, которую фиг сопоставишь с любой из карт (т.е. процессы геокодирования становятся совершенно ненадежными). Т.е. мы не можем по транзакции зачастую понять, где купили, а что купили — там просто не пишется.

              В итоге транзакции достаточно просто сопоставляются только в одном случае — если вы банк, и это ваша карточка, а еще в кафешке стоит POS от вас же (что очевидно далеко не всегда так). В остальных случаях уже тоже начинались эвристики.

              >подробности наших методик
              Ну так именно это и интересно, конечно же :) И конкурентам вашим — в первую очередь.


              1. TatianaLi Автор
                25.09.2021 00:35

                Очень все индивидуально (в плане данных/алгоритма/точности и т. д.) для каждого формата/заказчика. Но факт, что даже модель "на коленке" может улучшить работу отдела развития (те ребята, которые ищут коммерческие помещения и т. д.) - это факт. Чем крупнее компания и соответственно обучающая выборка и ресурсы, тем лучше результат конечно, от этого никуда не денешься... а мэтчинг транзакций мало кто использует, слишком это дорого и эффект не оправдывает себя


                1. sshikov
                  25.09.2021 09:52

                  >мэтчинг транзакций мало кто использует
                  Ну мы пробовали. Помимо всего прочего, это персональные данные со всеми вытекающими. Тут нужно сильно стараться, чтобы они в таком персональном виде не вытекли куда-то случайно, а это тоже да, дорого. Да и много их, тут уже реально большие данные начинают иметь место.


              1. PastorGL
                25.09.2021 00:38

                И конкурентам вашим — в первую очередь.

                А мы даже с удовольствием бы всю нашу алгоритмику и методику продавали кому угодно, но «конкуренты» почему-то хотят всё сделать сами, вместо того чтобы готовое взять %) Странно это. Никто не хочет верить чужим эвристикам, видимо.


                Пока что только с Precisely договорились, чтобы augment'ить ихний социодем по Европе. По Америке вот не получается, там качество сырых данных такое, что 99% сразу уходит в шлак.


                1. sshikov
                  25.09.2021 09:57

                  >Никто не хочет верить чужим эвристикам, видимо.
                  Возможно. А может быть у них просто другой набор доступных данных и несколько другие задачи, и они не уверены, что что-то получится на чужих эвристиках.


      1. sshikov
        24.09.2021 19:35
        +1

        Честно говоря, какой-то код на питоне — чуть ли не последнее, что хотелось бы видеть в статье обзорного плана. Ну, во всяком случае на мой взгляд. Те кто этой темой уже занимается (и даже такие как я, кто уже три года как бросил и занят другой), уже вероятно нашли свой инструмент, и уж что такое OSM, вероятно знают давно. И ЦИАН тоже парсили :)

        Лучше бы ближе к бизнесу, если можно так выразиться, и чуть выше уровнем.


        1. TatianaLi Автор
          25.09.2021 00:36

          Спасибо за обратную связь! Статья как раз рассчитана на новичков, "прожженным" аналитикам в сфере гео будет скучно, согласна)


          1. PastorGL
            25.09.2021 00:45

            Тема очень уж узкоспециализированная, новичков в неё фиг заманишь %)


          1. mentin
            25.09.2021 02:45

            Код на питоне - отлично. Мне делающему это на SQL интересно как это в параллельной вселенной :).

            Кстати, а зачем spatial join шестиугольников с точками? Думаю здесь гораздо быстрее для каждой точки посчитать номер шестиугольника и сделать обычный джоин по номерам шестиугольников, нет?


            1. TatianaLi Автор
              25.09.2021 21:10

              Спасибо!


            1. TatianaLi Автор
              25.09.2021 21:11

              Пространственные индексы используются в предназначенных для работы с его данными базах как раз для оптимизации расчетов. Вручную с помощью python это делать смысла нет, когда есть библиотека GeoPandas


              1. mentin
                25.09.2021 23:15

                Пространственные индексы великая вещь, когда вы работаете с произвольными фигурами, например полигонами территорий, линиями дорог. Но они в любом случае сравнительно медленные, по сравнению с хеш-таблицами. Убер свои шестиугольники создал именно как индекс, потому что (при масштабе Убера) универсальный пространственный индекс не справлялся.

                Вы используете шестиугольники как удобную сетку, но не используете их основное предназначение как индекс. Когда всё уже проиндексировано шестиугольниками, совершенно естественно ими воспользоваться, а не индексировать заново универсальным индексом, который про специальные шестиугольники ничего знает, и будет работать с ними как с произвольными полигонами.


        1. ne555
          25.09.2021 08:27
          +2

          код на питоне — чуть ли не последнее, что хотелось бы видеть в статье обзорного плана

          Лучше бы ближе к бизнесу

          Хабр технический ресурс, а не бизнес инкубатор.

          Автору за статью благодарность, позновательно!


          1. sshikov
            25.09.2021 09:54

            Если вам код такого типа интересен — то он уже сотни раз опубликован. Впрочем, мне совершенно не жалко, Я лишь свое мнение высказал, что идеи, которые этот код реализует, гораздо интереснее, потому что их мало хороших. А приведенный код расчета плотности населения… ну и что он вам дал полезного?


            1. TatianaLi Автор
              25.09.2021 21:14

              Понимаю ваш интерес, согласна, что можно было бы шире раскрыть тему. А про плотность населения на удивление не все в курсе) (опять таки, я говорю про небольшие компании, а не про специализированные отделы геоаналитики:)


              1. sshikov
                25.09.2021 22:11

                >про плотность населения на удивление не все в курсе
                Я тут вижу пару аспектов:
                — про шестиугольную сетку вы допустим рассказали, и показали какие-то расчеты, но вот почему собственно такая выбрана (а не обычная прямоугольная) — как-то я не вижу чтобы было написано (я если что, догадываюсь :).
                — задача расчета плотности населения (как и любая частная задача) еще очень далека от реальной задачи оптимального размещения своего бизнеса в городе. То есть, на мой взгляд опять же, интереснее то, как можно получить практические выводы, тем более что тут все далеко не так очевидно, и не сводится к простой математике. Да и к сложной, в общем-то, тоже…


          1. TatianaLi Автор
            25.09.2021 21:12

            Спасибо!


      1. Cleando
        25.09.2021 00:36
        +1

        Будем ждать!


        1. TatianaLi Автор
          25.09.2021 00:37

          Спасибо!!!


  1. molec
    25.09.2021 12:11

    Огромное спасибо за статью! Я бы сам до шестиугольников еще долго не дошёл, хотя с географией работаю.

    Пс. Главное, чтобы никто не бросился по этой тепловой карте строить кофейни в Краснодарском Шанхае ;)


    1. TatianaLi Автор
      25.09.2021 21:16

      Спасибо! Ох, про наш местный Шанхай совсем другая история)))


      1. molec
        25.09.2021 22:20

        И еще одна история, как центр Красноярска оказался на КДПВ?


  1. mbamber
    25.09.2021 21:17

    Отличная статья спасибо!

    Продолжайте)


    1. TatianaLi Автор
      25.09.2021 21:17

      Спасибо!


  1. mentin
    26.09.2021 09:05

    Просьба, если уж включаете в статью код, проверять что он хоть чуть-чуть работает.

    Видимо, делалось все в Jupiter, где куча всякого хлама остается в контексте, и можно итеративно использовать переменные созданные ниже или раньше, но стоит запустить код чистом режиме, и проверить, чтобы такой хлам не попадал в статью.

    • Откуда hexagons в самом начале в for hex in hexagons :, к этому моменту был только один шестиугольник h3_address. Мистически появились из четвертого куска кода (мы во втором).

    • Почему get_lat_lon_ вызывается с подчеркиванием на конце, а определено без?

    • Что за geometry используется в overlay = gpd.GeoSeries(geometry).to_json() - до этого geometry была только локальной переменной и не присутствует в текущем скоупе, переменной с таким именем нет даже ниже.

    Такой подход к коду оставляет впечатление жуткой неаккуратности (к которой на мой взгляд приучает Jupiter, но это для другой статьи).


    1. TatianaLi Автор
      26.09.2021 16:57

      Спасибо большое за замечания, уже редактирую, сегодня обновлю статью!

      Не ругайтесь на юпитер) вы, как я поняла, разработчик, поэтому я прекрасно понимаю о чем вы)


  1. Vinnand
    26.09.2021 19:20

    Круто. Отличная статья!


    1. TatianaLi Автор
      26.09.2021 19:20

      Спасибо!


  1. boggis30
    22.10.2021 19:58

    Очень интересно, узнал из статьи и из комментариев про индексирование шестиугольниками. Спасибо!