복붙노트

[PYTHON] 파이썬에서 거대한 CSV 파일의 무작위 라인 읽기

PYTHON

파이썬에서 거대한 CSV 파일의 무작위 라인 읽기

나는이 꽤 큰 CSV 파일 (15Gb)을 가지고 있으며 그것으로부터 약 1 백만개의 무작위 라인을 읽을 필요가있다. 내가 볼 수 있고 구현하는 한, 파이썬의 CSV 유틸리티는 파일에서 순차적으로 반복 만 허용합니다.

임의의 선택을 사용하기 위해 모든 파일을 메모리로 읽어들이는 데 매우 많은 메모리가 필요하며 모든 파일을 골라 일부 값을 버리고 다른 파일을 선택하는 데 많은 시간이 소요되므로 CSV 파일에서 임의의 줄을 선택해야합니다. 그 줄만 읽으시겠습니까?

나는 성공하지 않고 노력했다.

   import csv

    with open('linear_e_LAN2A_F_0_435keV.csv') as file:
        reader = csv.reader(file)
        print reader[someRandomInteger]

CSV 파일의 샘플 :

331.093,329.735 
251.188,249.994 
374.468,373.782 
295.643,295.159 
83.9058,0 
380.709,116.221 
352.238,351.891 
183.809,182.615 
257.277,201.302
61.4598,40.7106

해결법

  1. ==============================

    1.

    import random
    
    filesize = 1500                 #size of the really big file
    offset = random.randrange(filesize)
    
    f = open('really_big_file')
    f.seek(offset)                  #go to random position
    f.readline()                    # discard - bound to be partial line
    random_line = f.readline()      # bingo!
    
    # extra to handle last/first line edge cases
    if len(random_line) == 0:       # we have hit the end
        f.seek(0)
        random_line = f.readline()  # so we'll grab the first line instead
    

    @AndreBoos가 지적했듯이,이 접근법은 편향된 선택으로 이어질 것입니다. 라인의 최소 및 최대 길이를 알고있는 경우 다음을 수행하여 이러한 편향을 제거 할 수 있습니다.

    (이 경우) min = 3, max = 15라고 가정 해 봅시다.

    1) 이전 행의 길이 (Lp)를 찾으십시오.

    Lp = 3이면 선은 가장 편향됩니다. 그러므로 우리는 100 % 시간을 가져야합니다. Lp = 15이면 선은 가장 편향되어 있습니다. 5 * 선택 가능성이 높기 때문에 시간의 20 % 만 가져 가야합니다.

    우리는 무작위로 시간의 X % 라인을 유지하여 이것을 수행합니다.

    X = 최소 / Lp

    라인을 지키지 않으면 주사위 굴리기가 잘 될 때까지 또 다른 무작위로 선택합니다. :-)

  2. ==============================

    2.정확하게 1 백만 라인을 필요로하지 않고 CSV 파일에 미리 줄 번호를 알고 있다고 가정하면 저장소 샘플링을 사용하여 임의의 하위 집합을 검색 할 수 있습니다. 데이터를 반복하고 각 라인마다 선택되는 라인 확률을 결정하십시오. 그렇게하면 데이터를 한 번만 전달하면됩니다.

    정확하게 1 백만 라인을 필요로하지 않고 CSV 파일에 미리 줄 번호를 알고 있다고 가정하면 저장소 샘플링을 사용하여 임의의 하위 집합을 검색 할 수 있습니다. 데이터를 반복하고 각 라인마다 선택되는 라인 확률을 결정하십시오. 그렇게하면 데이터를 한 번만 전달하면됩니다.

    무작위 샘플을 자주 추출해야하지만 실제 데이터 세트가 자주 변경되지 않으면 (데이터 세트가 변경 될 때마다 항목 수를 추적하면되므로)이 방법이 유용합니다.

    chances_selected = desired_num_results / total_entries
    for line in csv.reader(file):
       if random() < chances_selected:
            result.append(line)
    
  3. ==============================

    3.파일에서 임의의 행을 선택하는 확률 적 방법의 변형을 사용할 수 있습니다.

    파일에서 임의의 행을 선택하는 확률 적 방법의 변형을 사용할 수 있습니다.

    선택한 단일 번호를 유지하는 대신 크기 C의 버퍼를 유지할 수 있습니다. 각 줄 번호 n에 대해 N 줄이있는 파일에서 원래 줄이 아닌 확률 C / n으로 줄을 선택하려고합니다 1 / n. 번호가 선택되면 C 길이 버퍼에서 임의의 위치를 ​​선택하여 축출합니다.

    다음은 작동 방식입니다.

    import random
    
    C = 2
    fpath = 'somelines.txt'
    buffer = []
    
    f = open(fpath, 'r')
    for line_num, line in enumerate(f):
        n = line_num + 1.0
        r = random.random()
        if n <= C:
            buffer.append(line.strip())
        elif r < C/n:
            loc = random.randint(0, C-1)
            buffer[loc] = line.strip()
    

    이 경우 파일을 한 번 통과해야하므로 (선형 시간이므로) 파일에서 정확히 C 행을 반환합니다. 각 라인은 확률 C / N이 선택됩니다.

    위의 작업을 확인하기 위해 a, b, c, d, e를 포함하는 5 줄의 파일을 만들었습니다. C = 2로 코드를 10,000 번 실행했습니다. 이것은 5의 균등 분포에 대해 2 (가능한 10) 개의 선택을 선택해야합니다. 결과 :

    a,b: 1046
    b,c: 1018
    b,e: 1014
    a,c: 1003
    c,d: 1002
    d,e: 1000
    c,e: 993
    a,e: 992
    a,d: 985
    b,d: 947
    
  4. ==============================

    4.임의의 라인을 여러 번 잡아 내고 싶다면 (예 : 기계 학습을위한 미니 배치) 메모리에로드하지 않고 거대한 파일을 한 번 스캔해도 괜찮습니다. 빨리 마리아 Zverina의 답변을 기반으로 라인을 잡아 찾기를 사용하십시오.

    임의의 라인을 여러 번 잡아 내고 싶다면 (예 : 기계 학습을위한 미니 배치) 메모리에로드하지 않고 거대한 파일을 한 번 스캔해도 괜찮습니다. 빨리 마리아 Zverina의 답변을 기반으로 라인을 잡아 찾기를 사용하십시오.

    # Overhead:
    # Read the line locations into memory once.  (If the lines are long,
    # this should take substantially less memory than the file itself.)
    fname = 'big_file'
    s = [0]
    linelocs = [s.append(s[0]+len(n)) or s.pop(0) for n in open(fname)]
    f = open(fname) # Reopen the file.
    
    # Each subsequent iteration uses only the code below:
    # Grab a 1,000,000 line sample
    # I sorted these because I assume the seeks are faster that way.
    chosen = sorted(random.sample(linelocs, 1000000))
    sampleLines = []
    for offset in chosen:
      f.seek(offset)
      sampleLines.append(f.readline())
    # Now we can randomize if need be.
    random.shuffle(sampleLines)
    
  5. ==============================

    5.라인이 진정으로 .csv 형식이고 고정 필드가 아니라면, 그렇지 않습니다. 파일을 한 번 크롤링하고 각 행의 바이트 오프셋을 인덱싱 한 다음 나중에 인덱스 세트 만 사용해야 할 경우 임의의 csv 파일에 대한 줄 종료 \ n 문자의 정확한 위치를 선험적으로 예측할 수는 없습니다.

    라인이 진정으로 .csv 형식이고 고정 필드가 아니라면, 그렇지 않습니다. 파일을 한 번 크롤링하고 각 행의 바이트 오프셋을 인덱싱 한 다음 나중에 인덱스 세트 만 사용해야 할 경우 임의의 csv 파일에 대한 줄 종료 \ n 문자의 정확한 위치를 선험적으로 예측할 수는 없습니다.

  6. ==============================

    6.당신이 총 라인 수를 알고 있다면 또 다른 해결책이 가능합니다 - 총 라인 수까지 100 만개의 난수 (random.sample (xrange (n), 1000000))를 하나의 세트로 생성 한 다음 다음을 사용하십시오 :

    당신이 총 라인 수를 알고 있다면 또 다른 해결책이 가능합니다 - 총 라인 수까지 100 만개의 난수 (random.sample (xrange (n), 1000000))를 하나의 세트로 생성 한 다음 다음을 사용하십시오 :

    for i, line in enumerate(csvfile):
        if i in lines_to_grab:
            yield line
    

    이렇게하면 공평하지 않은 방식으로 정확히 1 백만 줄을 얻을 수 있지만 사전에 줄 수를 가져야합니다.

  7. ==============================

    7.이 데이터를 sqlite3 데이터베이스에 배치 할 수 있다면 임의의 수의 임의의 행을 선택하는 것은 간단합니다. 파일에서 선을 미리 읽거나 패드 할 필요가 없습니다. sqlite 데이터 파일은 바이너리이기 때문에 데이터 파일은 CSV 텍스트보다 1/3에서 1/2 작습니다.

    이 데이터를 sqlite3 데이터베이스에 배치 할 수 있다면 임의의 수의 임의의 행을 선택하는 것은 간단합니다. 파일에서 선을 미리 읽거나 패드 할 필요가 없습니다. sqlite 데이터 파일은 바이너리이기 때문에 데이터 파일은 CSV 텍스트보다 1/3에서 1/2 작습니다.

    THIS와 같은 스크립트를 사용하여 CSV 파일을 가져 오거나 더 나은 방법으로 데이터를 처음부터 데이터베이스 테이블에 쓸 수 있습니다. SQLite3은 Python 배포판의 일부입니다.

    그런 다음이 명령문을 사용하여 1,000,000 개의 임의 행을 가져옵니다.

    mydb='csv.db'
    con=sqlite3.connect(mydb)
    
    with con:
        cur=con.cursor()
        cur.execute("SELECT * FROM csv ORDER BY RANDOM() LIMIT 1000000;")
    
        for row in cur.fetchall():
            # now you have random rows...
    
  8. ==============================

    8.고정 길이 레코드로 파일을 다시 쓰고 나중에 중간 파일에 대해 임의 액세스를 수행 할 수 있습니다.

    고정 길이 레코드로 파일을 다시 쓰고 나중에 중간 파일에 대해 임의 액세스를 수행 할 수 있습니다.

    ifile = file.open("inputfile.csv")
    ofile = file.open("intermediatefile.csv",'w')
    for line in ifile:
        ofile.write(line.rstrip('\n').ljust(15)+'\n')
    

    그런 다음 할 수 있습니다.

    import random
    ifile = file.open("intermediatefile.csv")
    lines = []
    samples = random.sample(range(nlines))
    for sample in samples:
        ifile.seek(sample)
        lines.append(ifile.readline())
    

    더 많은 디스크 공간이 필요하며 첫 번째 프로그램은 실행하는 데 약간의 시간이 걸릴 수 있지만 나중에 무작위로 무작위로 액세스 할 수 있습니다.

  9. ==============================

    9.

    # pass 1, count the number of rows in the file
    rowcount = sum(1 for line in file)
    # pass 2, select random lines
    file.seek(0)
    remaining = 1000000
    for row in csv.reader(file):
        if random.randrange(rowcount) < remaining:
            print row
            remaining -= 1
        rowcount -= 1
    
  10. ==============================

    10.이 메소드에서는, 요소의 수가 읽는 행 수와 동일한 난수 세트를 생성합니다. 범위는 데이터에있는 행 수입니다. 그런 다음 가장 작은 것부터 가장 큰 것까지 정렬되고 저장됩니다.

    이 메소드에서는, 요소의 수가 읽는 행 수와 동일한 난수 세트를 생성합니다. 범위는 데이터에있는 행 수입니다. 그런 다음 가장 작은 것부터 가장 큰 것까지 정렬되고 저장됩니다.

    그런 다음 csv 파일을 한 줄씩 읽고 line_counter를 사용하여 행 번호를 나타냅니다. 이 line_counter는 정렬 된 난수 목록의 첫 번째 요소로 검사되고, 일치하면 특정 줄이 새 CSV 파일에 쓰여지고 첫 번째 요소가 목록에서 제거되고 이전의 두 번째 요소가 먼저주기가 계속됩니다.

    import random
    k=random.sample(xrange(No_of_rows_in_data),No_of_lines_to_be_read)
    Num=sorted(k)    
    line_counter = 0
    
    with open(input_file,'rb') as file_handle:
        reader = csv.reader(file_handle)
        with open(output_file,'wb') as outfile:
                a=csv.writer(outfile)
                for line in reader:
                    line_counter += 1
                    if line_counter == Num[0]:
                    a.writerow(line)
                    Num.remove(Num[0])
                    if len(Num)==0:
                    break    
    
  11. from https://stackoverflow.com/questions/10819911/read-random-lines-from-huge-csv-file-in-python by cc-by-sa and MIT license