ক্ষেত্রের মধ্যে সবচেয়ে কম সংখ্যক সন্ধানের অজগরটির কি আরও দ্রুত উপায় আছে?


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? ডেস্কটপ.কার্গিস.com
en/

হ্যাঁ. আমাকে কোথাও শুরু করতে হবে এবং খুব কমই আরকি দিয়ে কোনও প্রোগ্রামিং করতে হবে। এটি দুর্দান্ত যে এত লোক এতগুলি পদ্ধতির পরামর্শ দিতে সক্ষম। এটি নতুন জিনিস শেখার সেরা উপায়।
রবার্ট বাকলে

min_val = min([i[0] for i in arcpy.da.SearchCursor(fc,Xfield)])
বেরু

উত্তর:


15

আমি বেশ কয়েকটি জিনিস দেখতে পাচ্ছি যা আপনার স্ক্রিপ্টটি ধীর করে দিতে পারে। যে জিনিসটি সম্ভবত খুব ধীর হয়ে উঠছে তা হ'ল arcpy.CalculateField_management()ফাংশন। আপনার একটি কার্সার ব্যবহার করা উচিত, এটি বেশ কয়েক মাপের দ্রুত গতিতে চলে আসবে। এছাড়াও, আপনি বলেছিলেন যে আপনি আর্কজিআইএস ডেস্কটপ 10.3.1 ব্যবহার করছেন তবে আপনি পুরানো আরকজিআইএস 10.0 স্টাইলের কার্সার ব্যবহার করছেন, এটিও অনেক ধীর are

এমনকি 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] = (আর [0] - মান) / 20.0 প্রকারের ত্রুটি: অসমর্থিত অপরেন্দ্র প্রকারের জন্য:: 'ননটাইপ' এবং 'ইনট'
রবার্ট

এর ঠিক অর্থ আপনার নিজের মধ্যে কিছু নাল মান রয়েছে "XKoordInt"। আমার সম্পাদনা দেখুন, আপনাকে যা করতে হবে তা হ'ল নালাগুলি।
ক্রমাক্কি

2
সঙ্গে সাবধান range। আরকজিআইএস এখনও পাইথন ২.7 ব্যবহার করে, সুতরাং এটি একটি প্রদান করে list। তবে 3.x এ, rangeএটি নিজস্ব বিশেষ ধরণের অবজেক্ট যা অপ্টিমাইজেশন থাকতে পারে। আরও নির্ভরযোগ্য পরীক্ষা হবে min(list(range(200000)))যা নিশ্চিত করবে যে আপনি সরল তালিকার সাথে কাজ করছেন। timeitপারফরম্যান্স পরীক্ষার জন্য মডিউলটি ব্যবহার করার বিষয়টিও বিবেচনা করুন ।
jpmc26

আপনি তালিকার চেয়ে সেটগুলি ব্যবহার করে সম্ভবত আরও কিছু সময় অর্জন করতে পারেন। এইভাবে আপনি সদৃশ মান সংরক্ষণ করছেন না এবং আপনি কেবল অনন্য মানগুলিতে সন্ধান করছেন।
ফেজার

@ ফেজার এটি বিতরণের উপর নির্ভর করে। সমস্ত মান হ্যাশ করার ব্যয়কে ছাড়িয়ে যাওয়ার জন্য এবং নির্মাণের সময় প্রত্যেকে সেটটিতে রয়েছে কিনা তা খতিয়ে দেখার জন্য পর্যাপ্ত নির্ভুল নকল থাকতে হবে। উদাহরণস্বরূপ, যদি কেবল 1% সদৃশ হয় তবে এটি সম্ভবত ব্যয়ের উপযুক্ত নয়। এছাড়াও মনে রাখবেন যে মানটি যদি ভাসমান বিন্দু হয় তবে অনেকগুলি যথাযথ সদৃশ হওয়ার সম্ভাবনা নেই।
jpmc26

1

@ ক্রমাক্কি যেমন উল্লেখ করেছেন, ধীর অংশটি সম্ভবত গণনা ক্ষেত্রের পদ্ধতির কারণে। অন্যান্য উপযুক্ত সমাধানের বিকল্প হিসাবে এবং ধরে নিই যে আপনি আপনার ডেটা সংরক্ষণ করার জন্য জিওডাটাবেস ব্যবহার করছেন, আপনি আপডেট কার্সার করার আগে আরোহণের ক্রম সাজানোর জন্য অর্ডার বাই এসকিএল কমান্ডটি ব্যবহার করতে পারেন।

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()। পারফরম্যান্স লাভ দেখানোর জন্য আমি এটি আমার গতির পরীক্ষায়ও অন্তর্ভুক্ত করব।
ক্রমাক্কি

আমি কোথায় আগ্রহী তা জানতে আগ্রহী হব। অতিরিক্ত এসকিএল অপারেশনগুলি এটি ধীর করে দিলে আমি অবাক হব না।
dslamb

2
সময় বেঞ্চমার্ক যোগ করা হয়েছে, আমার সম্পাদনা দেখুন। এবং আমি মনে করি আপনি সঠিক ছিলেন, বর্গক্ষেত্রটি কিছু অতিরিক্ত ওভারহেড যুক্ত করেছিল বলে মনে হয়েছিল তবে এটি পুরো কারেন্টটি কয়েক 0.56সেকেন্ডের মধ্যে পুরো তালিকার মধ্য দিয়ে চলেছে , যা আমি আশা করি পারফরম্যান্স লাভের মতো বেশি নয়।
ক্রমাক্কি

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()উভয় গতি এবং succinctness জন্য)। তারপরে জেনারেটর এক্সপ্রেশন থেকে ন্যূনতম মানটি এক 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-st সূত্র /এসরির
হারুন

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। আমি মনে করি না যে এখানেই তার বাধা ঘটছে bet ক্ষেত্রের ক্যালকুলেটর বনাম একটি নতুন শৈল কার্সারের সময় দ্বারা যাচাই করা যেতে পারে।
ক্রমাচকি

1
আসলে আমি সময়গুলির সাথেও আগ্রহী হব :) তবে এটি মূল কোডটিতে একটি সহজ প্রতিস্থাপন এবং তাই দ্রুত পরীক্ষা করা হয়েছে।
ম্যাট

আমি জানি আমি কার্সার বনাম ফিল্ড ক্যালকুলেটরের কিছুক্ষণ আগে কিছু বেনমার্ক পরীক্ষা করেছি। আমি আরও একটি পরীক্ষা করব এবং আমার উত্তরে আমার ফলাফলগুলি প্রতিবেদন করব। আমি মনে করি পুরানো বনাম নতুন কার্সারের গতিও দেখানো ভাল।
ক্রমাক্কি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.