सबसे कुशल तरीका है कि सुन्न सरणी में मोड ढूंढें


89

मेरे पास एक 2 डी सरणी है जिसमें पूर्णांक (सकारात्मक या नकारात्मक दोनों) हैं। प्रत्येक पंक्ति एक विशेष स्थानिक साइट के लिए समय के साथ मूल्यों का प्रतिनिधित्व करती है, जबकि प्रत्येक कॉलम एक निश्चित समय के लिए विभिन्न स्थानिक साइटों के लिए मूल्यों का प्रतिनिधित्व करता है।

तो अगर सरणी की तरह है:

1 3 4 2 2 7
5 2 2 1 4 1
3 3 2 2 1 1

परिणाम होना चाहिए

1 3 2 2 2 1

ध्यान दें कि जब मोड के लिए कई मान होते हैं, तो किसी एक को (यादृच्छिक रूप से चयनित) मोड के रूप में सेट किया जा सकता है।

मैं एक समय में मोड एक खोजने वाले कॉलम पर पुनरावृति कर सकता हूं, लेकिन मुझे उम्मीद थी कि ऐसा करने के लिए कुछ खराबी हो सकती है। या अगर वहाँ एक चाल है कि कुशलता से पाशन के बिना खोजने के लिए।



1
@ tom10: आपका मतलब है scipy.stats.mode () , है ना? अन्य एक नकाबपोश सरणी का उत्पादन करने के लिए लगता है।
fgb

@fgb: सही, सुधार के लिए धन्यवाद (और आपके उत्तर के लिए +1)।
टमाटर 10

जवाबों:


121

जांचें scipy.stats.mode()(@ tom10 की टिप्पणी से प्रेरित):

import numpy as np
from scipy import stats

a = np.array([[1, 3, 4, 2, 2, 7],
              [5, 2, 2, 1, 4, 1],
              [3, 3, 2, 2, 1, 1]])

m = stats.mode(a)
print(m)

आउटपुट:

ModeResult(mode=array([[1, 3, 2, 2, 1, 1]]), count=array([[1, 2, 2, 2, 1, 2]]))

जैसा कि आप देख सकते हैं, यह दोनों मोड के साथ-साथ मायने रखता है। आप सीधे मोड का चयन कर सकते हैं m[0]:

print(m[0])

आउटपुट:

[[1 3 2 2 1 1]]

4
तो अपने आप में कोई भी इस तरह की कार्यक्षमता का समर्थन नहीं करता है?
निक

1
जाहिरा तौर पर नहीं, लेकिन डरपोक का कार्यान्वयन केवल सुन्न पर निर्भर करता है , इसलिए आप बस उस कोड को अपने स्वयं के फ़ंक्शन में कॉपी कर सकते हैं।
fgb

12
भविष्य में इसे देखने वाले लोगों के लिए बस एक नोट: आपको import scipy.statsस्पष्ट रूप से इसकी आवश्यकता है , यह तब शामिल नहीं होता है जब आप बस एक करते हैं import scipy
ffledgling

1
क्या आप बता सकते हैं कि मोड मान और गिनती प्रदर्शित करना वास्तव में कैसा है? मैं प्रदान किए गए इनपुट के साथ आउटपुट को संबंधित नहीं कर सका।
राहुल

2
@ राहुल: आपको तयशुदा दूसरे तर्क पर विचार करना होगा axis=0। उपरोक्त कोड इनपुट के प्रति कॉलम मोड की रिपोर्ट कर रहा है। गिनती हमें बता रही है कि उसने प्रत्येक कॉलम में रिपोर्ट मोड को कितनी बार देखा है। यदि आप समग्र मोड चाहते हैं, तो आपको निर्दिष्ट करने की आवश्यकता है axis=None। अधिक जानकारी के लिए, कृपया docs.scipy.org/doc/scipy/reference/generated/…
fgb

22

अपडेट करें

scipy.stats.modeसमारोह में काफी इस पोस्ट के बाद से अनुकूलित किया गया है, और सिफारिश की तरीका होगा

पुराना उत्तर

यह एक मुश्किल समस्या है, क्योंकि वहाँ एक अक्ष के साथ मोड की गणना करने के लिए बहुत कुछ नहीं है। समाधान 1-डी सरणियों के लिए सीधे आगे है, जहां numpy.bincountआसान है, साथ ही numpy.uniqueसाथ return_countsarg के रूप में True। सबसे आम एन-आयामी फ़ंक्शन जो मैं देख रहा हूं वह scipy.stats.mode है, हालांकि यह निषेधात्मक रूप से धीमा है- विशेष रूप से कई अद्वितीय मूल्यों के साथ बड़े सरणियों के लिए। समाधान के रूप में, मैंने इस फ़ंक्शन को विकसित किया है, और इसका भारी उपयोग करता हूं:

import numpy

def mode(ndarray, axis=0):
    # Check inputs
    ndarray = numpy.asarray(ndarray)
    ndim = ndarray.ndim
    if ndarray.size == 1:
        return (ndarray[0], 1)
    elif ndarray.size == 0:
        raise Exception('Cannot compute mode on empty array')
    try:
        axis = range(ndarray.ndim)[axis]
    except:
        raise Exception('Axis "{}" incompatible with the {}-dimension array'.format(axis, ndim))

    # If array is 1-D and numpy version is > 1.9 numpy.unique will suffice
    if all([ndim == 1,
            int(numpy.__version__.split('.')[0]) >= 1,
            int(numpy.__version__.split('.')[1]) >= 9]):
        modals, counts = numpy.unique(ndarray, return_counts=True)
        index = numpy.argmax(counts)
        return modals[index], counts[index]

    # Sort array
    sort = numpy.sort(ndarray, axis=axis)
    # Create array to transpose along the axis and get padding shape
    transpose = numpy.roll(numpy.arange(ndim)[::-1], axis)
    shape = list(sort.shape)
    shape[axis] = 1
    # Create a boolean array along strides of unique values
    strides = numpy.concatenate([numpy.zeros(shape=shape, dtype='bool'),
                                 numpy.diff(sort, axis=axis) == 0,
                                 numpy.zeros(shape=shape, dtype='bool')],
                                axis=axis).transpose(transpose).ravel()
    # Count the stride lengths
    counts = numpy.cumsum(strides)
    counts[~strides] = numpy.concatenate([[0], numpy.diff(counts[~strides])])
    counts[strides] = 0
    # Get shape of padded counts and slice to return to the original shape
    shape = numpy.array(sort.shape)
    shape[axis] += 1
    shape = shape[transpose]
    slices = [slice(None)] * ndim
    slices[axis] = slice(1, None)
    # Reshape and compute final counts
    counts = counts.reshape(shape).transpose(transpose)[slices] + 1

    # Find maximum counts and return modals/counts
    slices = [slice(None, i) for i in sort.shape]
    del slices[axis]
    index = numpy.ogrid[slices]
    index.insert(axis, numpy.argmax(counts, axis=axis))
    return sort[index], counts[index]

परिणाम:

In [2]: a = numpy.array([[1, 3, 4, 2, 2, 7],
                         [5, 2, 2, 1, 4, 1],
                         [3, 3, 2, 2, 1, 1]])

In [3]: mode(a)
Out[3]: (array([1, 3, 2, 2, 1, 1]), array([1, 2, 2, 2, 1, 2]))

कुछ बेंचमार्क:

In [4]: import scipy.stats

In [5]: a = numpy.random.randint(1,10,(1000,1000))

In [6]: %timeit scipy.stats.mode(a)
10 loops, best of 3: 41.6 ms per loop

In [7]: %timeit mode(a)
10 loops, best of 3: 46.7 ms per loop

In [8]: a = numpy.random.randint(1,500,(1000,1000))

In [9]: %timeit scipy.stats.mode(a)
1 loops, best of 3: 1.01 s per loop

In [10]: %timeit mode(a)
10 loops, best of 3: 80 ms per loop

In [11]: a = numpy.random.random((200,200))

In [12]: %timeit scipy.stats.mode(a)
1 loops, best of 3: 3.26 s per loop

In [13]: %timeit mode(a)
1000 loops, best of 3: 1.75 ms per loop

संपादित करें: एक पृष्ठभूमि के अधिक प्रदान की और अधिक स्मृति कुशल होने के लिए दृष्टिकोण को संशोधित किया


1
कृपया इसे स्काइप के स्टैट मॉड्यूल में योगदान दें ताकि अन्य लोग भी इससे लाभान्वित हो सकें।
ARF

बड़े अंतर ndarrays के साथ उच्च आयामी समस्याओं के लिए, आपका समाधान scipy.stats.mode की तुलना में अभी भी बहुत तेज है। मुझे 4x250x250x500 ndarray के पहले अक्ष के साथ मोड की गणना करनी थी, और आपके फ़ंक्शन को 10s लगे, जबकि scipy.stats.mode को लगभग 600s लगे।
CheshireCat

11

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

(_, idx, counts) = np.unique(a, return_index=True, return_counts=True)
index = idx[np.argmax(counts)]
mode = a[index]

जब len (np.argmax (मायने रखता है))> 1 को मोड को त्यागने के लिए याद रखें, तो यह भी मान्य करने के लिए कि क्या यह वास्तव में आपके डेटा के केंद्रीय वितरण का प्रतिनिधि है आप जाँच सकते हैं कि यह आपके मानक विचलन अंतराल के अंदर आता है या नहीं।


यदि आप एक अक्ष निर्दिष्ट नहीं करते हैं, तो np.argmax 1 से अधिक लंबाई के साथ कभी कुछ वापस करता है?
loganjones16

10

एक साफ समाधान जो केवलnumpy (न scipyही Counterवर्ग) का उपयोग करता है :

A = np.array([[1,3,4,2,2,7], [5,2,2,1,4,1], [3,3,2,2,1,1]])

np.apply_along_axis(lambda x: np.bincount(x).argmax(), axis=0, arr=A)

सरणी ([1, 3, 2, 2, 1, 1])


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

यह एक भयानक समाधान है। वास्तव में एक खामी है scipy.stats.mode। जब सबसे अधिक घटना (कई मोड) वाले कई मान होते हैं, तो यह एक उम्मीद फेंक देगा। लेकिन यह विधि स्वचालित रूप से "पहला मोड" लेगी।
क्रिस्टोफर

5

यदि आप केवल खसखस ​​का उपयोग करना चाहते हैं:

x = [-1, 2, 1, 3, 3]
vals,counts = np.unique(x, return_counts=True)

देता है

(array([-1,  1,  2,  3]), array([1, 1, 1, 2]))

और इसे निकालें:

index = np.argmax(counts)
return vals[index]

इस पद्धति की तरह क्योंकि यह न केवल पूर्णांक का समर्थन करता है, बल्कि फ्लोट और यहां तक ​​कि तार भी है!
क्रिस्टोफर

3

मुझे लगता है कि काउंटर क्लास का उपयोग करने के लिए एक बहुत ही सरल तरीका होगा। फिर आप यहां बताए गए काउंटर इंस्टेंस के सबसे अधिक (असामान्य) फ़ंक्शन का उपयोग कर सकते हैं

1-डी सरणियों के लिए:

import numpy as np
from collections import Counter

nparr = np.arange(10) 
nparr[2] = 6 
nparr[3] = 6 #6 is now the mode
mode = Counter(nparr).most_common(1)
# mode will be [(6,3)] to give the count of the most occurring value, so ->
print(mode[0][0])    

कई आयामी सरणियों (थोड़ा अंतर) के लिए:

import numpy as np
from collections import Counter

nparr = np.arange(10) 
nparr[2] = 6 
nparr[3] = 6 
nparr = nparr.reshape((10,2,5))     #same thing but we add this to reshape into ndarray
mode = Counter(nparr.flatten()).most_common(1)  # just use .flatten() method

# mode will be [(6,3)] to give the count of the most occurring value, so ->
print(mode[0][0])

यह एक कुशल कार्यान्वयन हो सकता है या नहीं भी हो सकता है, लेकिन यह सुविधाजनक है।


2
from collections import Counter

n = int(input())
data = sorted([int(i) for i in input().split()])

sorted(sorted(Counter(data).items()), key = lambda x: x[1], reverse = True)[0][0]

print(Mean)

Counter(data)आवृत्ति में गिना जाता है और एक defaultdict देता है। sorted(Counter(data).items())कुंजियों का उपयोग करके सॉर्ट करें, आवृत्ति नहीं। अंत में, दूसरे के साथ सॉर्ट की गई आवृत्ति का उपयोग करने की आवश्यकता होती है key = lambda x: x[1]। रिवर्स पायथन को सबसे बड़ी से सबसे छोटी आवृत्ति को सॉर्ट करने के लिए कहता है।


चूँकि यह प्रश्न 6 साल पहले पूछा गया था, इसलिए यह सामान्य है कि उसे बहुत प्रतिष्ठा नहीं मिली।
ज़लीहा बेक्टास

1

पायथन में सबसे आसान तरीका एक सूची या सरणी ए का मोड प्राप्त करने के लिए

   import statistics
   print("mode = "+str(statistics.(mode(a)))

बस

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.