दो सरणियों के सभी संयोजनों की एक सरणी बनाने के लिए संख्यात्मक का उपयोग करना


143

मैं इसके साथ कुछ भी जटिल करने की कोशिश करने से पहले संख्यात्मक व्यवहार का अध्ययन करने के लिए 6 पैरामीटर फ़ंक्शन के पैरामीटर स्थान पर चलाने की कोशिश कर रहा हूं, इसलिए मैं ऐसा करने के लिए एक कुशल तरीका खोज रहा हूं।

मेरा फंक्शन फ्लोट वैल्यू लेता है जिसे इनपुट के रूप में 6-डिम्पी के रूप में दिया गया है। शुरू में मैंने जो करने की कोशिश की वह यह थी:

पहले मैंने एक फ़ंक्शन बनाया जो 2 सरणियों को लेता है और दो सरणियों से मूल्यों के सभी संयोजनों के साथ एक सरणी उत्पन्न करता है

from numpy import *
def comb(a,b):
    c = []
    for i in a:
        for j in b:
            c.append(r_[i,j])
    return c

तब मैं reduce()उसी सरणी की प्रतियों के लिए आवेदन करता था:

def combs(a,m):
    return reduce(comb,[a]*m)

और फिर मैं इस तरह से अपने कार्य का मूल्यांकन करता हूं:

values = combs(np.arange(0,1,0.1),6)
for val in values:
    print F(val)

यह काम करता है लेकिन यह बहुत धीमी गति से होता है। मुझे पता है कि मापदंडों का स्थान बहुत बड़ा है, लेकिन यह इतना धीमा नहीं होना चाहिए। मैंने इस उदाहरण में केवल 10 6 (एक मिलियन) अंक लिए हैं और सरणी बनाने में सिर्फ 15 सेकंड से अधिक समय लगा है values

क्या आप सुन्न के साथ ऐसा करने का कोई और कुशल तरीका जानते हैं?

Fयदि आवश्यक हो तो मैं फ़ंक्शन को ले जाने के तरीके को संशोधित कर सकता हूं ।


सबसे तेज़ कार्टेसियन उत्पाद मैंने पाया है, इस उत्तर को देखें । (चूँकि प्रश्न इस एक से बहुत अलग तरीके से व्यक्त किया गया है, इसलिए मुझे लगता है कि प्रश्न डुप्लिकेट नहीं हैं, लेकिन दो प्रश्नों का सबसे अच्छा समाधान समान है।)
प्रेषक

जवाबों:


127

numpy(> 1.8.x) के नए संस्करण में , numpy.meshgrid()बहुत तेज़ कार्यान्वयन प्रदान करता है:

@ Pv का समाधान

In [113]:

%timeit cartesian(([1, 2, 3], [4, 5], [6, 7]))
10000 loops, best of 3: 135 µs per loop
In [114]:

cartesian(([1, 2, 3], [4, 5], [6, 7]))

Out[114]:
array([[1, 4, 6],
       [1, 4, 7],
       [1, 5, 6],
       [1, 5, 7],
       [2, 4, 6],
       [2, 4, 7],
       [2, 5, 6],
       [2, 5, 7],
       [3, 4, 6],
       [3, 4, 7],
       [3, 5, 6],
       [3, 5, 7]])

numpy.meshgrid()केवल 2D होना चाहिए, अब यह ND के लिए सक्षम है। इस मामले में, 3 डी:

In [115]:

%timeit np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)
10000 loops, best of 3: 74.1 µs per loop
In [116]:

np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)

Out[116]:
array([[1, 4, 6],
       [1, 5, 6],
       [2, 4, 6],
       [2, 5, 6],
       [3, 4, 6],
       [3, 5, 6],
       [1, 4, 7],
       [1, 5, 7],
       [2, 4, 7],
       [2, 5, 7],
       [3, 4, 7],
       [3, 5, 7]])

ध्यान दें कि अंतिम परिणामी का क्रम थोड़ा अलग है।


14
np.stack(np.meshgrid([1, 2, 3], [4, 5], [6, 7]), -1).reshape(-1, 3)सही आदेश देगा
एरिक

@CT झू क्या इसे बदलने का एक आसान तरीका है ताकि एक मैट्रिक्स को विभिन्न सरणियों को पकड़कर कॉलम के रूप में प्रयोग किया जाए?
डोले

2
यह ध्यान दिया जाना चाहिए कि मेशग्रिड केवल छोटे रेंज सेट के लिए काम करता है, मेरे पास एक बड़ा है और मुझे त्रुटि मिलती है: ValueError: एक
ndarray के

158

यहाँ एक शुद्ध-संख्यात्मक कार्यान्वयन है। Itertools का उपयोग करने की तुलना में यह लगभग 5 × तेज है।


import numpy as np

def cartesian(arrays, out=None):
    """
    Generate a cartesian product of input arrays.

    Parameters
    ----------
    arrays : list of array-like
        1-D arrays to form the cartesian product of.
    out : ndarray
        Array to place the cartesian product in.

    Returns
    -------
    out : ndarray
        2-D array of shape (M, len(arrays)) containing cartesian products
        formed of input arrays.

    Examples
    --------
    >>> cartesian(([1, 2, 3], [4, 5], [6, 7]))
    array([[1, 4, 6],
           [1, 4, 7],
           [1, 5, 6],
           [1, 5, 7],
           [2, 4, 6],
           [2, 4, 7],
           [2, 5, 6],
           [2, 5, 7],
           [3, 4, 6],
           [3, 4, 7],
           [3, 5, 6],
           [3, 5, 7]])

    """

    arrays = [np.asarray(x) for x in arrays]
    dtype = arrays[0].dtype

    n = np.prod([x.size for x in arrays])
    if out is None:
        out = np.zeros([n, len(arrays)], dtype=dtype)

    m = n / arrays[0].size
    out[:,0] = np.repeat(arrays[0], m)
    if arrays[1:]:
        cartesian(arrays[1:], out=out[0:m, 1:])
        for j in xrange(1, arrays[0].size):
            out[j*m:(j+1)*m, 1:] = out[0:m, 1:]
    return out

47
कभी इसे प्रस्तुत करने पर विचार करें जो सुपीरियर में शामिल किया जाए? यह पहली बार नहीं है जब मैं इस कार्यक्षमता की तलाश में गया हूँ और अपनी पोस्ट को पाया है।
एंडोलिथ

1
इस कार्यान्वयन में बग है। उदाहरण के लिए तार के सरणियों के लिए: सरणियाँ [0] .डाइप = "| S3" और सरणियाँ [1] .डाइप = "| S5"। इसलिए इनपुट में सबसे लंबी स्ट्रिंग ढूंढने की जरूरत है और इसके प्रकार का उपयोग = np.zeros ([n, len (arrays)], dtype = dtype) में किया गया है
Notecces

38
FYI करें: ऐसा लगता है कि इसे scikit-learn पैकेज में बनाया गया हैfrom sklearn.utils.extmath import cartesian
Gus

2
मुझे बस एहसास हुआ: यह itertools.combments से थोड़ा अलग है, क्योंकि यह फ़ंक्शन मूल्यों के क्रम का सम्मान करता है जबकि संयोजन नहीं करता है, इसलिए यह फ़ंक्शन संयोजन से अधिक मान लौटाता है। अभी भी बहुत प्रभावशाली है, लेकिन दुर्भाग्य से मैं क्या देख रहा था :(
डेविड मार्क्स

6
TypeError: slice indices must be integers or None or have an __index__ methodद्वारा फेंकाcartesian(arrays[1:], out=out[0:m,1:])
Boern

36

itertools.combinations सामान्य रूप से पायथन कंटेनर से संयोजन प्राप्त करने का सबसे तेज़ तरीका है (यदि आप वास्तव में संयोजन चाहते हैं, अर्थात, पुनरावृत्ति और ऑर्डर के बिना व्यवस्था; यह वही है जो आपका कोड करता प्रतीत होता है, लेकिन मैं नहीं कर सकता यह बताएं कि क्या आपका कोड छोटी गाड़ी है या इसलिए कि आप गलत शब्दावली का उपयोग कर रहे हैं)।

यदि आप कुछ अन्य पुनरावृत्तियों के संयोजन से अलग कुछ चाहते हैं, productया permutations, आप बेहतर सेवा कर सकते हैं। उदाहरण के लिए, ऐसा लगता है कि आपका कोड लगभग समान है:

for val in itertools.product(np.arange(0, 1, 0.1), repeat=6):
    print F(val)

इन पुनरावृत्तियों में से सभी टुपल्स का उत्पादन करते हैं, न कि सूचियों या नंबरी सरणियों का, इसलिए यदि आपका एफ विशेष रूप से एक अफीम सरणी प्राप्त करने के बारे में है, तो आपको प्रत्येक चरण पर निर्माण या समाशोधन और फिर से भरने के अतिरिक्त ओवरहेड को स्वीकार करना होगा।


8

आप ऐसा कुछ कर सकते हैं

import numpy as np

def cartesian_coord(*arrays):
    grid = np.meshgrid(*arrays)        
    coord_list = [entry.ravel() for entry in grid]
    points = np.vstack(coord_list).T
    return points

a = np.arange(4)  # fake data
print(cartesian_coord(*6*[a])

जो देता है

array([[0, 0, 0, 0, 0, 0],
   [0, 0, 0, 0, 0, 1],
   [0, 0, 0, 0, 0, 2],
   ..., 
   [3, 3, 3, 3, 3, 1],
   [3, 3, 3, 3, 3, 2],
   [3, 3, 3, 3, 3, 3]])

2
क्या मेशग्रीड के लिए 32 से अधिक सरणियों को स्वीकार करने के लिए NumPy प्राप्त करने का एक तरीका है? यह विधि मेरे लिए तब तक काम करती है जब तक मैं 32 से अधिक सरणियों से नहीं गुजरता।
जोएलमोब

8

निम्नलिखित सुन्न कार्यान्वयन लगभग होना चाहिए। दिए गए उत्तर की गति को 2x करें:

def cartesian2(arrays):
    arrays = [np.asarray(a) for a in arrays]
    shape = (len(x) for x in arrays)

    ix = np.indices(shape, dtype=int)
    ix = ix.reshape(len(arrays), -1).T

    for n, arr in enumerate(arrays):
        ix[:, n] = arrays[n][ix[:, n]]

    return ix

1
अछा लगता है। मेरे अल्पविकसित परीक्षणों से, यह सभी जोड़े, त्रिभुज, और {1,2, ..., 100} के 4-ट्यूपल के मूल उत्तर की तुलना में तेज़ दिखता है। उसके बाद, मूल उत्तर जीत जाता है। इसके अलावा, भविष्य के पाठकों के लिए {1, ..., n} के सभी k-tuples उत्पन्न करने की तलाश np.indices((n,...,n)).reshape(k,-1).Tहै।
जेम

यह केवल पूर्णांकों के लिए काम करता है, जबकि स्वीकृत उत्तर भी फ़्लोट्स के लिए काम करता है।
FJC

7

ऐसा लगता है कि आप अपने फ़ंक्शन का मूल्यांकन करने के लिए एक ग्रिड चाहते हैं, जिस स्थिति में आप उपयोग कर सकते हैं numpy.ogrid(ओपन) या numpy.mgrid(fleshed out):

import numpy
my_grid = numpy.mgrid[[slice(0,1,0.1)]*6]


4

शुद्ध NumPy, कोई पुनरावृत्ति, कोई सूची समझ, और लूप के लिए कोई स्पष्ट नहीं का उपयोग करके यहां एक और तरीका है। यह मूल उत्तर की तुलना में लगभग 20% धीमा है, और यह np.meshgrid पर आधारित है।

def cartesian(*arrays):
    mesh = np.meshgrid(*arrays)  # standard numpy meshgrid
    dim = len(mesh)  # number of dimensions
    elements = mesh[0].size  # number of elements, any index will do
    flat = np.concatenate(mesh).ravel()  # flatten the whole meshgrid
    reshape = np.reshape(flat, (dim, elements)).T  # reshape and transpose
    return reshape

उदाहरण के लिए,

x = np.arange(3)
a = cartesian(x, x, x, x, x)
print(a)

देता है

[[0 0 0 0 0]
 [0 0 0 0 1]
 [0 0 0 0 2]
 ..., 
 [2 2 2 2 0]
 [2 2 2 2 1]
 [2 2 2 2 2]]

3

1D सरणियों (या फ्लैट अजगर सूचियों) के कार्टेशियन उत्पाद के शुद्ध संख्यात्मक कार्यान्वयन के लिए, बस का उपयोग करें meshgrid(), अक्षों के साथ रोल करें transpose(), और वांछित ouput को फिर से खोलें:

 def cartprod(*arrays):
     N = len(arrays)
     return transpose(meshgrid(*arrays, indexing='ij'), 
                      roll(arange(N + 1), -1)).reshape(-1, N)

ध्यान दें कि इसमें अंतिम धुरी का कन्वेंशन सबसे तेजी से बदल रहा है ("सी स्टाइल" या "पंक्ति-प्रमुख")।

In [88]: cartprod([1,2,3], [4,8], [100, 200, 300, 400], [-5, -4])
Out[88]: 
array([[  1,   4, 100,  -5],
       [  1,   4, 100,  -4],
       [  1,   4, 200,  -5],
       [  1,   4, 200,  -4],
       [  1,   4, 300,  -5],
       [  1,   4, 300,  -4],
       [  1,   4, 400,  -5],
       [  1,   4, 400,  -4],
       [  1,   8, 100,  -5],
       [  1,   8, 100,  -4],
       [  1,   8, 200,  -5],
       [  1,   8, 200,  -4],
       [  1,   8, 300,  -5],
       [  1,   8, 300,  -4],
       [  1,   8, 400,  -5],
       [  1,   8, 400,  -4],
       [  2,   4, 100,  -5],
       [  2,   4, 100,  -4],
       [  2,   4, 200,  -5],
       [  2,   4, 200,  -4],
       [  2,   4, 300,  -5],
       [  2,   4, 300,  -4],
       [  2,   4, 400,  -5],
       [  2,   4, 400,  -4],
       [  2,   8, 100,  -5],
       [  2,   8, 100,  -4],
       [  2,   8, 200,  -5],
       [  2,   8, 200,  -4],
       [  2,   8, 300,  -5],
       [  2,   8, 300,  -4],
       [  2,   8, 400,  -5],
       [  2,   8, 400,  -4],
       [  3,   4, 100,  -5],
       [  3,   4, 100,  -4],
       [  3,   4, 200,  -5],
       [  3,   4, 200,  -4],
       [  3,   4, 300,  -5],
       [  3,   4, 300,  -4],
       [  3,   4, 400,  -5],
       [  3,   4, 400,  -4],
       [  3,   8, 100,  -5],
       [  3,   8, 100,  -4],
       [  3,   8, 200,  -5],
       [  3,   8, 200,  -4],
       [  3,   8, 300,  -5],
       [  3,   8, 300,  -4],
       [  3,   8, 400,  -5],
       [  3,   8, 400,  -4]])

यदि आप पहले अक्ष को सबसे तेज़ बदलना चाहते हैं ("फोरट्रान शैली" या "कॉलम-मेजर"), तो बस इस तरह के orderपैरामीटर को बदलें reshape():reshape((-1, N), order='F')


1

पंडों ने mergeपेश किया एक भोले, समस्या का तेज़ समाधान:

# given the lists
x, y, z = [1, 2, 3], [4, 5], [6, 7]

# get dfs with same, constant index 
x = pd.DataFrame({'x': x}, index=np.repeat(0, len(x))
y = pd.DataFrame({'y': y}, index=np.repeat(0, len(y))
z = pd.DataFrame({'z': z}, index=np.repeat(0, len(z))

# get all permutations stored in a new df
df = pd.merge(x, pd.merge(y, z, left_index=True, righ_index=True),
              left_index=True, right_index=True)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.