[英]Random select is taking too much memory in Python
我正在嘗試選擇要從CSV文件讀取的行,選擇基於:
n = 123456789
s = n//10
# skip value will be used with skip_row in pd.read_csv
skip = sorted(random.sample(range(1, n+1), k=(n-s))
此行導致腳本由於高RAM使用率而崩潰。 文件大小接近5 GB,這就是為什么需要隨機讀取10%的原因。 期望的是random.select不會占用太多內存,令人驚訝的是它會占用內存(如任務管理器所示,該內存可達5 GB甚至更多)
有沒有一種有效的方法來選擇要跳過的行。
感謝所有評論,我設法編寫了一個代碼,可以在31秒內(使用Core i5 PC)找到所有n = 123456789個標簽/樣本,這是:
import random
import time
start_time = time.time()
n=123456789
if n>123456789:
step_size = int(1e7)
else:
step_size=n//10
no_of_steps = n//step_size-1
s= step_size//10
xx=list(); i=0
while (i<no_of_steps):
xx= xx + sorted(random.sample(range(i*step_size, (i+1)*step_size-1), k= s))
i +=1
print("Size of selected samples", len(xx))
print("Percentage achieved", len(xx)/n)
print("Time needed to pick 10% from ", n, "samples is:" , time.time()-start_time)
print("-------- \n Show a few samples", xx[:10], '...', xx[-10:])
確實,您當前的方法非常繁瑣。 它實際上是試圖在內存中存儲和處理(采樣+排序)123,456,789個python整數數組。 我不認為您實際上確實需要事先在數組中“跳過”行。 如果是這樣,您可以改為使用generator
。 您可以按照以下方法generating
樣本集:
def random_sampler_generator(input_seq, skip_chance=0.5):
for i in input_seq:
if random.random() > skip_chance:
yield i
for m in random_sampler_generator(xrange(1, n+1), skip_chance=0.1):
print m
這要慢一些,因為它調用了random.random()超過1億次,但是由於yield
函數,它的存儲效率極高。 出於完全相同的原因,您可能還應該使用xrange
代替range
。
在這里, random_sampler_generator
使用啟發式采樣方法,因為不能保證您擁有X%
但是隨着輸入集大小的增加,結果將趨向於X%
。 skip_chance
期望一個介於0和1之間的浮點數,其中.1被跳過10% skip_chance
5被跳過50% skip_chance
75被跳過75%, skip_chance
。
您需要數據的隨機子集,但是您抱怨數據太大而無法駐留在內存中。 因此,請使用外部排序將數據整理成合適的形狀。
從source.csv
讀取所有N條記錄,在PRNG或計數器的SHA1或每個記錄的SHA1(對於唯一記錄)之前添加隨機值,並將N條結果記錄發送到/usr/bin/sort
或關系數據庫中的索引表。
現在,您的記錄以隨機順序出現在磁盤上,並且您可以通過讀取前S條記錄輕松進行第一個實驗,而不會影響內存。 記住您從哪里開始,您可以通過閱讀下一個S記錄來執行第二次實驗。 或者,另一種方法是詢問最初的SHA1 nybble為'3'的前S條記錄,另一條以'5'開始的前S條記錄,以此類推。 由於您創建了非重疊分區,因此實驗設計將不必擔心會在多個實驗中隨機遇到相同的源記錄。
編輯:產生不替換的隨機索引不同於產生實驗數據的隨機倍數。 該請求是為了節省內存。 使用CSV輸入指定的OP問題,此方法可以在內存有限(永遠不與s
成比例分配)的情況下快速運行。 尾部-4在最后給出結果,prepend在5分鍾(302秒)內運行於OP所需的數據大小,而這種排序在大約一分鍾(77秒)內運行:
$ time env LC_ALL=C gsort --parallel=8 /tmp/rand.csv > /tmp/shuffled.csv
#! /usr/bin/env python3
import base64
import csv
import hashlib
import re
import time
def prepend(infile, outfile):
"""Turns the input CSV into one that has hex hashes in column 1."""
with open(infile) as fin, open(outfile, 'w') as fout:
insheet = csv.reader(fin)
outsheet = csv.writer(fout)
for i, word in insheet:
hex = hashlib.sha224(word.encode()).hexdigest()[:12]
outsheet.writerow((hex, i, word))
def gen(outfile, n=123456789, seed='foo'):
num_word_re = re.compile(r'([a-zA-Z]\w{7})')
just_alnum = str.maketrans('', '', '+/')
with open(outfile, 'w') as fout:
sheet = csv.writer(fout)
for i in range(1, n + 1):
sha = hashlib.sha224(f'{seed}{str(i)}'.encode())
dig = sha.digest()
text = base64.b64encode(dig).decode()
m = num_word_re.search(text.translate(just_alnum))
sheet.writerow((i, str(dig[-1] % 10) + m.group(1)))
if __name__ == '__main__':
big = '/tmp/big.csv'
gen(big)
t0 = time.time()
prepend(big, '/tmp/rand.csv')
print('%.3f sec.' % (time.time() - t0))
==> /tmp/rand.csv <==
b784b9e91557,123456786,8vdzlXU0k
6b545b9a8be8,123456787,1tfemzO0R
40ae0bfea89b,123456788,0Ua8LYuK8
c461c832b5aa,123456789,0y91nshi6
==> /tmp/shuffled.csv <==
ffffff1db601,117611393,3TCHPS1tD
ffffff3d6962,110031982,7jzzKwrNz
ffffff468062,20413362,6juuxuPN2
ffffff81de0a,102223550,2p91NhYXq
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.