একটি আধ্যাত্মিক cartesian_product
(প্রায়)
বিভিন্ন সম্পত্তি সহ এই সমস্যাটির অনেকগুলি পন্থা রয়েছে। কিছু অন্যের চেয়ে দ্রুত এবং কিছু সাধারণ-উদ্দেশ্যে। অনেকগুলি পরীক্ষার এবং টুইট করার পরে, আমি দেখতে পেয়েছি যে নিম্নলিখিত ফাংশনটি, যা একটি এন-ডাইমেনশনাল গণনা করে cartesian_product
, অনেকগুলি ইনপুটগুলির জন্য অন্যান্যদের চেয়ে দ্রুত। একজোড়া পদ্ধতির জন্য যা কিছুটা জটিল, তবে অনেক ক্ষেত্রে আরও কিছুটা দ্রুত, পল পানজারের উত্তর দেখুন ।
এই উত্তরটি দেওয়া হয়েছে, কারটিশিয়ান পণ্যটির বিষয়ে এটি সম্পর্কে আমি সচেতন যে এটি এখন আর দ্রুত বাস্তবায়ন নয় numpy
। তবে আমি মনে করি এর সরলতা ভবিষ্যতের উন্নতির জন্য এটি একটি কার্যকর মানদণ্ড হিসাবে অবিরত থাকবে:
def cartesian_product(*arrays):
la = len(arrays)
dtype = numpy.result_type(*arrays)
arr = numpy.empty([len(a) for a in arrays] + [la], dtype=dtype)
for i, a in enumerate(numpy.ix_(*arrays)):
arr[...,i] = a
return arr.reshape(-1, la)
এটি উল্লেখযোগ্য যে এই ফাংশনটি ix_
অস্বাভাবিক উপায়ে ব্যবহার করে; যদিও এর নথিভুক্ত ব্যবহার ix_
হ'ল সূচকগুলি তৈরি করা একটি অ্যারেতে করা , এটি ঠিক তাই ঘটে যে একই আকারের অ্যারেগুলি সম্প্রচারিত অ্যাসাইনমেন্টের জন্য ব্যবহার করা যেতে পারে। মিগিলসনকে অনেক ধন্যবাদ , যিনি আমাকে ix_
এইভাবে চেষ্টা করার জন্য অনুপ্রেরণা দিয়েছিলেন এবং আনটবুকে , যিনি এই উত্তরটি সম্পর্কে ব্যবহারের পরামর্শ সহ কিছু চূড়ান্ত সহায়ক প্রতিক্রিয়া সরবরাহ করেছিলেনnumpy.result_type
।
উল্লেখযোগ্য বিকল্প
ফোর্টরান ক্রমে মেমরির সংকীর্ণ ব্লকগুলি লেখার জন্য এটি কখনও কখনও দ্রুত। এটি এই বিকল্পের ভিত্তি cartesian_product_transpose
, যা কিছু হার্ডওয়্যার থেকে দ্রুত প্রমাণিত হয়েছে cartesian_product
(নীচে দেখুন)। তবে একই নীতিটি ব্যবহার করে পল পাঞ্জেরের উত্তর আরও দ্রুত। তবুও, আমি আগ্রহী পাঠকদের জন্য এটি এখানে অন্তর্ভুক্ত করছি:
def cartesian_product_transpose(*arrays):
broadcastable = numpy.ix_(*arrays)
broadcasted = numpy.broadcast_arrays(*broadcastable)
rows, cols = numpy.prod(broadcasted[0].shape), len(broadcasted)
dtype = numpy.result_type(*arrays)
out = numpy.empty(rows * cols, dtype=dtype)
start, end = 0, rows
for a in broadcasted:
out[start:end] = a.reshape(-1)
start, end = end, end + rows
return out.reshape(cols, rows).T
পানজারের পদ্ধতির বিষয়টি বুঝতে পেরে, আমি একটি নতুন সংস্করণ লিখেছিলাম যা তার চেয়ে প্রায় দ্রুত এবং এটি প্রায় সহজ cartesian_product
:
def cartesian_product_simple_transpose(arrays):
la = len(arrays)
dtype = numpy.result_type(*arrays)
arr = numpy.empty([la] + [len(a) for a in arrays], dtype=dtype)
for i, a in enumerate(numpy.ix_(*arrays)):
arr[i, ...] = a
return arr.reshape(la, -1).T
এটিতে কিছু ধ্রুবক-সময় ওভারহেড উপস্থিত রয়েছে যা এটি ছোট ইনপুটগুলির জন্য পানজারের চেয়ে ধীর গতিতে চালিত করে। তবে বৃহত্তর ইনপুটগুলির জন্য, আমি যে সমস্ত পরীক্ষাগুলি চালিয়েছি, এটি তার দ্রুত বাস্তবায়ন ( cartesian_product_transpose_pp
) হিসাবে সম্পাদন করে ।
নিম্নলিখিত বিভাগগুলিতে, আমি অন্যান্য বিকল্পের কয়েকটি পরীক্ষা অন্তর্ভুক্ত করছি। এগুলি এখন কিছুটা পুরানো, তবে সদৃশ প্রচেষ্টার চেয়ে আমি এগুলি এখানে historicalতিহাসিক আগ্রহের বাইরে রেখেই যাওয়ার সিদ্ধান্ত নিয়েছি। আপ-টু-ডেট পরীক্ষাগুলির জন্য পানজারের উত্তরও দেখুন পানজারের নিকো স্ক্ল্যামার এর দেখুন।
বিকল্পের বিরুদ্ধে টেস্ট
এখানে পরীক্ষাগুলির একটি ব্যাটারি রয়েছে যা পারফরম্যান্সের বুস্ট দেখায় যে এই কয়েকটি ফাংশন বিভিন্ন বিকল্পের সাথে সম্পর্কিত করে। এখানে প্রদর্শিত সমস্ত পরীক্ষাগুলি ম্যাক ওএস 10.12.5, পাইথন 3.6.1 এবং চলমান কোয়াড-কোর মেশিনে সঞ্চালিত হয়েছিল এবংnumpy
1.12.1 । হার্ডওয়্যার এবং সফ্টওয়্যার এর পার্থক্য বিভিন্ন ফলাফল উত্পাদন হিসাবে পরিচিত, তাই ওয়াইএমএমভি। নিজের নিশ্চিত হওয়ার জন্য এই পরীক্ষাগুলি চালান!
সজ্ঞা:
import numpy
import itertools
from functools import reduce
### Two-dimensional products ###
def repeat_product(x, y):
return numpy.transpose([numpy.tile(x, len(y)),
numpy.repeat(y, len(x))])
def dstack_product(x, y):
return numpy.dstack(numpy.meshgrid(x, y)).reshape(-1, 2)
### Generalized N-dimensional products ###
def cartesian_product(*arrays):
la = len(arrays)
dtype = numpy.result_type(*arrays)
arr = numpy.empty([len(a) for a in arrays] + [la], dtype=dtype)
for i, a in enumerate(numpy.ix_(*arrays)):
arr[...,i] = a
return arr.reshape(-1, la)
def cartesian_product_transpose(*arrays):
broadcastable = numpy.ix_(*arrays)
broadcasted = numpy.broadcast_arrays(*broadcastable)
rows, cols = numpy.prod(broadcasted[0].shape), len(broadcasted)
dtype = numpy.result_type(*arrays)
out = numpy.empty(rows * cols, dtype=dtype)
start, end = 0, rows
for a in broadcasted:
out[start:end] = a.reshape(-1)
start, end = end, end + rows
return out.reshape(cols, rows).T
# from https://stackoverflow.com/a/1235363/577088
def cartesian_product_recursive(*arrays, out=None):
arrays = [numpy.asarray(x) for x in arrays]
dtype = arrays[0].dtype
n = numpy.prod([x.size for x in arrays])
if out is None:
out = numpy.zeros([n, len(arrays)], dtype=dtype)
m = n // arrays[0].size
out[:,0] = numpy.repeat(arrays[0], m)
if arrays[1:]:
cartesian_product_recursive(arrays[1:], out=out[0:m,1:])
for j in range(1, arrays[0].size):
out[j*m:(j+1)*m,1:] = out[0:m,1:]
return out
def cartesian_product_itertools(*arrays):
return numpy.array(list(itertools.product(*arrays)))
### Test code ###
name_func = [('repeat_product',
repeat_product),
('dstack_product',
dstack_product),
('cartesian_product',
cartesian_product),
('cartesian_product_transpose',
cartesian_product_transpose),
('cartesian_product_recursive',
cartesian_product_recursive),
('cartesian_product_itertools',
cartesian_product_itertools)]
def test(in_arrays, test_funcs):
global func
global arrays
arrays = in_arrays
for name, func in test_funcs:
print('{}:'.format(name))
%timeit func(*arrays)
def test_all(*in_arrays):
test(in_arrays, name_func)
# `cartesian_product_recursive` throws an
# unexpected error when used on more than
# two input arrays, so for now I've removed
# it from these tests.
def test_cartesian(*in_arrays):
test(in_arrays, name_func[2:4] + name_func[-1:])
x10 = [numpy.arange(10)]
x50 = [numpy.arange(50)]
x100 = [numpy.arange(100)]
x500 = [numpy.arange(500)]
x1000 = [numpy.arange(1000)]
পরীক্ষার ফলাফল:
In [2]: test_all(*(x100 * 2))
repeat_product:
67.5 µs ± 633 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
dstack_product:
67.7 µs ± 1.09 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
cartesian_product:
33.4 µs ± 558 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
cartesian_product_transpose:
67.7 µs ± 932 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
cartesian_product_recursive:
215 µs ± 6.01 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
cartesian_product_itertools:
3.65 ms ± 38.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [3]: test_all(*(x500 * 2))
repeat_product:
1.31 ms ± 9.28 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
dstack_product:
1.27 ms ± 7.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
cartesian_product:
375 µs ± 4.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
cartesian_product_transpose:
488 µs ± 8.88 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
cartesian_product_recursive:
2.21 ms ± 38.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
cartesian_product_itertools:
105 ms ± 1.17 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [4]: test_all(*(x1000 * 2))
repeat_product:
10.2 ms ± 132 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
dstack_product:
12 ms ± 120 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
cartesian_product:
4.75 ms ± 57.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
cartesian_product_transpose:
7.76 ms ± 52.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
cartesian_product_recursive:
13 ms ± 209 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
cartesian_product_itertools:
422 ms ± 7.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
সব ক্ষেত্রে, cartesian_product
এই উত্তরটির শুরুতে সংজ্ঞায়িত হিসাবে দ্রুততম।
যে ফাংশনগুলি ইনপুট অ্যারেগুলির একটি স্বেচ্ছাসেবী সংখ্যার স্বীকৃতি দেয়, তাদের জন্য যখন এটি পরীক্ষা len(arrays) > 2
করা ভাল হয় performance (যতক্ষণ না আমি নির্ধারণ করতে পারি যে কেন cartesian_product_recursive
এই ক্ষেত্রে ত্রুটি ছুঁড়েছে, আমি এগুলি এই পরীক্ষাগুলি থেকে সরিয়েছি))
In [5]: test_cartesian(*(x100 * 3))
cartesian_product:
8.8 ms ± 138 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
cartesian_product_transpose:
7.87 ms ± 91.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
cartesian_product_itertools:
518 ms ± 5.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [6]: test_cartesian(*(x50 * 4))
cartesian_product:
169 ms ± 5.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
cartesian_product_transpose:
184 ms ± 4.32 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
cartesian_product_itertools:
3.69 s ± 73.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [7]: test_cartesian(*(x10 * 6))
cartesian_product:
26.5 ms ± 449 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
cartesian_product_transpose:
16 ms ± 133 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
cartesian_product_itertools:
728 ms ± 16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [8]: test_cartesian(*(x10 * 7))
cartesian_product:
650 ms ± 8.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
cartesian_product_transpose:
518 ms ± 7.09 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
cartesian_product_itertools:
8.13 s ± 122 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
যেমন এই পরীক্ষাগুলি দেখায়, cartesian_product
ইনপুট অ্যারেগুলির সংখ্যা চারটির উপরে না বাড়ানো পর্যন্ত প্রতিযোগিতামূলক থেকে যায়। তারপর,cartesian_product_transpose
একটি সামান্য প্রান্ত আছে।
এটি পুনরাবৃত্তি করার মতো যে অন্যান্য হার্ডওয়্যার এবং অপারেটিং সিস্টেমের ব্যবহারকারীরা বিভিন্ন ফলাফল দেখতে পারেন। উদাহরণস্বরূপ, উবুন্টু 14.04, পাইথন numpy
3.4.3 , এবং 1.14.0.dev0 + বি 7050a9 ব্যবহার করে এই পরীক্ষাগুলির জন্য নিম্নলিখিত ফলাফলগুলি দেখার জন্য আনটবু রিপোর্ট করেছে:
>>> %timeit cartesian_product_transpose(x500, y500)
1000 loops, best of 3: 682 µs per loop
>>> %timeit cartesian_product(x500, y500)
1000 loops, best of 3: 1.55 ms per loop
নীচে, আমি পূর্ববর্তী পরীক্ষাগুলি সম্পর্কে কিছু বিশদ যাচ্ছি আমি এই লাইনের সাথে চালিয়েছি। এই হার্ডওয়্যার এবং পাইথনের বিভিন্ন সংস্করণ এবং বিভিন্ন সংস্করণের জন্য এই পদ্ধতির আপেক্ষিক কার্য সম্পাদন সময়ের সাথে সাথে পরিবর্তিত হয়েছে numpy
। যদিও এটি আপ-টু-ডেট সংস্করণগুলির লোকদের জন্য তাত্ক্ষণিকভাবে কার্যকর নয়numpy
, তবে এটি ব্যাখ্যা করে যে এই উত্তরের প্রথম সংস্করণ থেকে জিনিসগুলি কীভাবে পরিবর্তিত হয়েছে।
একটি সহজ বিকল্প: meshgrid
+dstack
বর্তমানে উত্তর ব্যবহারসমূহ গৃহীত tile
এবং repeat
সম্প্রচারের দুই অ্যারে একসঙ্গে। তবে meshgrid
ফাংশনটি কার্যত একই জিনিসটি করে। স্থানান্তর করতে পাস করার আগে tile
এবং এর repeat
আগে আউটপুট দেওয়া হয়:
In [1]: import numpy
In [2]: x = numpy.array([1,2,3])
...: y = numpy.array([4,5])
...:
In [3]: [numpy.tile(x, len(y)), numpy.repeat(y, len(x))]
Out[3]: [array([1, 2, 3, 1, 2, 3]), array([4, 4, 4, 5, 5, 5])]
এবং এখানে ফলাফল meshgrid
:
In [4]: numpy.meshgrid(x, y)
Out[4]:
[array([[1, 2, 3],
[1, 2, 3]]), array([[4, 4, 4],
[5, 5, 5]])]
আপনি দেখতে পাচ্ছেন, এটি প্রায় অভিন্ন। হুবহু একই ফলাফল পেতে আমাদের কেবল পুনরায় আকার পরিবর্তন করতে হবে।
In [5]: xt, xr = numpy.meshgrid(x, y)
...: [xt.ravel(), xr.ravel()]
Out[5]: [array([1, 2, 3, 1, 2, 3]), array([4, 4, 4, 5, 5, 5])]
এই মুহুর্তে পুনর্নির্মাণের পরিবর্তে, আমরা এর ফলাফল আউটপুটটি এবং পরে পুনরায় আকার meshgrid
দিতে পারলাম dstack
যা কিছু কাজ সাশ্রয় করে:
In [6]: numpy.dstack(numpy.meshgrid(x, y)).reshape(-1, 2)
Out[6]:
array([[1, 4],
[2, 4],
[3, 4],
[1, 5],
[2, 5],
[3, 5]])
এই মন্তব্যে দাবির বিপরীতে , আমি কোনও প্রমাণ দেখিনি যে বিভিন্ন ইনপুটগুলি ভিন্ন আকারের আকারের আউটপুট তৈরি করে এবং উপরের দিক থেকে প্রমাণিত হয় যে তারা খুব অনুরূপ কাজ করে, তাই তারা যদি তা করে তবে এটি বেশ আশ্চর্যজনক হবে। আপনি যদি কাউন্টারিক্স নমুনা পান তবে দয়া করে আমাকে জানান।
পরীক্ষা meshgrid
+ dstack
বনাম repeat
+transpose
এই দুটি পদ্ধতির আপেক্ষিক কর্মক্ষমতা সময়ের সাথে পরিবর্তিত হয়েছে। পাইথনের পূর্ববর্তী সংস্করণে (২.)), meshgrid
+ এর ফলাফলটি dstack
ছোট ইনপুটগুলির জন্য লক্ষণীয়ভাবে দ্রুততর হয়েছিল। (দ্রষ্টব্য যে এই পরীক্ষাগুলি এই উত্তরের একটি পুরানো সংস্করণ থেকে এসেছে)) সংজ্ঞা:
>>> def repeat_product(x, y):
... return numpy.transpose([numpy.tile(x, len(y)),
numpy.repeat(y, len(x))])
...
>>> def dstack_product(x, y):
... return numpy.dstack(numpy.meshgrid(x, y)).reshape(-1, 2)
...
মাঝারি আকারের ইনপুটটির জন্য, আমি একটি উল্লেখযোগ্য স্পিডআপ দেখেছি। তবে আমি পাইথনের আরও সাম্প্রতিক সংস্করণ (3.6.1) এবং numpy
(1.12.1) এর সাথে একটি নতুন মেশিনে এই পরীক্ষাগুলি পুনরায় চেষ্টা করেছি । দুটি পদ্ধতির এখন প্রায় অভিন্ন।
ওল্ড টেস্ট
>>> x, y = numpy.arange(500), numpy.arange(500)
>>> %timeit repeat_product(x, y)
10 loops, best of 3: 62 ms per loop
>>> %timeit dstack_product(x, y)
100 loops, best of 3: 12.2 ms per loop
নতুন পরীক্ষা
In [7]: x, y = numpy.arange(500), numpy.arange(500)
In [8]: %timeit repeat_product(x, y)
1.32 ms ± 24.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [9]: %timeit dstack_product(x, y)
1.26 ms ± 8.47 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
বরাবরের মতো, ওয়াইএমএমভি, তবে এটি সূচিত করে যে পাইথন এবং অলস এর সাম্প্রতিক সংস্করণগুলিতে এগুলি বিনিময়যোগ্য।
সাধারণীকরণ পণ্য ফাংশন
সাধারণভাবে, আমরা আশা করতে পারি যে অন্তর্নির্মিত ফাংশনগুলি ছোট ইনপুটগুলির জন্য দ্রুততর হবে, তবে বড় ইনপুটগুলির জন্য, একটি উদ্দেশ্য-নির্মিত ফাংশনটি দ্রুততর হতে পারে। জেনারেলাইজড এন-ডাইমেনশনাল পণ্য tile
এবং আরও জন্যrepeat
করবে না কারণ তাদের উচ্চতর মাত্রিক অ্যানালগগুলি পরিষ্কার নেই clear সুতরাং উদ্দেশ্য-নির্মিত ফাংশনগুলির আচরণও তদন্তের পক্ষে মূল্যবান।
প্রাসঙ্গিক পরীক্ষাগুলির বেশিরভাগই এই উত্তরের শুরুতে উপস্থিত হয় তবে পাইথনের পূর্ববর্তী সংস্করণগুলিতে এবং numpy
তুলনার জন্য পরীক্ষাগুলির কয়েকটি দেওয়া হল ।
cartesian
ফাংশন সংজ্ঞায়িত অন্য উত্তর বৃহত্তর ইনপুট জন্য চমত্কার সঞ্চালন করতে ব্যবহৃত। (এটি cartesian_product_recursive
উপরে বর্ণিত ফাংশনটির মতোই ।) এর সাথে তুলনা cartesian
করার জন্য dstack_prodct
, আমরা মাত্র দুটি মাত্রা ব্যবহার করি।
এখানে আবারও, পুরানো পরীক্ষাটি একটি উল্লেখযোগ্য পার্থক্য দেখিয়েছে, যখন নতুন পরীক্ষাটি প্রায় কোনওটিই দেখায় না।
ওল্ড টেস্ট
>>> x, y = numpy.arange(1000), numpy.arange(1000)
>>> %timeit cartesian([x, y])
10 loops, best of 3: 25.4 ms per loop
>>> %timeit dstack_product(x, y)
10 loops, best of 3: 66.6 ms per loop
নতুন পরীক্ষা
In [10]: x, y = numpy.arange(1000), numpy.arange(1000)
In [11]: %timeit cartesian([x, y])
12.1 ms ± 199 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [12]: %timeit dstack_product(x, y)
12.7 ms ± 334 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
আগের মতো dstack_product
এখনও cartesian
ছোট আকারের স্কেলে মারধর করে।
নতুন পরীক্ষা ( অপ্রয়োজনীয় পুরাতন পরীক্ষা দেখানো হয়নি )
In [13]: x, y = numpy.arange(100), numpy.arange(100)
In [14]: %timeit cartesian([x, y])
215 µs ± 4.75 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [15]: %timeit dstack_product(x, y)
65.7 µs ± 1.15 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
এই পার্থক্যগুলি হ'ল, আমি মনে করি, আকর্ষণীয় এবং মূল্যবান রেকর্ডিং; তবে শেষ পর্যন্ত তারা একাডেমিক। এই উত্তরের শুরুতে পরীক্ষাগুলি যেমন দেখিয়েছে, এই উত্তরগুলির cartesian_product
একেবারে শুরুতে সংজ্ঞায়িত এই সংস্করণগুলির প্রায় সবসময়ই ধীর ।