2016-11-27 8 views
1

次のコードでは、アイテムとユーザーのリストを作成しました。私は非常に人気のある一般的なアイテムとレギュラーアイテムの3つの異なるリストにアイテムを分けました。Python - 異なる分布を持つ 'x'リストのサンプル

import numpy as np 


N_USERS = 20000 
N_ITEMS = 1000 

items = range(0, N_ITEMS) 
users = range(0, N_USERS) 

vpop = int(len(items)*0.1) 
pop = int(len(items)*0.3) 

np.random.shuffle(items) 
vpop_items = items[:vpop] 
pop_items = items[vpop:pop] 
reg_items = items [pop:] 

Xのサンプルを別の配信リストからサンプルしたいとします。たとえば、次のように

list_of_items = sample(vpop_items, pop_items, reg_items, p = [0.5, 0.35, 0.15], X) 

Xは私がしたいサンプルの数であり、Pはリスト(vpop_itemspop_itemsreg_items)に対応するディストリビューションのリストです。

最後にX「アイテム」がlist_of_itemsにあります。例えば、X = 100合計で100サンプル、vpop_itemsから0.5チャンス、pop_itemsから0.35チャンス、reg_itemsから0.15チャンスが必要です。サンプリングは交換せずに行う必要があります。つまり、項目を複数回選択することはできません。

+0

私が正しくあなたを理解していれば、あなたがp' 'によって与えられた確率で3つのリストからサンプリングしたいです。そして、それぞれのリストからランダムな項目を選択しますか?選択したアイテムを削除する必要がありますか?最後に、 'sample'を実装しようとしましたか? – agold

+0

私はX項目をサンプルしたいです。 100アイテムを言うことができます。私はvpop_itemsから0.5チャンスで上記のすべてのリストから100項目をサンプリングしたいと思います。 pop_itemsから0.35チャンス。そしてreg_itemsから0.15チャンス –

+1

あなたはまだ何か試しましたか? vpopから 'p <0.5'を引き出す場合は0から1までの乱数' p'を、それ以外の場合は 'p <0.85'をpopから引き出し、そうでなければregから引き出します。 'X '回繰り返す。 – Julien

答えて

2

ここでは、必要な処理を行うプレーンなPythonアルゴリズムを示します。現在行っていることよりも効率的ですが、よりスマートな方法があると確信しています。 :)

numを必要なサンプルの総数とします。最初にnumの乱数を0 - 1の範囲で生成し、それぞれの確率範囲内で発生する数の数を維持しながら、望ましい累積確率に対してそれらをテストします。次に、サンプル・サイズとして最初のステップで見つかったカウントを使用して、各シーケンスをサンプリングします。最後に、これらのサンプルを一緒にシャッフルします。

以下のコードでは、コードのテスト中に何が起こっているのかを簡単に確認できるようにシャッフルする行をコメントアウトしました。

from random import seed, random, sample, shuffle 
from itertools import accumulate 

def multi_sample(seqs, probs, num): 
    ''' Sample from each sequence in list/tuple `seqs` with the corresponding 
     probability in list/tuple `probs`. Return a list containing `num` samples 
    ''' 
    # Compute the cumulative probability 
    # This really should raise ValueError if aprobs[-1] != 1.0 
    # and we ought to check that len(seqs) == len(probs)... 
    aprobs = list(accumulate(probs)) 

    # Determine how many samples to take from each seq 
    counts = [0] * len(seqs) 
    for _ in range(num): 
     x = random() 
     for i, p in enumerate(aprobs): 
      if x < p: 
       break 
     counts[i] += 1 

    lst = [] 
    for seq, count in zip(seqs, counts): 
     lst.extend(sample(seq, count)) 

    #shuffle(lst) 
    return lst 

# Test 

N_ITEMS = 1000 
items = list(range(N_ITEMS)) 
vpop = int(N_ITEMS * 0.1) 
pop = int(N_ITEMS * 0.3) 

#shuffle(items) 
vpop_items = items[:vpop] 
pop_items = items[vpop:pop] 
reg_items = items[pop:] 

all_items = (vpop_items, pop_items, reg_items) 

list_of_items = multi_sample(all_items, probs=[0.5, 0.35, 0.15], num=100) 
print(list_of_items) 

# Verify 

#list_of_items.sort() 
#print(list_of_items) 

# Should be ~50 
print(sum(1 for x in list_of_items if x < vpop)) 
# Should be ~35 
print(sum(1 for x in list_of_items if vpop <= x < pop)) 

典型的な出力

[65, 16, 81, 97, 30, 33, 52, 92, 96, 72, 50, 4, 75, 7, 44, 18, 90, 9, 91, 56, 85, 28, 84, 88, 76, 21, 14, 77, 8, 59, 22, 34, 93, 95, 63, 10, 99, 41, 60, 36, 66, 2, 13, 64, 51, 43, 11, 106, 153, 235, 189, 132, 150, 226, 196, 247, 245, 194, 172, 227, 202, 256, 163, 205, 131, 192, 295, 147, 246, 108, 291, 155, 128, 171, 141, 124, 102, 210, 294, 284, 276, 148, 122, 290, 948, 566, 894, 884, 310, 476, 562, 313, 357, 846, 794, 317, 335, 599, 370, 988] 
47 
37 

この関数が失敗することがあるので注意してください:あなたはsample(seq, count)を呼び出す場合、それはValueError: Sample larger than populationを調達する場所count > len(seq)。そのため、numが発生しないように十分に小さくする必要があります。完全に安全であるためには、numが< =が最小シーケンスの長さよりも大きいことを確認してください。与えられたデータでは、numは100で、最小のシーケンスはvpop_itemsです。これは100個のアイテムを含んでいますので、心配する必要はありません。

この重要な点を私の注意を引くためにAndras Deakに感謝します。


私が先に言ったように、そこには、これを行うためのよりスマートな方法であることがバインドされています:むしろループでcountsを計算するよりも、我々は、ちょうど適切な数学を使用して直接それらの数を生成することができるはずですが、私はよ私はそれをする方法を知らない(または覚えていない)のではないかと恐れている。もちろん、 "チート"でした。 :)与えられたデータを使って、vpop_itemsから約50個、pop_itemsから35個、残りの15個をreg_itemsから求めます。したがって、counts[50, 35, 15]に設定し、合計を100に保つように注意しながら、各カウントに小さなランダムな調整を加えることができます。

+0

ちょっとthats素晴らしいソリューションです。私はサンプルサイズを乗り越えないように数えるためにガードを追加します。そのサンプルを投げる場所を決めるだけでいい –

1

ここでは、3つのカテゴリのみを使用する簡単な方法があります。これはおそらく、3つの選択肢をループするだけで、あまりにも多くのカテゴリではうまく調整できません。

まず、一定の擬似乱数を生成して、どのグループからどのサンプルを採取するかを決定します。次に、サンプリングを実行するnumpy.random.choiceを使用:

import numpy as np 

# data setup 
N_ITEMS = 1000 

items = list(range(0, N_ITEMS)) #python 3 

vpop = int(len(items)*0.1) 
pop = int(len(items)*0.3) 

np.random.shuffle(items) 
vpop_items = items[:vpop] 
pop_items = items[vpop:pop] 
reg_items = items[pop:] 

# actual answer 
def randsample(data1,data2,data3,probs,samples): 
    # "samples" is the number of samples to take 
    uniforms = np.random.rand(samples) 
    inds1 = uniforms<=probs[0] 
    inds2 = (probs[0]<uniforms) & (uniforms<=probs[0]+probs[1]) 
    inds3 = ~(inds1|inds2) 

    output = np.empty(samples,dtype=type(data1[0])) #set dtype 
    for ind,dat in zip((inds1,inds2,inds3),(data1,data2,data3)): 
     output[ind] = np.random.choice(dat,ind.sum(),replace=False) 

    #TODO: guard against depletion of one of the data sources... 

    return output 

res = randsample(vpop_items, pop_items, reg_items, [0.5, 0.35, 0.15], 100) 
uniforms

アレイは、各サンプル点について0と1の間で疑似ランダムに均一な番号を含みます。これらの数値を入力で与えられた(累積)確率と比較して、それぞれのカテゴリから所定の確率で選択する。一般に、与えられたサンプルについて、対応する擬似乱数がsum(probs[:i])sum(probs[:i+1])の間にある場合、タイプiから選択します。 3つのインデックス配列inds1,inds2,inds3は、出力サンプルを分割し、与えられたサンプルポイントのカテゴリのタイプを指定します。次に、与えられたカテゴリのランダムな選択に基づいて、出力配列の対応するインデックスを設定するだけです。

だけで得られた試​​料は正しいと代表的なものであることを確認する:

>>> np.in1d(res, vpop_items).sum()/res.size 
0.53000000000000003 
>>> np.in1d(res, pop_items).sum()/res.size 
0.34000000000000002 
>>> np.in1d(res, reg_items).sum()/res.size 
0.13 
>>> (np.in1d(res, reg_items) & np.in1d(res,pop_items)).sum() 
0 
>>> (np.in1d(res, reg_items) & np.in1d(res,vpop_items)).sum() 
0 
>>> (np.in1d(res, pop_items) & np.in1d(res,vpop_items)).sum() 
0 
関連する問題