Вложенный дикт из цикла for, добавляющий одинаковые значения ко всем вложенным ключам - PullRequest
1 голос
/ 29 сентября 2019

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

nested_dict = {poly_1: {address1: distance, address2 : distance}, 
               poly2: {address1: distance, address2: distance}, etc}

Полный применимый код, который я использую:

import pandas as pd
from shapely.geometry import mapping, Polygon, LinearRing, Point
import geopandas as gpd
from math import radians, cos, sin, asin, sqrt

address_dict = {k: [] for k in addresses_geo.input_string}
sludge_dtc = {k: [] for k in sf_geo.unique_name}

def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance between two points
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a))
    r = 3956 # Radius of earth in miles. Use 6371 for kilometers
    return c * r

# Here's the key loop that isn't working correctly
for unique_name, i in zip(sf_geo.unique_name, sf_geo.index):
    for address, pt in zip(addresses_geo.input_string, addresses_geo.index):
        pol_ext = LinearRing(sf_geo.iloc[i].geometry.exterior.coords)
        d = pol_ext.project(addresses_geo.iloc[pt].geometry)
        p = pol_ext.interpolate(d)
        closest_point_coords = list(p.coords)[0]
        # print(closest_point_coords)
        dist = haversine(addresses_geo.iloc[pt].geometry.x,
                         addresses_geo.iloc[pt].geometry.y,
                         closest_point_coords[0], closest_point_coords[1])
        address_dict[address] = dist
    sludge_dtc[unique_name] = address_dict
# Test results on a single address
addresses_with_sludge_distance = pd.DataFrame(sludge_dtc)
print(addresses_with_sludge_distance.iloc[[1]].T)

Если я нарушу этот код и попытаюсь вычислить расстояния для одного многоугольника, кажется, он работает нормально. Однако, когда я создаю DataFrame и проверяю адрес, он перечисляет одинаковое расстояние для каждого полигона.

Таким образом, внутренний ключ-диктат '123 Main Street' будет иметь 5,25 миль за каждый из ключей многоугольника во внешнем диктанте, а '456 South Street' будет иметь 6,13 миль за каждый из ключей многоугольника ввнешний диктат. (Придуманные примеры.)

Я понимаю, что должен делать что-то глупое, как у меня настроены циклы for, но я не могу понять это. Я изменил порядок операторов for, перепутанных с отступами - все тот же результат.

Чтобы было ясно, что я хочу, это:

  • Взятьодин полигон, затем
  • Для каждого адреса в адресных данных найдите расстояние от этого полигона и добавьте его в словарь address_dict с адресом в качестве ключа и расстоянием в качестве значения
  • Когда всеадреса были вычислены, добавьте полный адрес dict в качестве значения для ключа многоугольника в sludge_dtc
  • Перейдите к следующему многоугольнику и продолжите

Есть идеи, что мне не хватает?

1 Ответ

1 голос
/ 02 октября 2019

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

import pandas as pd
from shapely.geometry import mapping, Polygon, LinearRing, Point
import geopandas as gpd
from math import radians, cos, sin, asin, sqrt

def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance between two points
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a))
    r = 3956 # Radius of earth in miles. Use 6371 for kilometers
    return c * r

sludge_dtc = {k: [] for k in sf_geo.unique_name}

# Here's the key loop that isn't working correctly
for unique_name, i in zip(sf_geo.unique_name, sf_geo.index):

    address_dict = {k: [] for k in addresses_geo.input_string}

    for address, pt in zip(addresses_geo.input_string, addresses_geo.index):
        pol_ext = LinearRing(sf_geo.iloc[i].geometry.exterior.coords)
        d = pol_ext.project(addresses_geo.iloc[pt].geometry)
        p = pol_ext.interpolate(d)
        closest_point_coords = list(p.coords)[0]
        # print(closest_point_coords)
        dist = haversine(addresses_geo.iloc[pt].geometry.x,
                         addresses_geo.iloc[pt].geometry.y,
                         closest_point_coords[0], closest_point_coords[1])
        address_dict[address] = dist
    sludge_dtc[unique_name] = address_dict
# Test results on a single address
addresses_with_sludge_distance = pd.DataFrame(sludge_dtc)
print(addresses_with_sludge_distance.iloc[[1]].T)

Другое соображение:

Вы создаете пустые словари с пустыми списками в качестве значений, но после установкизначения напрямую (пустой список заменяется). Если вам нужно собрать список значений, вы должны append значения в существующий список, например:

address_dict[address].append(dist)

и

sludge_dtc[unique_name].append(address_dict)
...