J'ai un tableau avec 8 colonnes et ~ 16,7 millions d'enregistrements. J'ai besoin d'exécuter un ensemble d'équations if-else sur les colonnes. J'ai écrit un script à l'aide du module UpdateCursor, mais après quelques millions d'enregistrements, il manque de mémoire. Je me demandais s'il y avait une meilleure façon de traiter ces 16,7 millions d'enregistrements.
import arcpy
arcpy.TableToTable_conversion("combine_2013", "D:/mosaic.gdb", "combo_table")
c_table = "D:/mosaic.gdb/combo_table"
fields = ['dev_agg', 'herb_agg','forest_agg','wat_agg', 'cate_2']
start_time = time.time()
print "Script Started"
with arcpy.da.UpdateCursor(c_table, fields) as cursor:
for row in cursor:
# row's 0,1,2,3,4 = dev, herb, forest, water, category
#classficiation water = 1; herb = 2; dev = 3; forest = 4
if (row[3] >= 0 and row[3] > row[2]):
row[4] = 1
elif (row[2] >= 0 and row[2] > row[3]):
row[4] = 4
elif (row[1] > 180):
row[4] = 2
elif (row[0] > 1):
row[4] = 3
cursor.updateRow(row)
end_time = time.time() - start_time
print "Script Complete - " + str(end_time) + " seconds"
MISE À JOUR # 1
J'ai exécuté le même script sur un ordinateur avec 40 Go de RAM (l'ordinateur d'origine n'avait que 12 Go de RAM). Il s'est terminé avec succès après ~ 16 heures. Je pense que 16 heures, c'est trop long, mais je n'ai jamais travaillé avec un si grand ensemble de données, donc je ne sais pas à quoi m'attendre. Le seul nouvel ajout à ce script est arcpy.env.parallelProcessingFactor = "100%"
. J'essaie deux méthodes suggérées (1) faire 1 million d'enregistrements par lots et (2) en utilisant SearchCursor et en écrivant des sorties sur csv. Je rendrai compte des progrès sous peu.
MISE À JOUR # 2
La mise à jour SearchCursor et CSV a parfaitement fonctionné! Je n'ai pas les temps d'exécution précis, je mettrai à jour le message quand je serai au bureau demain, mais je dirais que le temps d'exécution approximatif est de ~ 5-6 minutes, ce qui est assez impressionnant. Je ne m'y attendais pas. Je partage mon code non poli tous les commentaires et améliorations sont les bienvenus:
import arcpy, csv, time
from arcpy import env
arcpy.env.parallelProcessingFactor = "100%"
arcpy.TableToTable_conversion("D:/mosaic.gdb/combine_2013", "D:/mosaic.gdb", "combo_table")
arcpy.AddField_management("D:/mosaic.gdb/combo_table","category","SHORT")
# Table
c_table = "D:/mosaic.gdb/combo_table"
fields = ['wat_agg', 'dev_agg', 'herb_agg','forest_agg','category', 'OBJECTID']
# CSV
c_csv = open("D:/combine.csv", "w")
c_writer = csv.writer(c_csv, delimiter= ';',lineterminator='\n')
c_writer.writerow (['OID', 'CATEGORY'])
c_reader = csv.reader(c_csv)
start_time = time.time()
with arcpy.da.SearchCursor(c_table, fields) as cursor:
for row in cursor:
#skip file headers
if c_reader.line_num == 1:
continue
# row's 0,1,2,3,4,5 = water, dev, herb, forest, category, oid
#classficiation water = 1; dev = 2; herb = 3; ; forest = 4
if (row[0] >= 0 and row[0] > row[3]):
c_writer.writerow([row[5], 1])
elif (row[1] > 1):
c_writer.writerow([row[5], 2])
elif (row[2] > 180):
c_writer.writerow([row[5], 3])
elif (row[3] >= 0 and row[3] > row[0]):
c_writer.writerow([row[5], 4])
c_csv.close()
end_time = time.time() - start_time
print str(end_time) + " - Seconds"
MISE À JOUR # 3 Mise à jour finale. La durée totale d'exécution du script est de ~ 199,6 secondes / 3,2 minutes.
la source
Réponses:
Vous pouvez écrire l'Objectid et le résultat du calcul (cate_2) dans un fichier csv. Joignez ensuite le fichier csv à votre fichier d'origine, remplissez un champ pour conserver le résultat. De cette façon, vous ne mettez pas à jour la table à l'aide du curseur DA. Vous pouvez utiliser un curseur de recherche.
la source
Toutes mes excuses, si je continue de faire revivre ce vieux fil. L'idée était d'exécuter les instructions if-else sur le raster de combinaison, puis d'utiliser le nouveau champ dans la recherche pour créer un nouveau raster. J'ai compliqué le problème en exportant les données sous forme de tableau et introduit un flux de travail inefficace qui a été résolu par @Alex Tereshenkov. Après avoir réalisé l'évidence, j'ai groupé les données en 17 requêtes (1 million chacune) comme suggéré par @FelixIP. Il a fallu environ 1,5 minute à chaque lot pour terminer et la durée totale de fonctionnement était d'environ 23,3 minutes. Cette méthode élimine le besoin de jointures et je pense que cette méthode accomplit le mieux la tâche. Voici un script révisé pour référence future:
la source
Lookup
et exporter le raster avec les catégories nouvellement définies.arcpy.env.parallelProcessingFactor = "100%"
n'a aucun effet sur votre script. Je ne vois aucun outil qui exploite cet environnement.Vous pouvez essayer de passer à l'aide de CalculateField_management . Cela évite de parcourir en boucle à l'aide de curseurs et, d'après l'apparence de vos options pour la valeur de catégorie, vous pouvez définir cela comme quatre sous-processus générés séquentiellement. Lorsque chaque sous-processus se termine, sa mémoire est libérée avant de démarrer le suivant. Vous prenez un petit coup (millisecondes) engendrant chaque sous-processus.
Ou, si vous souhaitez conserver votre approche actuelle, ayez un sous-processus qui prend x-lignes à la fois. Avoir un processus principal pour le piloter et, comme précédemment, vous continuez à fouiller votre mémoire chaque fois qu'elle se termine. L'avantage de le faire de cette façon (en particulier via un processus python autonome) est que vous pouvez utiliser davantage tous vos cœurs en tant que sous-processus de génération dans le multithreading de python que vous contournez le GIL. Cela est possible avec ArcPy et une approche que j'ai utilisée dans le passé pour effectuer des transferts de données massifs. Évidemment, gardez vos morceaux de données bas sinon vous finirez par manquer de mémoire plus rapidement!
la source
La logique de manipulation des données peut être écrite sous la forme d'une instruction SQL UPDATE à l'aide d'une expression CASE, que vous pouvez exécuter à l'aide de GDAL / OGR, par exemple via OSGeo4W avec
gdal-filegdb
installé.Voici le workflow, qui utilise à la
osgeo.ogr
place dearcpy
:Sur une table similaire avec un peu plus d'un million d'enregistrements, cette requête a pris 18 minutes. Le traitement de 16 millions d'enregistrements peut donc prendre environ 4 à 5 heures.
la source
arcpy
mais j'apprécie la réponse. J'essaie lentement d'utiliser GDAL davantage.La mise à jour du code dans la section # 2 de votre question ne montre pas comment vous joignez le
.csv
fichier à la table d'origine dans votre géodatabase fichier. Vous dites que l'exécution de votre script a duré environ 5 minutes. Cela semble juste si vous avez uniquement exporté le.csv
fichier sans faire de jointures. Lorsque vous tenterez de ramener le.csv
fichier dans ArcGIS, vous rencontrerez les problèmes de performances.1) Vous ne pouvez pas faire de jointures directement à partir
.csv
de la table de géodatabase, car le.csv
fichier n'a pas d'OID (avoir un champ calculé avec des valeurs uniques n'aidera pas car vous devrez toujours convertir votre.csv
fichier en table de géodatabase). Ainsi, plusieurs minutes pour l'Table To Table
outil GP (vous pouvez utiliser l'in_memory
espace de travail pour y créer une table temporaire, ce sera légèrement plus rapide).2) Une fois que vous avez chargé le
.csv
dans une table de géodatabase, vous souhaitez créer un index sur le champ sur lequel vous effectuez la jointure (dans votre cas, laobjectid
valeur source du.csv
fichier. Cela prendrait quelques minutes sur un tableau de 16 ml de lignes).3) Ensuite, vous devez utiliser les outils
Add Join
ouJoin Field
GP. Aucun ne fonctionnera bien sur vos grandes tables.4) Ensuite, vous devez faire l'
Calculate Field
outil GP pour calculer le ou les nouveaux champs joints. De nombreuses minutes passent ici; encore plus, le calcul de champ prend plus de temps lorsque les champs qui participent au calcul proviennent d'une table jointe.En un mot, vous n'obtiendrez rien près de 5 minutes que vous mentionnez. Si vous arriviez dans une heure, je serais impressionné.
Pour éviter de traiter le traitement d'ensembles de données volumineux dans ArcGIS, je suggère de prendre vos données en dehors d'ArcGIS dans un bloc de
pandas
données et de faire tous vos calculs là-bas. Lorsque vous avez terminé, réécrivez simplement les lignes du bloc de données dans une nouvelle table de géodatabase avecda.InsertCursor
(ou vous pouvez tronquer votre table existante et écrire vos lignes dans la source).Le code complet que j'ai écrit pour comparer ceci est ci-dessous:
Vous trouverez ci-dessous la sortie du Debug IO (le nombre signalé est le nombre de lignes dans une table utilisée) avec des informations sur le temps d'exécution pour les fonctions individuelles:
L'insertion d'une ligne avec
da.InsertCursor
prend un temps constant, c'est-à-dire que si insérer 1 ligne prend, disons, 0,1 seconde, l'insertion de 100 lignes prendra 10 secondes. Malheureusement, 95% + du temps d'exécution total est consacré à la lecture de la table de géodatabase, puis à la réinsertion des lignes dans la géodatabase.Il en va de même pour la création d'une
pandas
trame de données à partir d'unda.SearchCursor
générateur et pour le calcul du ou des champs. Comme le nombre de lignes de votre table de géodatabase source double, le temps d'exécution du script ci-dessus augmente également. Bien sûr, vous devez toujours utiliser le Python 64 bits car pendant l'exécution, certaines structures de données plus grandes seront traitées en mémoire.la source
Lookup
pour créer un raster basé sur les valeurs de la nouvelle colonne. Ma méthode comportait de nombreuses étapes inutiles et un flux de travail inefficace, j'aurais dû le mentionner dans ma question d'origine. Vivre et apprendre. Je vais cependant essayer votre script plus tard cette semaine.