Agregando polígonos para cumplir con los requisitos de privacidad

10

Tengo una clase de entidad de puntos que representa ubicaciones de oficinas de todos los empleadores en una industria determinada. La clase de entidad tiene un atributo para almacenar la cantidad de empleados que trabajan en cada oficina. Alguien ha solicitado utilizar estos datos, unidos espacialmente a la unidad geográfica más pequeña posible: bloques censales, en este caso. Sin embargo, un acuerdo de privacidad impide la divulgación de los datos tal cual. En cambio, debe suprimirse para cumplir con dos criterios:

  1. Cualquier polígono debe contener al menos 3 empleadores (puntos);
  2. No más del 80% del empleo total dentro de un polígono puede ser por un solo empleador.

He escrito con éxito un guión que une espacialmente los puntos a los bloques del censo, manteniendo la suma y el empleo máximo en cada uno. Cada uno que no cumple con los criterios de supresión está marcado. (Los polígonos que no contienen puntos no se marcan, ya que no hay datos para suprimir). Luego verifico cada Grupo de bloques para ver si hay Bloques marcados dentro de él. Los grupos de bloques que contienen solo bloques sin marcar se reemplazan por los bloques. La clase de entidades resultante se compara con los criterios de supresión, para verificar si los Grupos de bloques han suprimido adecuadamente los datos.

El mismo proceso se repite para Tracts, dejándome con un conjunto de datos que consta de Tracts (algunos marcados y otros no), Grupos de bloques y Bloques (todos sin marcar). Sin embargo, la siguiente progresión en la jerarquía geográfica es el condado, que no sirve para la persona que solicita estos datos.

Mi pregunta, entonces, es esta: ¿Existen métodos comúnmente aceptados para agregar polígonos en tantos grupos como sea posible, de modo que todos cumplan con algunos criterios mínimos?

Aquí hay algunas reglas que me gustaría aplicar a la agregación:

  • Siempre que sea posible, los Tractos marcados solo deben agregarse con otros Tratados marcados;
  • Para los Tratados marcados que no son contiguos con ningún otro (o agrupaciones aisladas que aún no cumplen con los criterios), se pueden unir con Tractos que ya cumplen con los criterios, aunque puede haber Tratados sin empleadores entre ellos que también Necesita ser incluido.
  • Me gustaría mantener intactos los límites del condado a menos que sea absolutamente imposible (y anticipo hacerlo separando las características de las entradas en sus respectivos condados antes de procesarlas).
  • La solución debe estar en Python, con el uso de herramientas ArcGIS o bibliotecas Python de código abierto.

Idealmente, alguien puede señalarme un medio existente para implementar esta agregación. Si no, estoy feliz de codificar el algoritmo yo mismo, aunque una lista de pasos / herramientas específicas sería muy apreciada. El problema me parece un caso especial de redistribución de distritos (con polígonos no contiguos), y con este fin he investigado el uso de los algoritmos de regionalización de PySAL , aunque no tengo claro cómo verificar el porcentaje máximo de empleadores que emplean estos empleados. .

nmpeterson
fuente

Respuestas:

5

Para cualquiera que tenga curiosidad, se me ocurrió una solución por mi cuenta, utilizando el algoritmo region.Maxp de PySAL . Esencialmente, Max-p me permite generar un conjunto de regiones que cumple con mi primer criterio (número mínimo de empleadores por región), y lo puse dentro de un ciclo while, que rechazará cualquiera de las soluciones de Max-p que tampoco Satisfacer el segundo criterio (porcentaje de empleo aportado por el mayor empleador de una región). Lo he implementado como una herramienta ArcGIS.

Decidí descartar el trabajo que había hecho anteriormente para marcar bloques / grupos de bloques / secciones y, en su lugar, ejecutar Max-p en bloques (aunque he estado haciendo todas mis pruebas en secciones, ya que ha aumentado un poco el número de polígonos de entrada un efecto dramático en el tiempo de procesamiento). La parte relevante de mi código sigue. El "archivo de forma" requerido como entrada para la generate_regions()función (pasada como una cadena que contiene la ruta completa de un archivo de forma) es uno que tiene las características de punto de los empleadores ya unidas espacialmente, con el número de empleadores, el número máximo de empleados de un solo empleador , y el total de empleados almacenados como un atributo para cada característica de entrada.

import arcpy, math, pysal, random
import numpy as np

# Suppression criteria:
MIN_EMP_CT = 3      # Minimum number of employers per polygon feature
MAX_EMP_FRAC = 0.8  # Maximum ratio of employees working for a single employer per polygon feature

def generate_regions(shapefile, min_emp_ct=MIN_EMP_CT, max_emp_frac=MAX_EMP_FRAC):
    '''Use pysal's region.Maxp method to generate regions that meet suppression criteria.'''
    w = pysal.rook_from_shapefile(shapefile, idVariable='GEOID10')
    dbf = pysal.open(shapefile[:-4] + '.dbf')
    ids = np.array((dbf.by_col['GEOID10']))
    vars = np.array((dbf.by_col[employer_count_fieldname],dbf.by_col[max_employees_fieldname],dbf.by_col[total_employees_fieldname]))
    employers = vars[0]
    vars = vars.transpose()
    vars_dict = {}
    for i in range(len(ids)):
        vars_dict[ids[i]] = [int(vars[i][0]),float(vars[i][1]),float(vars[i][2])]
    random.seed(100)     # Using non-random seeds ensures repeatability of results
    np.random.seed(100)  # Using non-random seeds ensures repeatability of results
    bump_iter = int(arcpy.GetParameterAsText(3)) # Number of failed iterations after which to increment the minimum number of employers per region (otherwise we could be stuck in the loop literally forever).
    iteration = 0
    tests_failed = 1
    while tests_failed:
        floor = int(min_emp_ct + math.floor(iteration / bump_iter))
        solution = pysal.region.Maxp(w,vars,floor,employers)
        regions_failed = 0
        for region in solution.regions:
            SUM_emp10sum = 0
            MAX_emp10max = 0
            for geo in region:
                emp10max = vars_dict[geo][1]
                emp10sum = vars_dict[geo][2]
                SUM_emp10sum += emp10sum
                MAX_emp10max = max(MAX_emp10max, emp10max)
            if SUM_emp10sum > 0:
                ratio = MAX_emp10max / SUM_emp10sum
            else:
                ratio = 1
            if ratio >= max_emp_frac:
                regions_failed += 1
        iteration += 1
        if regions_failed == 0:
            arcpy.AddMessage('Iteration ' + str(iteration) + ' (MIN_EMP_CT = ' + str(floor) +') - PASSED!')
            tests_failed = 0
        else:
            arcpy.AddMessage('Iteration ' + str(iteration) + ' (MIN_EMP_CT = ' + str(floor) +') - failed...')
    return solution

solution = generate_regions(spatially_joined_shapefile)

regions = solution.regions

### Write input-to-region conversion table to a CSV file.
csv = open(conversion_table,'w')
csv.write('"GEOID10","REGION_ID"\n')
for i in range(len(regions)):
    for geo in regions[i]:
        csv.write('"' + geo + '","' + str(i+1) + '"\n')
csv.close()
nmpeterson
fuente
2

Nunca me he encontrado con una situación como esta, y creo que una ruta más común es mantener las unidades que usted decida a priori y luego usar diferentes técnicas para "falsificar" los datos para proteger las preocupaciones de privacidad.

Para una introducción a la miríada de formas en que las personas ocultan datos, sugeriría este artículo;

Matthews, Gregory J. y Ofer Harel. 2011. Confidencialidad de los datos: una revisión de los métodos para la limitación de divulgación estadística y los métodos para evaluar la privacidad . Encuestas estadísticas 5 : 1-29. El PDF está disponible gratuitamente en Project Euclid en el enlace anterior.

También tengo algunos enlaces a varios otros artículos que discuten "geomasking" en esa etiqueta en mi biblioteca citeulike (aunque no todos están estrictamente relacionados con datos geográficos).

Aunque esto no responde a su pregunta, es posible que algunas de las técnicas enumeradas en el artículo de Matthews y Ofer sean más fáciles de implementar para satisfacer sus necesidades. En particular, la elaboración de datos sintéticos parece una extensión lógica de hacia dónde se dirigía (los datos externos se tomarían prestados del grupo de bloque censal o zona o condado circundante si fuera necesario). Además, algunos tipos de intercambio de datos (en el espacio) también pueden ser más fáciles de implementar.

Andy W
fuente
Gracias por la respuesta / comentario. Soy consciente de la existencia de métodos para falsificar datos para conservar la validez estadística mientras se protege la privacidad, aunque ese documento me ha ayudado a comprender un poco mejor los detalles. Desafortunadamente, tales métodos no parecen particularmente aplicables a mi situación, cuando la única información que estoy divulgando es la cantidad de empleadores y empleados en un área determinada: mezclarlos inevitablemente afectaría la validez de cualquier análisis basado en ellos, ¿no?
nmpeterson
La agregación impacta el análisis de una manera potencialmente similar. Sin embargo, es difícil dar consejos generales, y su producto final debe guiarse por lo que la gente hará posteriormente con los datos. Puedo imaginar algunas situaciones en las que crear unidades finales de agregaciones variables sería problemático. Por ejemplo, si estuviera interesado en comparar la aglomeración / competencia entre empleadores, diferentes unidades serían un problema, pero, especialmente si quiero continuar y vincularlo con otros datos demográficos del censo.
Andy W
En ese caso, preferiría tener una unidad de análisis que tenga una cantidad arbitraria de error incorporada, pero estoy seguro de que puede pensar en otros usos en los que sería preferible la agregación y (en teoría) ningún error.
Andy W