簡體   English   中英

隨機選擇占用了Python太多的內存

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM