आइटम का क्रम बनाए रखते हुए सूची से यादृच्छिक नमूना प्राप्त करें?


84

मेरे पास एक क्रमबद्ध सूची है, मान लें: (यह वास्तव में सिर्फ संख्या नहीं है, इसकी वस्तुओं की एक सूची है जो एक जटिल उपभोक्ता एल्गोरिथ्म के साथ क्रमबद्ध हैं)

mylist = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ,9  , 10 ]

क्या कुछ अजगर फ़ंक्शन है जो मुझे आइटम का एन देगा, लेकिन ऑर्डर रखेगा?

उदाहरण:

randomList = getRandom(mylist,4)
# randomList = [ 3 , 6 ,7 , 9 ]
randomList = getRandom(mylist,4)
# randomList = [ 1 , 2 , 4 , 8 ]

आदि...


1
आप क्यों नहीं चाहते random.sampleऔर फिर छाँटें?
डेनियल लुबरोव

यह एक गैर तुच्छ एल्गोरिथ्म के साथ क्रमबद्ध है ... यह वास्तव में सिर्फ नंबर नहीं है
योचाई टिमर

4
डैनियल की टिप्पणी के लिए एक बहुत ही मामूली बदलाव: नमूना की एक श्रेणी [0,count), नमूना सॉर्ट करें (रेंज में संख्याओं का एक प्राकृतिक क्रम है), फिर mylistसूचकांकों के आधार पर मान निकालें । का उपयोग zipकर थोड़ा अलग यांत्रिकी के साथ एक ही प्रभाव को प्राप्त कर सकता है।

1
ठीक है, क्या मुझे एक उत्तर + उदाहरण मिल सकता है इसलिए मुझे कुछ स्वीकार करना होगा? :)
योचाई टिमर

जवाबों:


121

निम्नलिखित कोड आकार 4 का एक यादृच्छिक नमूना उत्पन्न करेगा:

import random

sample_size = 4
sorted_sample = [
    mylist[i] for i in sorted(random.sample(range(len(mylist)), sample_size))
]

(नोट: अजगर 2 के साथ, xrangeइसके बजाय बेहतर उपयोग range)

व्याख्या

random.sample(range(len(mylist)), sample_size)

मूल सूची के सूचकांकों का एक यादृच्छिक नमूना उत्पन्न करता है ।

इन सूचकांकों को मूल सूची में तत्वों के क्रम को संरक्षित करने के लिए क्रमबद्ध किया जाता है।

अंत में, सूची की समझ, मूल सूची से वास्तविक तत्वों को निकालती है, जो नमूना सूचकांकों को दी जाती है।


89

सरल-से-कोड O (N + K * log (K)) तरीका है

सूचकांकों के प्रतिस्थापन के बिना एक यादृच्छिक नमूना लें, सूचकांकों को क्रमबद्ध करें, और उन्हें मूल से लें।

indices = random.sample(range(len(myList)), K)
[myList[i] for i in sorted(indices)]

या अधिक संक्षेप में:

[x[1] for x in sorted(random.sample(enumerate(myList),K))]

अनुकूलित O (N) -टाइम, O (1) -ऑक्सिलरी-स्पेस तरीका

आप वैकल्पिक रूप से एक गणित चाल का उपयोग कर सकते हैं और myListगतिशील रूप से बदलती संभावना के साथ संख्याओं को उठाते हुए, बाएं से दाएं की ओर से चलने वाले हैं (N-numbersPicked)/(total-numbersVisited)। इस दृष्टिकोण का लाभ यह है कि यह एक O(N)एल्गोरिथ्म है क्योंकि इसमें छंटाई शामिल नहीं है!

from __future__ import division

def orderedSampleWithoutReplacement(seq, k):
    if not 0<=k<=len(seq):
        raise ValueError('Required that 0 <= sample_size <= population_size')

    numbersPicked = 0
    for i,number in enumerate(seq):
        prob = (k-numbersPicked)/(len(seq)-i)
        if random.random() < prob:
            yield number
            numbersPicked += 1

अवधारणा का प्रमाण और परीक्षण कि संभावनाएँ सही हैं :

5 घंटे के दौरान 1 ट्रिलियन छद्म आयामी नमूनों के साथ नकली:

>>> Counter(
        tuple(orderedSampleWithoutReplacement([0,1,2,3], 2))
        for _ in range(10**9)
    )
Counter({
    (0, 3): 166680161, 
    (1, 2): 166672608, 
    (0, 2): 166669915, 
    (2, 3): 166667390, 
    (1, 3): 166660630, 
    (0, 1): 166649296
})

संभावनाएं 1.0001 से कम कारक द्वारा वास्तविक संभावनाओं से भिन्न होती हैं। इस परीक्षण को फिर से चलाने के परिणामस्वरूप एक अलग क्रम होता है जिसका अर्थ है कि यह एक आदेश देने के लिए पक्षपाती नहीं है। के लिए कम नमूनों के साथ परीक्षण चलाना [0,1,2,3,4], k=3और [0,1,2,3,4,5], k=4इसके समान परिणाम थे।

संपादित करें: यह निश्चित नहीं है कि लोग गलत टिप्पणियों को क्यों वोट कर रहे हैं या अपवोट करने से डरते हैं ... नहीं, इस पद्धति में कुछ भी गलत नहीं है। =)

(टिप्पणियों में उपयोगकर्ता टेगन से एक उपयोगी नोट भी: यदि यह python2 है, तो आप xrange का उपयोग करना चाहेंगे, हमेशा की तरह, यदि आप वास्तव में अतिरिक्त स्थान की परवाह करते हैं।)

संपादित करें : प्रमाण: आकार की kआबादी से बाहर का सबसेट चुनने के समान वितरण (प्रतिस्थापन के बिना) को देखते हुए , हम एक मनमाने बिंदु पर 'बाएं' (0,1, ..., i-1) में विभाजन पर विचार कर सकते हैं। और 'सही' (i, i + 1, ..., len (seq))। यह देखते हुए कि हमने बाएं ज्ञात उपसमुच्चय से उठाया है, शेष को समान अज्ञात उपधारा पर समान वितरण से आना चाहिए, हालांकि पैरामीटर अब अलग हैं। विशेष रूप से, संभावना जिसमें एक चुना तत्व शामिल है , याseqlen(seq)inumbersPickedseq[i]#remainingToChoose/#remainingToChooseFrom(k-numbersPicked)/(len(seq)-i), इसलिए हम इसका अनुकरण करते हैं और परिणाम पर पुनरावृत्ति करते हैं। (यह समाप्त हो जाना चाहिए क्योंकि अगर #remainingToChoose == #remainingToChooseFrom, तो शेष सभी संभावनाएं 1 हैं।) यह एक संभावना वाले पेड़ के समान है जो गतिशील रूप से उत्पन्न होता है। मूल रूप से आप पूर्व चुनावों पर कंडीशनिंग द्वारा एक समान संभाव्यता वितरण का अनुकरण कर सकते हैं (जैसा कि आप संभाव्यता के पेड़ को बढ़ाते हैं, आप वर्तमान शाखा की संभावना को उठाते हैं जैसे कि यह पूर्व पत्तियों के समान है, अर्थात पूर्व विकल्पों पर वातानुकूलित; यह काम करेगा क्योंकि यह संभावना समान रूप से N / k) है।

संपादित करें : टिमोथी शील्ड्स ने जलाशय नमूनाकरण का उल्लेख किया है , जो len(seq)अज्ञात होने पर (जैसे कि एक जनरेटर अभिव्यक्ति के साथ) इस पद्धति का सामान्यीकरण है । विशेष रूप से "एल्गोरिथ्म आर" के रूप में विख्यात ओ-एन (ओ) और ओ (1) स्थान है यदि इन-प्लेस किया जाता है; इसमें पहला N तत्व लेना और धीरे-धीरे उन्हें प्रतिस्थापित करना (एक संकेत प्रमाण पर एक संकेत भी दिया गया है) शामिल है। विकिपीडिया पृष्ठ पर पाए जाने वाले जलाशय के नमूने के उपयोगी वितरित संस्करण और विविध प्रकार भी हैं।

संपादित करें : यहाँ एक और अधिक स्पष्ट अर्थ में इसे नीचे कोड करने का एक और तरीका है।

from __future__ import division
import random

def orderedSampleWithoutReplacement(seq, sampleSize):
    totalElems = len(seq)
    if not 0<=sampleSize<=totalElems:
        raise ValueError('Required that 0 <= sample_size <= population_size')

    picksRemaining = sampleSize
    for elemsSeen,element in enumerate(seq):
        elemsRemaining = totalElems - elemsSeen
        prob = picksRemaining/elemsRemaining
        if random.random() < prob:
            yield element
            picksRemaining -= 1

from collections import Counter         
Counter(
    tuple(orderedSampleWithoutReplacement([0,1,2,3], 2))
    for _ in range(10**5)

)


1
@ पीएसटी: कोई नुकसान नहीं, O(N)बल्कि एक स्पीडअपO(N log(N))
नन्जेजेको

1
बहुत अच्छा, मैं सोच रहा था कि इस रैखिक दृष्टिकोण को कैसे किया जाए। क्या इस सूत्र में विकिपीडिया पृष्ठ है? :)
जोचन रिट्जेल

2
मुझे आश्चर्य है कि इस उत्तर में अधिक अपवित्रता नहीं है, यह वास्तव में बताता है कि समाधान कैसे काम करता है (और दूसरा समाधान प्रदान करता है!), जैसा कि पहले उत्तर के विपरीत है जो सिर्फ एक-पंक्ति स्निपेट है - मुझे कोई विचार नहीं दे रहा है या क्यों यह कैसे काम किया।
पागल २

1
अच्छा समाधान Ninjagecko। अगर कोई इसे लिखने में रुचि रखता है तो आपके समाधान के लिए एक अच्छा प्रेरक प्रमाण है।
नील जी

3
अच्छा समाधान! from __future__ import divisionपायथन 2 चलाने वालों के लिए जोड़ना न भूलें ।
xApple

7

हो सकता है कि आप केवल सूचकांकों के नमूने उत्पन्न कर सकते हैं और फिर अपनी सूची से आइटम एकत्र कर सकते हैं।

randIndex = random.sample(range(len(mylist)), sample_size)
randIndex.sort()
rand = [mylist[i] for i in randIndex]

4

जाहिर तौर पर random.sampleअजगर 2.3 में पेश किया गया था

तो इसके तहत संस्करण के लिए, हम फेरबदल का उपयोग कर सकते हैं (उदाहरण के लिए 4 आइटम):

myRange =  range(0,len(mylist)) 
shuffle(myRange)
coupons = [ bestCoupons[i] for i in sorted(myRange[:4]) ]

4
आप पायथन 2.2 का उपयोग कर रहे हैं ?! आपको अपग्रेड करना चाहिए ... यह तरीका पुराना है।
कैटरील

1
ठीक है, इसका हमारे पास सर्वरों पर है .. सिस्टम-वाइड अपडेट करना बहुत अधिक नौकरशाही है
योचाई टिमर

-2

random.sample इसे कार्यान्वित करता है।

>>> random.sample([1, 2, 3, 4, 5],  3)   # Three samples without replacement
[4, 1, 5]

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