NumPy: একযোগে সর্বোচ্চ () এবং সর্বনিম্ন () এর জন্য ফাংশন


109

numpy.amax () একটি অ্যারেতে সর্বাধিক মান সন্ধান করবে এবং numpy.amin () নূন্যতম মানের জন্য একই কাজ করবে। আমি যদি সর্বোচ্চ এবং সর্বনিম্ন উভয়ই সন্ধান করতে চাই তবে আমাকে উভয় ফাংশন কল করতে হবে, যার জন্য দু'বার (খুব বড়) অ্যারে পেরিয়ে যাওয়া দরকার, যা ধীর বলে মনে হচ্ছে।

নম্পি এপিআইতে এমন কোনও ফাংশন রয়েছে যা কেবলমাত্র একক পাস দিয়ে ডেটা দিয়ে সর্বোচ্চ এবং সর্বনিম্ন উভয়কেই খুঁজে পায়?


1
খুব বড় কত বড়? আমি যদি কিছুটা সময় পাই তবে আমি একটি amaxamin
ফরট্রান

1
আমি স্বীকৃতি দেব "খুব বড়" বিষয়গত। আমার ক্ষেত্রে, আমি অ্যারেগুলি নিয়ে কথা বলছি যা কয়েক জিবি।
স্টুয়ার্ট বার্গ

এটা বেশ বড়। এটি ফরআরতে গণনা করার জন্য আমি একটি উদাহরণ দিয়েছি (আপনি ফরট্রান না জানলেও কোডটি বোঝা খুব সহজ হওয়া উচিত)। এটি অদৃশ্য মাধ্যমে চলমান বনাম বনাম থেকে এটি চালিয়ে যাওয়া একটি পার্থক্য করে। (সম্ভবত, আপনি সি থেকে একই পারফরম্যান্স পেতে সক্ষম হবেন ...) আমি নিশ্চিত নই - আমি মনে করি যে আমার ফাংশনগুলি তার চেয়ে আরও বেশি ভাল সম্পাদন করে কেন সে সম্পর্কে মন্তব্য করার জন্য আমাদের একটি নগ্ন দেবের প্রয়োজন হবে ...
মিলিলসন

অবশ্যই এটি কোনও অভিনব ধারণা নয়। উদাহরণস্বরূপ, বুস্ট মিনম্যাক্স লাইব্রেরি (সি ++) আমার সন্ধান করা অ্যালগরিদমের একটি বাস্তবায়ন সরবরাহ করে।
স্টুয়ার্ট বার্গ

3
জিজ্ঞাসিত প্রশ্নের উত্তর আসলেই নয়, তবে সম্ভবত এই থ্রেডে থাকা মানুষের আগ্রহ। minmaxসংখ্যায় গ্রন্থাগারে যুক্ত করার বিষয়ে নম্পপিকে জিজ্ঞাসা করলেন ( github.com/numpy/numpy/issues/9836 )।
জাকিরখাম

উত্তর:


49

নম্পি এপিআইতে এমন কোনও ফাংশন রয়েছে যা কেবলমাত্র একক পাস দিয়ে ডেটা দিয়ে সর্বোচ্চ এবং সর্বনিম্ন উভয়কেই খুঁজে পায়?

না। এই লেখার সময়, এই জাতীয় কোনও কার্যকারিতা নেই। (এবং হ্যাঁ, যদি সেখানে ছিল যেমন একটি ফাংশন, তার কর্মক্ষমতা হবে উল্লেখযোগ্যভাবে ভালো কলিং চেয়ে numpy.amin()এবং numpy.amax()ধারাবাহিকভাবে বৃহৎ শৃঙ্খলার।)


31

আমি মনে করি না যে অ্যারের উপর দিয়ে দুইবার পাস করা একটি সমস্যা। নিম্নলিখিত সিউডো কোড বিবেচনা করুন:

minval = array[0]
maxval = array[0]
for i in array:
    if i < minval:
       minval = i
    if i > maxval:
       maxval = i

এখানে কেবলমাত্র 1 টি লুপ রয়েছে, এখনও 2 টি চেক রয়েছে। (প্রতিটি 1 টি পরীক্ষা করে 2 টি লুপের পরিবর্তে)। সত্যিই আপনি কেবলমাত্র 1 টি লুপের ওভারহেড সংরক্ষণ করবেন। অ্যারেগুলি যদি আপনি যেমন বলেন বড় হয় তবে প্রকৃত লুপের কাজের বোঝার তুলনায় ওভারহেড ছোট is (দ্রষ্টব্য যে এগুলি সমস্ত সিতে প্রয়োগ করা হয়েছে, সুতরাং লুপগুলি যাইহোক কম বেশি কম বিনামূল্যে)।


সম্পাদনা করুন আপনারা যারা 4 জনকে উজ্জীবিত করেছেন এবং আমার প্রতি বিশ্বাস রেখেছিলেন তাদের জন্য দুঃখিত। আপনি অবশ্যই এটি অনুকূলিত করতে পারেন।

এখানে কয়েকটি ফোরট্রান কোড যা পাইথন মডিউলের মাধ্যমে সংকলন করা যেতে পারে f2py(সম্ভবত কোনও Cythonগুরু আসতে পারেন এবং এটি একটি অনুকূলিত সি সংস্করণের সাথে তুলনা করতে পারেন ...):

subroutine minmax1(a,n,amin,amax)
  implicit none
  !f2py intent(hidden) :: n
  !f2py intent(out) :: amin,amax
  !f2py intent(in) :: a
  integer n
  real a(n),amin,amax
  integer i

  amin = a(1)
  amax = a(1)
  do i=2, n
     if(a(i) > amax)then
        amax = a(i)
     elseif(a(i) < amin) then
        amin = a(i)
     endif
  enddo
end subroutine minmax1

subroutine minmax2(a,n,amin,amax)
  implicit none
  !f2py intent(hidden) :: n
  !f2py intent(out) :: amin,amax
  !f2py intent(in) :: a
  integer n
  real a(n),amin,amax
  amin = minval(a)
  amax = maxval(a)
end subroutine minmax2

এর মাধ্যমে সংকলন করুন:

f2py -m untitled -c fortran_code.f90

এবং এখন আমরা এমন জায়গায় রয়েছি যেখানে আমরা এটি পরীক্ষা করতে পারি:

import timeit

size = 100000
repeat = 10000

print timeit.timeit(
    'np.min(a); np.max(a)',
    setup='import numpy as np; a = np.arange(%d, dtype=np.float32)' % size,
    number=repeat), " # numpy min/max"

print timeit.timeit(
    'untitled.minmax1(a)',
    setup='import numpy as np; import untitled; a = np.arange(%d, dtype=np.float32)' % size,
    number=repeat), '# minmax1'

print timeit.timeit(
    'untitled.minmax2(a)',
    setup='import numpy as np; import untitled; a = np.arange(%d, dtype=np.float32)' % size,
    number=repeat), '# minmax2'

ফলাফলগুলি আমার জন্য কিছুটা হতবাক are

8.61869883537 # numpy min/max
1.60417699814 # minmax1
2.30169081688 # minmax2

আমাকে বলতে হবে, আমি এটি পুরোপুরি বুঝতে পারি না। তুলনা করা np.minবনাম minmax1এবং minmax2এখনও একটি হেরে যাওয়া যুদ্ধ, সুতরাং এটি কেবল একটি স্মৃতি বিষয় নয় ...

নোটস - একটি ফ্যাক্টর দ্বারা আকার বৃদ্ধি 10**aএবং 10**a(সমস্যার আকার স্থির রাখা) এর একটি গুণক দ্বারা পুনরাবৃত্তি হ্রাস কার্যকারিতা পরিবর্তন করে, তবে একটি আপাত ধারাবাহিক উপায়ে নয় যা দেখায় যে মেমরির কার্যকারিতা এবং ফাংশন কল ওভারহেডের মধ্যে কিছু ইন্টারপ্লে রয়েছে পাইথন। এমনকি minপ্রায় 2 টির একটি ফ্যাক্টর দ্বারা নিরস্তকে বীট বিট করতে একটি সাধারণ প্রয়োগের তুলনা করা ...


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

4
এটি মোটামুটি সত্য নয়, প্রায় অর্ধেক তত দ্রুত, কারণ এই ধরণের অ্যারেগুলির সাথে মেমরির গতি সাধারণত সীমাবদ্ধ ফ্যাক্টর, তাই এটি অর্ধেক দ্রুত হতে পারে ...
সেবার্গ

3
আপনার সবসময় দুটি চেকের দরকার হয় না। যদি i < minvalএটি সত্য হয় তবে i > maxvalসর্বদা মিথ্যা, সুতরাং যখন দ্বিতীয়টি ifএকটি দ্বারা প্রতিস্থাপিত করা হয় তখন আপনাকে কেবল পুনরাবৃত্তি প্রতি 1.5 টি চেক করতে হবে elif
ফ্রেড ফু

2
ছোট নোট: আমি সন্দেহ করি সাইথন হ'ল সর্বাধিক অনুকূলিত পাইথন-কলযোগ্য সি মডিউল পাওয়ার উপায়। সিথনের লক্ষ্য হ'ল এক ধরণের টাইপ-এনোটেটেড পাইথন, যা মেশিনে অনুবাদিত সি-তে অনুবাদ করা হয়, যেখানে f2pyহ্যান্ড-কোডেড ফোর্ত্রানকে কেবল মোড়ানো হয় যাতে পাইথন এটি কল করে। একটি "ফেইরার" পরীক্ষাটি সম্ভবত হ্যান্ড-কোডিং সি এবং তারপরে f2pyপাইথনের জন্য এটি মোড়ানোর জন্য (!) ব্যবহার করা হয় । আপনি যদি সি ++ এর অনুমতি দিচ্ছেন, তবে পারফরম্যান্সের সাথে কোডিং সাবলীলতার সাথে ভারসাম্য বজায় রাখার জন্য শেড স্কিন মিট স্পট হতে পারে।
জন ওয়াই

4
নর্পিত 1.8 মিনিট এবং সর্বাধিক amd64 প্ল্যাটফর্মে ভেক্টরাইজ করা হয়েছে, আমার কোর 2ডুও নিম্পি পারফরম্যান্স পাশাপাশি এই ফরট্রান কোডে। তবে অ্যারে বড় সিপু ক্যাশের আকারের চেয়ে বেশি হলে একক পাস উপকারী।
jtaylor

23

Numpy.ptp নামে পরিচিত (সর্বাধিক-মিনিট) সন্ধানের জন্য একটি ফাংশন রয়েছে যদি এটি আপনার জন্য দরকারী:

>>> import numpy
>>> x = numpy.array([1,2,3,4,5,6])
>>> x.ptp()
5

তবে আমি মনে করি না একটি ট্র্যাভারসাল সহ নূন্যতম এবং সর্বাধিক উভয় সন্ধান করার উপায় আছে।

সম্পাদনা করুন: পিটিপি হুডের নীচে কেবল সর্বনিম্ন এবং সর্বাধিক কল করে


2
এটি বিরক্তিকর কারণ সম্ভবত ptp যেভাবে প্রয়োগ করা হয় এটি সর্বোচ্চ এবং সর্বনিম্ন ট্র্যাক রাখতে হয়!
অ্যান্ডি হেডেন

1
অথবা এটি সর্বাধিক এবং মিনিট কল করতে পারে, নিশ্চিত নয়
জেটেরেস

3
@ হাইডেন পিটিপি সক্রিয় করেছেন সর্বাধিক এবং মিনিট
জেটেরাস

1
এটি ছিল মুখোশযুক্ত-অ্যারে কোড; মূল নাদারের কোডটি সি তে হয় তবে এটি সি কোডটিও অ্যারে থেকে বারবার পুনরাবৃত্তি করে: github.com/numpy/numpy/blob/…
কেন আর্নল্ড

20

আপনি ব্যবহার করতে পারে Numba , যা একটি NumPy সচেতন গতিশীল পাইথন কম্পাইলার LLVM ব্যবহার করে। ফলাফল বাস্তবায়ন বেশ সহজ এবং পরিষ্কার:

import numpy
import numba


@numba.jit
def minmax(x):
    maximum = x[0]
    minimum = x[0]
    for i in x[1:]:
        if i > maximum:
            maximum = i
        elif i < minimum:
            minimum = i
    return (minimum, maximum)


numpy.random.seed(1)
x = numpy.random.rand(1000000)
print(minmax(x) == (x.min(), x.max()))

এটি কোনও নম্পির min() & max()প্রয়োগের চেয়ে দ্রুত হওয়া উচিত । এবং সমস্ত কোডের একক সি / ফোর্টরান লাইন না লিখে।

আপনার নিজের কর্মক্ষমতা পরীক্ষা করুন, কেননা এটি সর্বদা আপনার আর্কিটেকচার, আপনার ডেটা, আপনার প্যাকেজ সংস্করণগুলির উপর নির্ভরশীল ...


2
> এটি কোনও নম্পির ন্যূনতম () এবং সর্বাধিক () প্রয়োগের চেয়েও দ্রুত হওয়া উচিত বলে আমি মনে করি এটি সঠিক নয়। নম্পিটি দেশীয় অজগর নয় - এটি সি `` `x = numpy.random.rand (10000000) টি = সময় () আমার সীমাতে (1000): মিনিম্যাক্স (এক্স) মুদ্রণ ('নাম্বা', সময় () - টি) টি = টাইম () আই ইন রেঞ্জ (1000): x.min () x.max () মুদ্রণ ('নমপি', সময় () - টি) in `` ফলাফল: ('নাম্বা', 10.299750089645386 ) ('নমপি', 9.898081064224243)
লেখক অপাটিরা

1
@ অউথম্যান অপতিরা: হ্যাঁ, বেঞ্চমার্কগুলি সর্বদা এরকম থাকে, এজন্যই আমি বলেছিলাম যে এটি " হওয়া উচিত " (দ্রুত হওয়া উচিত ) এবং " আপনার নিজের পারফরম্যান্স পরীক্ষা করা উচিত, কারণ এটি সর্বদা আপনার আর্কিটেকচার, আপনার ডেটার উপর নির্ভরশীল ... "। আমার ক্ষেত্রে, আমি 3 টি কম্পিউটারের সাথে চেষ্টা করেছিলাম এবং একই ফলাফল পেয়েছি (নুম্বা নিম্পির চেয়ে দ্রুত ছিল), তবে আপনার কম্পিউটারে ফলাফলগুলি পৃথক হতে পারে ... আপনি যদি numbaজেআইটি-সংকলিত তা নিশ্চিত করার জন্য বেঞ্চমার্কের আগে একবার কার্য সম্পাদন করার চেষ্টা করেছিলেন? । এছাড়াও, যদি আপনি ipythonসরলতার জন্য ব্যবহার করেন তবে আমি আপনাকে %timeit whatever_code()সময় নির্বাহের পরিমাপের জন্য ব্যবহার করার পরামর্শ দিচ্ছি ।
পেক

3
@ অউথম্যান আপাতিরা: যে কোনও ক্ষেত্রে আমি এই উত্তরটি দিয়ে যা দেখানোর চেষ্টা করেছি তা হ'ল কখনও কখনও পাইথন কোড (এই ক্ষেত্রে নুম্বার সাথে জেআইটি সংকলিত) দ্রুততম সি-সংকলিত গ্রন্থাগারের তুলনায় দ্রুততর হতে পারে (কমপক্ষে আমরা একই আদেশের বিষয়ে কথা বলছি) আকারের), যা বিবেচনায় নেওয়ার কারণে আমরা খাঁটি পাইথন কোড ব্যতীত আর কিছুই লিখি না, আপনি কি তা মানছেন না? ^^
পিক

আমি সম্মত নই =) এছাড়াও, জ্যোপাইটার সংক্রান্ত পূর্ববর্তী মন্তব্যে টিপসগুলির জন্য ধন্যবাদ এবং টাইমিং কোডের বাইরে একবার ফাংশনটি সংকলন করে।
লেখক অপটিরা

1
এটি কেবলমাত্র জুড়ে গেছে, এটি ব্যবহারিক ক্ষেত্রে গুরুত্বপূর্ণ নয়, তবে elifআপনার সর্বনিম্নটিকে আপনার সর্বোচ্চের চেয়ে বড় হতে দেয়। উদাহরণস্বরূপ, দৈর্ঘ্য 1 এর অ্যারে সহ, সর্বাধিক মানটি যাই হোক না কেন, সর্বনিম্ন হবে + ইনফিনিটি। এক-অফের জন্য কোনও বড় বিষয় নয়, তবে কোনও উত্পাদন পশুর পেটে গভীরভাবে ফেলার জন্য ভাল কোড নয়।
মাইক উইলিয়ামসন

12

সাধারণভাবে আপনি একবারে দুটি উপাদান প্রক্রিয়াজাত করে কেবল ছোটকে অস্থায়ী নূন্যতম এবং অস্থায়ী সর্বাধিকের সাথে আরও বড়টির সাথে তুলনা করে মিনম্যাক্স অ্যালগরিদমের তুলনার পরিমাণ হ্রাস করতে পারেন। নিখুঁত পদ্ধতির তুলনায় গড়ে একজনের তুলনায় মাত্র 3/4 প্রয়োজন।

এটি সি বা ফোর্টরান (বা অন্য কোনও নিম্ন-স্তরের ভাষায়) প্রয়োগ করা যেতে পারে এবং পারফরম্যান্সের ক্ষেত্রে এটি প্রায় অপরাজেয় হওয়া উচিত। আমি ব্যাবহার করছি নীতিটি বর্ণনা করার জন্য এবং খুব দ্রুত, টাইপ-ইন্ডিপেন্ডেন্ট বাস্তবায়ন পেতে:

import numba as nb
import numpy as np

@nb.njit
def minmax(array):
    # Ravel the array and return early if it's empty
    array = array.ravel()
    length = array.size
    if not length:
        return

    # We want to process two elements at once so we need
    # an even sized array, but we preprocess the first and
    # start with the second element, so we want it "odd"
    odd = length % 2
    if not odd:
        length -= 1

    # Initialize min and max with the first item
    minimum = maximum = array[0]

    i = 1
    while i < length:
        # Get the next two items and swap them if necessary
        x = array[i]
        y = array[i+1]
        if x > y:
            x, y = y, x
        # Compare the min with the smaller one and the max
        # with the bigger one
        minimum = min(x, minimum)
        maximum = max(y, maximum)
        i += 2

    # If we had an even sized array we need to compare the
    # one remaining item too.
    if not odd:
        x = array[length]
        minimum = min(x, minimum)
        maximum = max(x, maximum)

    return minimum, maximum

এটি পেক যে নিখুঁত পদ্ধতির তুলনায় স্পষ্টতই দ্রুত :

arr = np.random.random(3000000)
assert minmax(arr) == minmax_peque(arr)  # warmup and making sure they are identical 
%timeit minmax(arr)            # 100 loops, best of 3: 2.1 ms per loop
%timeit minmax_peque(arr)      # 100 loops, best of 3: 2.75 ms per loop

যেমনটি প্রত্যাশা করা হয়েছে নতুন মিনম্যাক্স বাস্তবায়ন কেবল নিষ্পাপ বাস্তবায়নের সময় প্রায় 3/4 লাগে ( 2.1 / 2.75 = 0.7636363636363637)


1
আমার মেশিনে, আপনার সমাধান পিকের চেয়ে দ্রুত নয়। নুম্বা 0.33।
জন জুইঙ্ক

@ জোজনজুইনক আপনি কি আমার উত্তরে মানদণ্ডটি চালিয়েছেন? যদি তা ভাগ করে নিতে পার? তবে এটি সম্ভব: আমি আরও নতুন সংস্করণে কিছু নিবিড়তা লক্ষ্য করেছি।
এমেসিফার্ট 24'17

আমি আপনার মানদণ্ড চালিয়েছি। আপনার সমাধান এবং @ পেকের সময়গুলি বেশ কিছুটা একই ছিল (~ 2.8 এমএস)।
জন জুইনেক

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

আমি এখন অন্য একটি মেশিনে চেষ্টা করেছি (একটি মৌমাছি ওয়ার্কস্টেশন) এবং পেকের জন্য আপনার বনাম 2.6 এর জন্য 2.4 এমএস পেয়েছি। সুতরাং, একটি ছোট জয়।
জন জুইনক

11

নিম্নলিখিত পন্থাগুলি প্রদত্ত যে কোনও একটি আশা করতে পারে সেই সংখ্যা সম্পর্কে কিছু ধারণা পেতে:

import numpy as np


def extrema_np(arr):
    return np.max(arr), np.min(arr)
import numba as nb


@nb.jit(nopython=True)
def extrema_loop_nb(arr):
    n = arr.size
    max_val = min_val = arr[0]
    for i in range(1, n):
        item = arr[i]
        if item > max_val:
            max_val = item
        elif item < min_val:
            min_val = item
    return max_val, min_val
import numba as nb


@nb.jit(nopython=True)
def extrema_while_nb(arr):
    n = arr.size
    odd = n % 2
    if not odd:
        n -= 1
    max_val = min_val = arr[0]
    i = 1
    while i < n:
        x = arr[i]
        y = arr[i + 1]
        if x > y:
            x, y = y, x
        min_val = min(x, min_val)
        max_val = max(y, max_val)
        i += 2
    if not odd:
        x = arr[n]
        min_val = min(x, min_val)
        max_val = max(x, max_val)
    return max_val, min_val
%%cython -c-O3 -c-march=native -a
#cython: language_level=3, boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True, infer_types=True


import numpy as np


cdef void _extrema_loop_cy(
        long[:] arr,
        size_t n,
        long[:] result):
    cdef size_t i
    cdef long item, max_val, min_val
    max_val = arr[0]
    min_val = arr[0]
    for i in range(1, n):
        item = arr[i]
        if item > max_val:
            max_val = item
        elif item < min_val:
            min_val = item
    result[0] = max_val
    result[1] = min_val


def extrema_loop_cy(arr):
    result = np.zeros(2, dtype=arr.dtype)
    _extrema_loop_cy(arr, arr.size, result)
    return result[0], result[1]
%%cython -c-O3 -c-march=native -a
#cython: language_level=3, boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True, infer_types=True


import numpy as np


cdef void _extrema_while_cy(
        long[:] arr,
        size_t n,
        long[:] result):
    cdef size_t i, odd
    cdef long x, y, max_val, min_val
    max_val = arr[0]
    min_val = arr[0]
    odd = n % 2
    if not odd:
        n -= 1
    max_val = min_val = arr[0]
    i = 1
    while i < n:
        x = arr[i]
        y = arr[i + 1]
        if x > y:
            x, y = y, x
        min_val = min(x, min_val)
        max_val = max(y, max_val)
        i += 2
    if not odd:
        x = arr[n]
        min_val = min(x, min_val)
        max_val = max(x, max_val)
    result[0] = max_val
    result[1] = min_val


def extrema_while_cy(arr):
    result = np.zeros(2, dtype=arr.dtype)
    _extrema_while_cy(arr, arr.size, result)
    return result[0], result[1]

( extrema_loop_*()পন্থা কি প্রস্তাব করা হয় একই রকম এখানে , যখন extrema_while_*()পন্থা থেকে কোড উপর ভিত্তি করে এখানে )

নিম্নলিখিত সময়:

বিএম

ইঙ্গিত দেয় যে extrema_while_*()দ্রুততম, সঙ্গে আছে extrema_while_nb()দ্রুততম হচ্ছে। যাইহোক , এছাড়াও extrema_loop_nb()এবং extrema_loop_cy()সমাধানগুলি NumPy- কেবল পদ্ধতির (ব্যবহার np.max()এবং np.min()পৃথকভাবে) ছাড়িয়ে যায় ।

পরিশেষে, দ্রষ্টব্য যে এগুলির কোনওটিই np.min()/ np.max()(এন-ডিম সমর্থন, axisপরামিতি ইত্যাদির ক্ষেত্রে নমনীয় নয় )।

(সম্পূর্ণ কোড এখানে উপলব্ধ )


2
মনে হয় আপনি @ এনজিট (ফাস্টম্যাথ = ট্রু) ব্যবহার করে অতিরিক্ত 10% গতি অর্জন করতে পারবেনextrema_while_nb
আরজেনিসলিয়ন

10

কেউ নাম্পি.পারসেন্টাইল উল্লেখ করেনি , তাই আমি ভেবেছিলাম আমি করব would আপনি যদি [0, 100]পারসেন্টাইলের জন্য জিজ্ঞাসা করেন তবে এটি আপনাকে দুটি উপাদানগুলির একটি অ্যারে দেবে, ন্যূনতম (0 তম পার্সেন্টাইল) এবং সর্বাধিক (100 তম শতাংশ)।

তবে এটি অপের উদ্দেশ্য পূরণ করে না: এটি পৃথকভাবে নূন্যতম এবং সর্বোচ্চের চেয়ে দ্রুত নয়। যে সম্ভবত কিছু যন্ত্রপাতি অ-চরম শতকরা (ক কঠিন সমস্যা, যার জন্য সম্ভব হবে কারণে এর উচিত বেশি সময় লাগতে)।

In [1]: import numpy

In [2]: a = numpy.random.normal(0, 1, 1000000)

In [3]: %%timeit
   ...: lo, hi = numpy.amin(a), numpy.amax(a)
   ...: 
100 loops, best of 3: 4.08 ms per loop

In [4]: %%timeit
   ...: lo, hi = numpy.percentile(a, [0, 100])
   ...: 
100 loops, best of 3: 17.2 ms per loop

In [5]: numpy.__version__
Out[5]: '1.14.4'

নম্পির ভবিষ্যতের সংস্করণটি যদি বিশেষভাবে [0, 100]অনুরোধ করা হয় তবে সাধারণ পারসেন্টাইল গণনা এড়াতে একটি বিশেষ ক্ষেত্রে রাখতে পারে । ইন্টারফেসে কিছু না জুড়েই, নম্পিকে এক কলে ন্যূনতম এবং সর্বোচ্চ সর্বাধিক জিজ্ঞাসা করার একটি উপায় রয়েছে (গ্রহণযোগ্য উত্তরে যা বলা হয়েছিল তার বিপরীতে), তবে লাইব্রেরির মানক প্রয়োগটি এ ক্ষেত্রে এটি গ্রহণের সুবিধা গ্রহণ করে না উপযুক্ত।


9

এটি একটি পুরানো থ্রেড, তবে যাইহোক, যদি কেউ কখনও আবার এটি দেখেন ...

একসাথে সর্বনিম্ন এবং সর্বাধিক সন্ধান করার সময়, তুলনার সংখ্যা হ্রাস করা সম্ভব। এটি যদি ভাসমান হয় তবে আপনি তুলনা করছেন (যা আমি এটি অনুমান করি) এটি আপনাকে কিছুটা সময় সাশ্রয় করতে পারে, যদিও এটি গণনার জটিলতা নয়।

(পাইথন কোড) এর পরিবর্তে:

_max = ar[0]
_min=  ar[0]
for ii in xrange(len(ar)):
    if _max > ar[ii]: _max = ar[ii]
    if _min < ar[ii]: _min = ar[ii]

আপনি প্রথমে অ্যারেতে দুটি সংলগ্ন মান তুলনা করতে পারেন, এবং তারপরে কেবল বর্তমান সর্বনিম্নের তুলনায় ছোটটি এবং বর্তমানের সর্বাধিকের তুলনায় বৃহত্তর তুলনা করতে পারেন:

## for an even-sized array
_max = ar[0]
_min = ar[0]
for ii in xrange(0, len(ar), 2)):  ## iterate over every other value in the array
    f1 = ar[ii]
    f2 = ar[ii+1]
    if (f1 < f2):
        if f1 < _min: _min = f1
        if f2 > _max: _max = f2
    else:
        if f2 < _min: _min = f2
        if f1 > _max: _max = f1

এখানে কোডটি পাইথনে লেখা হয়েছে, স্পষ্টতই আপনি গ বা ফোর্টরান বা সিথন ব্যবহার করতেন, তবে এইভাবে আপনি পুনরাবৃত্তি প্রতি 3 টি তুলনা করেন, লেন (আর) / 2 পুনরাবৃত্তি সহ 3/2 * লেন (আর) তুলনা করে giving এর বিপরীতে, তুলনাটি "স্পষ্ট উপায়" করে আপনি পুনরাবৃত্তি প্রতি দুটি তুলনা করেন, যার ফলে 2 * লেন (আর) তুলনা হয়। তুলনা সময় 25% সংরক্ষণ করে।

হতে পারে যে কেউ একদিন এটি দরকারী খুঁজে পেতে হবে।


6
আপনি কি এই বেঞ্চমার্ক করেছেন? আধুনিক x86 হার্ডওয়্যারটিতে আপনার প্রথম মেশিনে নূন্যতম এবং সর্বাধিক ব্যবহারের জন্য মেশিনের নির্দেশাবলী রয়েছে, এগুলি শাখাগুলির প্রয়োজনীয়তা এড়ায় যখন আপনার কোডটি একটি নিয়ন্ত্রণ নির্ভরতা রাখে যা সম্ভবত হার্ডওয়্যারে ম্যাপ করে না।
jtaylor

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

13
নম্পিতে একটি মিনম্যাক্স বাস্তবায়ন রয়েছে, এর দ্বারা ব্যবহৃত হুডের নীচে np.bincount, এখানে দেখুন । এটি আপনার নির্দেশিত কৌশলটি ব্যবহার করে না, কারণ এটি নিষ্পাপ পদ্ধতির চেয়ে ধীরে ধীরে 2x অবধি বেড়েছে। উভয় পদ্ধতির কিছু বিস্তৃত মানদণ্ডের সাথে PR থেকে একটি লিঙ্ক রয়েছে ।
জাইমে

5

প্রথম নজরে, কৌশলটি প্রদর্শিত হবে :numpy.histogram

count, (amin, amax) = numpy.histogram(a, bins=1)

... তবে আপনি যদি এই ফাংশনের উত্সটি লক্ষ্য করেন তবে এটি কেবল কল করে a.min()এবং a.max()স্বতন্ত্রভাবে, এবং সুতরাং এই প্রশ্নে বর্ণিত পারফরম্যান্স উদ্বেগ এড়াতে ব্যর্থ হয়। :-(

একইভাবে, scipy.ndimage.measurements.extremaএকটি সম্ভাবনার মতো দেখায় তবে এটি খুব সহজভাবে কল করে a.min()এবং a.max()স্বাধীনভাবে ly


3
np.histogramসর্বদা এটির জন্য কাজ করে না কারণ প্রত্যাবর্তিত (amin, amax)মানগুলি বিনের সর্বনিম্ন এবং সর্বাধিক মানগুলির জন্য। যদি আমার কাছে থাকে, উদাহরণস্বরূপ a = np.zeros(10), np.histogram(a, bins=1)ফেরত দেয় (array([10]), array([-0.5, 0.5]))। ব্যবহারকারী (amin, amax)সেই ক্ষেত্রে = (0, 0) খুঁজছেন ।
প্রত্যয় 21

3

এটি যাইহোক আমার জন্য প্রচেষ্টা মূল্যবান ছিল, তাই আমি আগ্রহী হতে পারে তার জন্য আমি এখানে সবচেয়ে কঠিন এবং কমপক্ষে মার্জিত সমাধানের প্রস্তাব করব। আমার সমাধানটি হ'ল সি ++ এ এক পাসের অ্যালগরিদমে একাধিক-থ্রেডেড মিনি-ম্যাক্স প্রয়োগ করা এবং পাইথন এক্সটেনশন মডিউলটি তৈরি করতে এটি ব্যবহার করুন। পাইথন এবং নুমপি সি / সি ++ এপিআই কীভাবে ব্যবহার করতে হয় তা শেখার জন্য এই প্রয়াসটির জন্য কিছুটা ওভারহেডের প্রয়োজন হয় এবং আমি এখানে কোডটি দেখাব এবং যে কেউ এই পথে যেতে চাইবে তার জন্য কিছু ছোট ব্যাখ্যা এবং রেফারেন্স দেব।

বহু-থ্রেডযুক্ত ন্যূনতম / সর্বোচ্চ

এখানে খুব আকর্ষণীয় কিছু নেই। অ্যারের আকারের অংশে বিভক্ত length / workers। সর্বনিম্ন / সর্বোচ্চটি এ-এর প্রতিটি futureঅংশের জন্য গণনা করা হয় , যা পরে বিশ্বব্যাপী ন্যূনতম / সর্বোচ্চের জন্য স্ক্যান করা হয়।

    // mt_np.cc
    //
    // multi-threaded min/max algorithm

    #include <algorithm>
    #include <future>
    #include <vector>

    namespace mt_np {

    /*
     * Get {min,max} in interval [begin,end)
     */
    template <typename T> std::pair<T, T> min_max(T *begin, T *end) {
      T min{*begin};
      T max{*begin};
      while (++begin < end) {
        if (*begin < min) {
          min = *begin;
          continue;
        } else if (*begin > max) {
          max = *begin;
        }
      }
      return {min, max};
    }

    /*
     * get {min,max} in interval [begin,end) using #workers for concurrency
     */
    template <typename T>
    std::pair<T, T> min_max_mt(T *begin, T *end, int workers) {
      const long int chunk_size = std::max((end - begin) / workers, 1l);
      std::vector<std::future<std::pair<T, T>>> min_maxes;
      // fire up the workers
      while (begin < end) {
        T *next = std::min(end, begin + chunk_size);
        min_maxes.push_back(std::async(min_max<T>, begin, next));
        begin = next;
      }
      // retrieve the results
      auto min_max_it = min_maxes.begin();
      auto v{min_max_it->get()};
      T min{v.first};
      T max{v.second};
      while (++min_max_it != min_maxes.end()) {
        v = min_max_it->get();
        min = std::min(min, v.first);
        max = std::max(max, v.second);
      }
      return {min, max};
    }
    }; // namespace mt_np

পাইথন এক্সটেনশন মডিউল

এখানেই জিনিসগুলি কুৎসিত হতে শুরু করে ... পাইথনে সি ++ কোড ব্যবহারের এক উপায় হ'ল একটি এক্সটেনশন মডিউল প্রয়োগ করা। এই মডিউলটি distutils.coreস্ট্যান্ডার্ড মডিউলটি ব্যবহার করে ইনস্টল করা যায় । পাইথন ডকুমেন্টেশনে এটিতে কী যুক্ত রয়েছে তার একটি সম্পূর্ণ বিবরণ: https://docs.python.org/3/extending/extending.htmlদ্রষ্টব্য: https://docs.python.org/3/extending/index.html#extending-index উদ্ধৃত করার জন্য অনুরূপ ফলাফল পাওয়ার অন্যান্য উপায়গুলি অবশ্যই রয়েছে :

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

মূলত, এই রুটটি সম্ভবত ব্যবহারিকের চেয়ে বেশি শিক্ষামূলক। এর সাথে বলা হচ্ছে যে, আমি এরপরে যা করলাম তা হ'ল টিউটোরিয়ালটির খুব কাছে গিয়ে মডিউল ফাইল তৈরি করুন। আপনার কোডটির সাথে কী করবেন এবং এটি থেকে একটি পাইথন মডিউল তৈরি করতে ডিস্টুইলেটগুলির জন্য এটি মূলত বয়লারপ্লেট। এর যে কোনও কিছু করার আগে পাইথন ভার্চুয়াল পরিবেশ তৈরি করা বুদ্ধিমানের কাজ যাতে আপনি আপনার সিস্টেম প্যাকেজগুলিকে কলুষিত না করেন ( https://docs.python.org/3/library/venv.html#module-venv দেখুন )।

মডিউল ফাইলটি এখানে:

// mt_np_forpy.cc
//
// C++ module implementation for multi-threaded min/max for np

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <python3.6/numpy/arrayobject.h>

#include "mt_np.h"

#include <cstdint>
#include <iostream>

using namespace std;

/*
 * check:
 *  shape
 *  stride
 *  data_type
 *  byteorder
 *  alignment
 */
static bool check_array(PyArrayObject *arr) {
  if (PyArray_NDIM(arr) != 1) {
    PyErr_SetString(PyExc_RuntimeError, "Wrong shape, require (1,n)");
    return false;
  }
  if (PyArray_STRIDES(arr)[0] != 8) {
    PyErr_SetString(PyExc_RuntimeError, "Expected stride of 8");
    return false;
  }
  PyArray_Descr *descr = PyArray_DESCR(arr);
  if (descr->type != NPY_LONGLTR && descr->type != NPY_DOUBLELTR) {
    PyErr_SetString(PyExc_RuntimeError, "Wrong type, require l or d");
    return false;
  }
  if (descr->byteorder != '=') {
    PyErr_SetString(PyExc_RuntimeError, "Expected native byteorder");
    return false;
  }
  if (descr->alignment != 8) {
    cerr << "alignment: " << descr->alignment << endl;
    PyErr_SetString(PyExc_RuntimeError, "Require proper alignement");
    return false;
  }
  return true;
}

template <typename T>
static PyObject *mt_np_minmax_dispatch(PyArrayObject *arr) {
  npy_intp size = PyArray_SHAPE(arr)[0];
  T *begin = (T *)PyArray_DATA(arr);
  auto minmax =
      mt_np::min_max_mt(begin, begin + size, thread::hardware_concurrency());
  return Py_BuildValue("(L,L)", minmax.first, minmax.second);
}

static PyObject *mt_np_minmax(PyObject *self, PyObject *args) {
  PyArrayObject *arr;
  if (!PyArg_ParseTuple(args, "O", &arr))
    return NULL;
  if (!check_array(arr))
    return NULL;
  switch (PyArray_DESCR(arr)->type) {
  case NPY_LONGLTR: {
    return mt_np_minmax_dispatch<int64_t>(arr);
  } break;
  case NPY_DOUBLELTR: {
    return mt_np_minmax_dispatch<double>(arr);
  } break;
  default: {
    PyErr_SetString(PyExc_RuntimeError, "Unknown error");
    return NULL;
  }
  }
}

static PyObject *get_concurrency(PyObject *self, PyObject *args) {
  return Py_BuildValue("I", thread::hardware_concurrency());
}

static PyMethodDef mt_np_Methods[] = {
    {"mt_np_minmax", mt_np_minmax, METH_VARARGS, "multi-threaded np min/max"},
    {"get_concurrency", get_concurrency, METH_VARARGS,
     "retrieve thread::hardware_concurrency()"},
    {NULL, NULL, 0, NULL} /* sentinel */
};

static struct PyModuleDef mt_np_module = {PyModuleDef_HEAD_INIT, "mt_np", NULL,
                                          -1, mt_np_Methods};

PyMODINIT_FUNC PyInit_mt_np() { return PyModule_Create(&mt_np_module); }

এই ফাইলে পাইথনের পাশাপাশি নম্পপি এপিআইয়ের উল্লেখযোগ্য ব্যবহার রয়েছে, আরও তথ্যের জন্য পরামর্শ নিন: https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple , এবং NumPy এর জন্য : https://docs.scipy.org/doc/numpy/references/c-api.array.html

মডিউল ইনস্টল করা হচ্ছে

পরবর্তী কাজটি হ'ল মডিউলটি ইনস্টল করার জন্য ডিস্টুইলেটগুলি ব্যবহার করা। এটির জন্য একটি সেটআপ ফাইল দরকার:

# setup.py

from distutils.core import setup,Extension

module = Extension('mt_np', sources = ['mt_np_module.cc'])

setup (name = 'mt_np', 
       version = '1.0', 
       description = 'multi-threaded min/max for np arrays',
       ext_modules = [module])

পরিশেষে মডিউলটি ইনস্টল করতে python3 setup.py installআপনার ভার্চুয়াল পরিবেশ থেকে চালিত করুন।

মডিউল পরীক্ষা করা হচ্ছে

পরিশেষে, আমরা দেখতে পারি যে সি ++ বাস্তবায়ন আসলে NumPy এর নিষ্পাপ ব্যবহারকে ছাপিয়ে যায় কিনা। এটি করার জন্য, এখানে একটি সাধারণ পরীক্ষা স্ক্রিপ্ট:

# timing.py
# compare numpy min/max vs multi-threaded min/max

import numpy as np
import mt_np
import timeit

def normal_min_max(X):
  return (np.min(X),np.max(X))

print(mt_np.get_concurrency())

for ssize in np.logspace(3,8,6):
  size = int(ssize)
  print('********************')
  print('sample size:', size)
  print('********************')
  samples = np.random.normal(0,50,(2,size))
  for sample in samples:
    print('np:', timeit.timeit('normal_min_max(sample)',
                 globals=globals(),number=10))
    print('mt:', timeit.timeit('mt_np.mt_np_minmax(sample)',
                 globals=globals(),number=10))

এগুলি করা থেকে প্রাপ্ত ফলাফলগুলি এখানে:

8  
********************  
sample size: 1000  
********************  
np: 0.00012079699808964506  
mt: 0.002468645994667895  
np: 0.00011947099847020581  
mt: 0.0020772050047526136  
********************  
sample size: 10000  
********************  
np: 0.00024697799381101504  
mt: 0.002037393998762127  
np: 0.0002713389985729009  
mt: 0.0020942929986631498  
********************  
sample size: 100000  
********************  
np: 0.0007130410012905486  
mt: 0.0019842900001094677  
np: 0.0007540129954577424  
mt: 0.0029724110063398257  
********************  
sample size: 1000000  
********************  
np: 0.0094779249993735  
mt: 0.007134920000680722  
np: 0.009129883001151029  
mt: 0.012836456997320056  
********************  
sample size: 10000000  
********************  
np: 0.09471094200125663  
mt: 0.0453535050037317  
np: 0.09436299200024223  
mt: 0.04188535599678289  
********************  
sample size: 100000000  
********************  
np: 0.9537652180006262  
mt: 0.3957935369980987  
np: 0.9624398809974082  
mt: 0.4019058070043684  

ফলাফলগুলি থ্রেডের পূর্বের চেয়ে আরও কম উত্সাহজনক, যা প্রায় 3.5x স্পিডআপের দিকে নির্দেশ করেছে এবং মাল্টি-থ্রেডিং অন্তর্ভুক্ত করেনি। আমি যে ফলাফলগুলি পেয়েছি তা কিছুটা যুক্তিসঙ্গত, আমি প্রত্যাশা করব যে থ্রেডিংয়ের ওভারহেডটি অ্যারেগুলি খুব বড় না হওয়া পর্যন্ত সময়কে প্রভাবিত করবে, যার পর্যায়ে পারফরম্যান্স বৃদ্ধিটি std::thread::hardware_concurrencyএক্স বর্ধনের দিকে আসতে শুরু করবে ।

উপসংহার

কিছু NumPy কোডে অ্যাপ্লিকেশন নির্দিষ্ট অপ্টিমাইজেশনের জন্য অবশ্যই জায়গা আছে, বিশেষত মাল্টি-থ্রেডিংয়ের ক্ষেত্রে এটি মনে হবে। চেষ্টাটি মূল্যবান কিনা তা আমার কাছে পরিষ্কার নয় তবে এটি অবশ্যই একটি ভাল অনুশীলনের (বা কিছু) বলে মনে হচ্ছে। আমি মনে করি যে সম্ভবত সাইথনের মতো সেই "তৃতীয় পক্ষের সরঞ্জামগুলি" শেখা সময়ের সময়ের আরও ভাল ব্যবহার হতে পারে তবে কে জানে।


1
আমি আপনার কোড অধ্যয়ন শুরু করি, কিছু সি ++ জানি তবে এখনও স্টডি :: ফিউচার এবং এসটিডি :: অ্যাসিঙ্ক ব্যবহার করিনি। আপনার 'min_max_mt' টেম্পলেট ফাংশনে, এটি কীভাবে জানবে যে প্রতিটি কর্মী গুলি চালানো এবং ফলাফলগুলি পুনরুদ্ধারের মধ্যে শেষ করেছে? (কেবল বোঝার জন্য জিজ্ঞাসা করুন, এটিতে কোনও ভুল আছে তা বলছেন না)
ChrCury78

লাইন v = min_max_it->get();getপদ্ধতি ব্লক পর্যন্ত ফলাফলের জন্য প্রস্তুত হয় এবং এটি আয়। যেহেতু লুপ প্রতিটি ভবিষ্যতের মধ্য দিয়ে যায়, যতক্ষণ না সেগুলি শেষ হয়ে যায়। ভবিষ্যত.জেট ()
নাথন চ্যাপেল

0

আমি যে সংক্ষিপ্ততম পথটি নিয়ে এসেছি তা হ'ল:

mn, mx = np.sort(ar)[[0, -1]]

তবে যেহেতু এটি অ্যারে বাছাই করে, এটি সবচেয়ে কার্যকর নয়।

আর একটি ছোট উপায় হ'ল:

mn, mx = np.percentile(ar, [0, 100])

এটি আরও দক্ষ হওয়া উচিত তবে ফলাফলটি গণনা করা হয় এবং একটি ভাসা ফেরত দেওয়া হয়।


লজ্জাজনকভাবে, এই দুটি হ'ল এই পৃষ্ঠার অন্যদের সাথে তুলনা করার ধীরতম সমাধান: এম = এনপি.মিন (ক); এম = এনপি.ম্যাক্স (ক) -> 0.54002 || ছিপি মি, এম = f90_minmax1 (ক) -> 0.72134 || ছায়া মি, এম = নাম্বা_মিনম্যাক্স (এ) -> 0.77323 || ছিপি মি, এম = এনপি.সোর্ট (ক) [[0, -1]] -> 12.01456 || মি, এম = এনপি.প্রেনসাইল (ক, [0, 100]) -> 11.09418 || ছায়া 100k উপাদানগুলির অ্যারের জন্য 10000 পুনরাবৃত্তির জন্য সেকেন্ডে
ইসায়াস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.