क्या किसी क्षेत्र में सबसे छोटी संख्या खोजने के अजगर में एक तेज़ तरीका है?


10

आर्कगिस डेस्कटॉप 10.3.1 का उपयोग करते हुए मेरे पास एक स्क्रिप्ट है जो किसी सूची में मानों को जोड़ने के लिए एक खोज कर्सर का उपयोग करती है और फिर सबसे छोटे पूर्णांक को खोजने के लिए मिनट () का उपयोग करती है। चर का उपयोग एक स्क्रिप्ट में किया जाता है। फ़ीचर क्लास में 200,000 पंक्तियाँ हैं और स्क्रिप्ट को पूरा होने में बहुत लंबा समय लगता है। क्या यह जल्दी करने का कोई तरीका है? फिलहाल मुझे लगता है कि मैं सिर्फ समय की लंबाई के कारण स्क्रिप्ट लिखने के बजाय इसे हाथ से करूंगा।

import arcpy
fc = arcpy.env.workspace = arcpy.GetParameterAsText(0)
Xfield = "XKoordInt"
cursor = arcpy.SearchCursor(fc)
ListVal = []
for row in cursor:
    ListVal.append(row.getValue(Xfield))
value = min(ListVal)-20
print value
expression = "(!XKoordInt!-{0})/20".format(value)
arcpy.CalculateField_management (fc, "Matrix_Z" ,expression, "PYTHON")

मुझे लगता है कि एक तेजी से कोई अजगर तरीका यह है कि आप कम से पर काम करने लग रहा था है gis.stackexchange.com/q/197873/115
PolyGeo

किसी भी कारण से आप उपयोग नहीं कर रहे हैं arcpy.Statistics_analysis? Desktop.arcgis.com/en/arcmap/10.3/tools/analysis-toolbox/…
बेरेंड

हाँ। मुझे कहीं से शुरू करना है और केवल बहुत कम ही है जो कि आर्कपी के साथ कोई प्रोग्रामिंग करता है। यह शानदार है कि इतने सारे लोग इतने दृष्टिकोणों का सुझाव देने में सक्षम हैं। नई चीजों को सीखने का यह सबसे अच्छा तरीका है।
रॉबर्ट बकले

min_val = min([i[0] for i in arcpy.da.SearchCursor(fc,Xfield)])
बेरा

जवाबों:


15

मैं कई चीजें देख सकता हूं जो आपकी स्क्रिप्ट को धीमा कर सकती हैं। जिस चीज की संभावना बहुत धीमी है, वह है arcpy.CalculateField_management()फंक्शन। आपको एक कर्सर का उपयोग करना चाहिए, यह तेजी से कई परिमाणों द्वारा करेगा। इसके अलावा, आपने कहा था कि आप आर्कगिस डेस्कटॉप 10.3.1 का उपयोग कर रहे हैं, लेकिन आप पुराने आर्कजीएस 10.0 शैली के कर्सर का उपयोग कर रहे हैं, जो बहुत धीमे हैं।

200K की एए सूची पर भी मिन () ऑपरेशन बहुत जल्दी होगा। आप इस छोटे स्निपेट को चलाकर इसे सत्यापित कर सकते हैं; यह पलक झपकते ही होता है:

>>> min(range(200000)) # will return 0, but is still checking a list of 200,000 values very quickly

देखें कि क्या यह कोई तेज़ है:

import arcpy
fc = arcpy.env.workspace = arcpy.GetParameterAsText(0)
Xfield = "XKoordInt"
with arcpy.da.SearchCursor(fc, [Xfield]) as rows:
    ListVal = [r[0] for r in rows]

value = min(ListVal) - 20
print value

# now update
with arcpy.da.UpdateCursor(fc, [Xfield, 'Matrix_Z']) as rows:
    for r in rows:
        if r[0] is not None:
            r[1] = (r[0] - value) / 20.0
            rows.updateRow(r)

संपादित करें:

मैंने कुछ समय परीक्षण चलाए और जैसा कि मुझे संदेह था, क्षेत्र कैलकुलेटर ने नए स्टाइल कर्सर के रूप में लगभग दो बार लिया। दिलचस्प बात यह है कि पुराने स्टाइल कर्सर फील्ड कैलकुलेटर की तुलना में ~ 3x धीमा था। मैंने 200,000 यादृच्छिक अंक बनाए और समान फ़ील्ड नामों का उपयोग किया।

प्रत्येक फ़ंक्शन को समय के लिए एक डेकोरेटर फ़ंक्शन का उपयोग किया गया था (सेटअप में कुछ मामूली ओवरहेड हो सकता है और फ़ंक्शन को फाड़ सकता है, इसलिए शायद टाइमपेट मॉड्यूल स्निपेट्स का परीक्षण करने के लिए थोड़ा अधिक सटीक होगा)।

यहाँ परिणाम हैं:

Getting the values with the old style cursor: 0:00:19.23 
Getting values with the new style cursor: 0:00:02.50 
Getting values with the new style cursor + an order by sql statement: 0:00:00.02

And the calculations: 

field calculator: 0:00:14.21 
old style update cursor: 0:00:42.47 
new style cursor: 0:00:08.71

और यहाँ वह कोड है जिसका मैंने उपयोग किया था ( timeitडेकोरेटर का उपयोग करने के लिए व्यक्तिगत कार्यों के लिए सब कुछ तोड़ दिया ):

import arcpy
import datetime
import sys
import os

def timeit(function):
    """will time a function's execution time
    Required:
        function -- full namespace for a function
    Optional:
        args -- list of arguments for function
        kwargs -- keyword arguments for function
    """
    def wrapper(*args, **kwargs):
        st = datetime.datetime.now()
        output = function(*args, **kwargs)
        elapsed = str(datetime.datetime.now()-st)[:-4]
        if hasattr(function, 'im_class'):
            fname = '.'.join([function.im_class.__name__, function.__name__])
        else:
            fname = function.__name__
        print'"{0}" from {1} Complete - Elapsed time: {2}'.format(fname, sys.modules[function.__module__], elapsed)
        return output
    return wrapper

@timeit
def get_value_min_old_cur(fc, field):
    rows = arcpy.SearchCursor(fc)
    return min([r.getValue(field) for r in rows])

@timeit
def get_value_min_new_cur(fc, field):
    with arcpy.da.SearchCursor(fc, [field]) as rows:
        return min([r[0] for r in rows])

@timeit
def get_value_sql(fc, field):
    """good suggestion to use sql order by by dslamb :) """
    wc = "%s IS NOT NULL"%field
    sc = (None,'Order By %s'%field)
    with arcpy.da.SearchCursor(fc, [field]) as rows:
        for r in rows:
            # should give us the min on the first record
            return r[0]

@timeit
def test_field_calc(fc, field, expression):
    arcpy.management.CalculateField(fc, field, expression, 'PYTHON')

@timeit
def old_cursor_calc(fc, xfield, matrix_field, value):
    wc = "%s IS NOT NULL"%xfield
    rows = arcpy.UpdateCursor(fc, where_clause=wc)
    for row in rows:
        if row.getValue(xfield) is not None:

            row.setValue(matrix_field, (row.getValue(xfield) - value) / 20)
            rows.updateRow(row)

@timeit
def new_cursor_calc(fc, xfield, matrix_field, value):
    wc = "%s IS NOT NULL"%xfield
    with arcpy.da.UpdateCursor(fc, [xfield, matrix_field], where_clause=wc) as rows:
        for r in rows:
            r[1] = (r[0] - value) / 20
            rows.updateRow(r)


if __name__ == '__main__':
    Xfield = "XKoordInt"
    Mfield = 'Matrix_Z'
    fc = r'C:\Users\calebma\Documents\ArcGIS\Default.gdb\Random_Points'

    # first test the speed of getting the value
    print 'getting value tests...'
    value = get_value_min_old_cur(fc, Xfield)
    value = get_value_min_new_cur(fc, Xfield)
    value = get_value_sql(fc, Xfield)

    print '\n\nmin value is {}\n\n'.format(value)

    # now test field calculations
    expression = "(!XKoordInt!-{0})/20".format(value)
    test_field_calc(fc, Xfield, expression)
    old_cursor_calc(fc, Xfield, Mfield, value)
    new_cursor_calc(fc, Xfield, Mfield, value)

और अंत में, यह वही है जो वास्तविक प्रिंट आउट मेरे कंसोल से था।

>>> 
getting value tests...
"get_value_min_old_cur" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:19.23
"get_value_min_new_cur" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:02.50
"get_value_sql" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:00.02


min value is 5393879


"test_field_calc" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:14.21
"old_cursor_calc" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:42.47
"new_cursor_calc" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:08.71
>>> 

संपादित करें 2: बस कुछ अपडेट किए गए परीक्षणों को पोस्ट किया, मुझे अपने timeitफ़ंक्शन के साथ एक मामूली दोष मिला ।


r [0] = (r [0] - मूल्य) / 20.0 टाइप करें: असमर्थित ऑपरेंड प्रकार (ओं) के लिए -: 'कोई नहीं' और 'int'
रॉबर्ट बकले

इसका मतलब है कि आपके पास अपने में कुछ अशक्त मूल्य हैं "XKoordInt"। मेरा संपादन देखें, आपको केवल नल को छोड़ना है।
crmackey

2
से सावधान रहें range। आर्कगिस अभी भी पायथन 2.7 का उपयोग करता है, इसलिए यह एक रिटर्न देता है list। लेकिन 3.x में, rangeअपनी स्वयं की विशेष प्रकार की वस्तु है जिसमें अनुकूलन हो सकते हैं। एक अधिक विश्वसनीय परीक्षण होगा min(list(range(200000))), जो यह सुनिश्चित करेगा कि आप एक सादे सूची के साथ काम कर रहे हैं। timeitप्रदर्शन परीक्षण के लिए मॉड्यूल का उपयोग करने पर भी विचार करें ।
jpmc26

आप शायद सूचियों के बजाय सेट का उपयोग करके कुछ और समय प्राप्त कर सकते हैं। इस तरह आप डुप्लिकेट मानों को संग्रहीत नहीं कर रहे हैं, और आप केवल अनन्य मानों पर खोज कर रहे हैं।
Fezter

@ फ्रीजर यह वितरण पर निर्भर करता है। निर्माण के दौरान सेट में प्रत्येक में होने पर सभी मूल्यों और हैशिंग की लागत को पछाड़ने के लिए पर्याप्त सटीक डुप्लिकेट होना चाहिए। उदाहरण के लिए, यदि केवल 1% की नकल की जाती है, तो यह संभवतः लागत के लायक नहीं है। यह भी ध्यान दें कि यदि मान फ़्लोटिंग पॉइंट है, तो कई सटीक डुप्लिकेट होने की संभावना नहीं है।
jpmc26

1

जैसा कि @crmackey बताते हैं, धीमा भाग संभवतः गणना क्षेत्र विधि के कारण है। अन्य उपयुक्त समाधानों के विकल्प के रूप में, और यह मानकर कि आप अपने डेटा को संग्रहीत करने के लिए एक जियोडैटेबेस का उपयोग कर रहे हैं, आप अपडेट कर्सर करने से पहले आरोही क्रम में सॉर्ट करने के लिए ऑर्डर बाय एसक्यूएल कमांड का उपयोग कर सकते हैं।

start = 0
Xfield = "XKoordInt"
minValue = None
wc = "%s IS NOT NULL"%Xfield
sc = (None,'Order By %s'%Xfield)
with arcpy.da.SearchCursor(fc, [Xfield],where_clause=wc,sql_clause=sc) as uc:
    for row in uc:
        if start == 0:
            minValue = row[0]
            start +=1
        row[0] = (row[0] - value) / 20.0
        uc.updateRow(row)

इस स्थिति में जहां खंड क्वेरी करने से पहले नल को हटा देता है, या आप दूसरे उदाहरण का उपयोग कर सकते हैं जो अपडेट करने से पहले किसी के लिए जांच नहीं करता है।


अच्छा! पहले रिकॉर्ड को आरोही और हथियाने के द्वारा आदेश का उपयोग करना निश्चित रूप से सभी मूल्यों को प्राप्त करने और फिर खोजने की तुलना में तेजी से होगा min()। मैं इसे अपनी गति परीक्षणों में शामिल करूँगा और साथ ही प्रदर्शन लाभ दिखाऊंगा।
crmackey

मैं यह देखने के लिए उत्सुक हूँ कि यह कहाँ पर है। मुझे आश्चर्य नहीं होगा यदि अतिरिक्त एसक्यूएल संचालन इसे धीमा कर दे।
dslamb

2
समय-सीमा के मानक जोड़े गए हैं, मेरा संपादन देखें। और मुझे लगता है कि आप सही थे, एसक्यूएल कुछ अतिरिक्त ओवरहेड जोड़ने के लिए लग रहा था, लेकिन इसने कर्सर को कुछ 0.56सेकंड के दौरान पूरी सूची के माध्यम से बाहर कर दिया , जो कि प्रदर्शन के लाभ के रूप में उतना नहीं है जितना मुझे उम्मीद थी।
crmackey

1

आप इस तरह के मामलों में सुन्न का उपयोग कर सकते हैं, हालांकि यह अधिक स्मृति गहन होगा।

जब आप डेटा को एक सुदूर सरणी में लोड करते हैं और फिर वापस डेटा स्रोत पर वापस जाते हैं, तब भी आपको एक बोतल गर्दन मिलेगी, लेकिन मैंने पाया है कि बड़े डेटा स्रोतों के साथ प्रदर्शन अंतर बेहतर (संख्यात्मक के पक्ष में) है, खासकर यदि आपको एकाधिक की आवश्यकता है आँकड़े / गणना .:

import arcpy
import numpy as np
fc = arcpy.env.workspace = arcpy.GetParameterAsText(0)
Xfield = "XKoordInt"

allvals = arcpy.da.TableToNumPyArray(fc,['OID@',Xfield])
value = allvals[Xfield].min() - 20

print value

newval = np.zeros(allvals.shape,dtype=[('id',int),('Matrix_Z',int)])
newval['id'] = allvals['OID@']
newval['Matrix_Z'] = (allvals[Xfield] - value) / 20

arcpy.da.ExtendTable(fc,'OBJECTID',newval,'id',False)

1

तालिका को आरोही क्यों नहीं बनाया जाता है, फिर पहली पंक्ति के लिए मान को हथियाने के लिए खोज कर्सर का उपयोग करें? http://pro.arcgis.com/en/pro-app/tool-reference/data-management/sort.htm

import arcpy
workspace = r'workspace\file\path'
arcpy.env.workspace = workspace

input = "input_data"
sort_table = "sort_table"
sort_field = "your field"

arcpy.Sort_management (input, sort_table, sort_field)

min_value = 0

count= 0
witha arcpy.da.SearchCursor(input, [sort_field]) as cursor:
    for row in cursor:
        count +=1
        if count == 1: min_value +=row[0]
        else: break
del cursor

1

मैं SearchCursorएक जनरेटर अभिव्यक्ति (यानी min()) में गति और सफलता दोनों के लिए लपेटूंगा। फिर एक daप्रकार में जनरेटर अभिव्यक्ति से न्यूनतम मूल्य को शामिल करें UpdateCursor। कुछ इस प्रकार है:

import arcpy

fc = r'C:\path\to\your\geodatabase.gdb\feature_class'

minimum_value = min(row[0] for row in arcpy.da.SearchCursor(fc, 'some_field')) # Generator expression

with arcpy.da.UpdateCursor(fc, ['some_field2', 'some_field3']) as cursor:
    for row in cursor:
        row[1] = (row[0] - (minimum_value - 20)) / 20 # Perform the calculation
        cursor.updateRow(row)

SearchCursorजब आप इसके साथ कर रहे हैं तो क्या इसे बंद नहीं किया जाना चाहिए ?
jpmc26

1
@ jpmc26 कर्सर के पूरा होने पर एक कर्सर छोड़ा जा सकता है। स्रोत (कर्सर और लॉकिंग): pro.arcgis.com/en/pro-app/arcpy/get-started/… । ESRI से एक और उदाहरण है (उदाहरण के 2 देखें): pro.arcgis.com/en/pro-app/arcpy/data-access/...
हारून

0

आपके लूप में आपके पास दो फ़ंक्शन संदर्भ हैं जो प्रत्येक पुनरावृत्ति के लिए पुनर्मूल्यांकित हैं।

for row in cursor: ListVal.append(row.getValue(Xfield))

लूप के बाहर संदर्भ होने के लिए यह तेज़ (लेकिन थोड़ा अधिक जटिल) होना चाहिए:

getvalue = row.getValue
append = ListVal.append

for row in cursor:
    append(getvalue(Xfield))

क्या यह वास्तव में इसे धीमा नहीं करेगा? आप वास्तव append()में listडेटाटाइप की अंतर्निहित विधि के लिए एक नया अलग संदर्भ बना रहे हैं । मुझे नहीं लगता कि यह वह जगह है जहां उसकी अड़चन हो रही है, मैं शर्त लगाता हूं कि गणना फ़ील्ड फ़ंक्शन अपराधी है। यह क्षेत्र कैलकुलेटर बनाम एक नए शैली कर्सर के समय से सत्यापित किया जा सकता है।
crmackey

1
वास्तव में मुझे समय के साथ-साथ :) में दिलचस्पी होगी: लेकिन यह मूल कोड में एक आसान प्रतिस्थापन है और इसलिए तेजी से जाँच की गई है।
मैट

मुझे पता है कि मैंने कुछ बेंचमार्क टेस्टिंग कुछ समय पहले ही वर्कर्स बनाम फील्ड कैलकुलेटर पर किए थे। मैं एक और परीक्षण करूंगा और अपने उत्तर में अपने निष्कर्षों को रिपोर्ट करूंगा। मुझे लगता है कि पुरानी बनाम नई कर्सर गति को भी दिखाना अच्छा होगा।
crmackey
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.