web-dev-qa-db-ja.com

Juliaでのcsvの読み取りは、Python

juliaで大きなテキスト/ csvファイルを読み取るには、Pythonに比べて長い時間がかかります。サイズが486.6MBで、153895行と644列のファイルを読み取る回数は次のとおりです。

python3.3の例

import pandas as pd
import time
start=time.time()
myData=pd.read_csv("C:\\myFile.txt",sep="|",header=None,low_memory=False)
print(time.time()-start)

Output: 19.90

R3.0.2の例

system.time(myData<-read.delim("C:/myFile.txt",sep="|",header=F,
   stringsAsFactors=F,na.strings=""))

Output:
User    System  Elapsed
181.13  1.07    182.32

Julia 0.2.0(Julia Studio 0.4.4)例#1

using DataFrames
timing = @time myData = readtable("C:/myFile.txt",separator='|',header=false)

Output:
elapsed time: 80.35 seconds (10319624244 bytes allocated)

Julia 0.2.0(Julia Studio 0.4.4)例#2

timing = @time myData = readdlm("C:/myFile.txt",'|',header=false)

Output:
elapsed time: 65.96 seconds (9087413564 bytes allocated)
  1. JuliaはRより高速ですが、Pythonと比較するとかなり低速です。大きなテキストファイルの読み取りを高速化するには、どうすればよいですか?

  2. 別の問題は、メモリ内のサイズがJuliaのハードディスクファイルサイズの18 xサイズですが、Pythonの場合は2.5xサイズしかないことです。大きなファイルに対して最もメモリ効率が高いことがわかったMatlabでは、ハードディスクファイルサイズの2倍のサイズです。 Juliaのメモリ内のファイルサイズが大きい特別な理由はありますか?

23
uday

おそらく最良の答えは、私はウェスほど優れたプログラマーではないということです。

一般に、DataFramesのコードは、Pandasのコードよりも最適化されていません。追いつくことができると確信していますが、最初に実装する必要のある基本的な機能がたくさんあるため、時間がかかります。 Juliaで構築する必要があるものがたくさんあるので、私は3つの部分で物事を行うことに集中する傾向があります:(1)任意のバージョンを構築する、(2)正しいバージョンを構築する、(3)高速で正しいバージョンを構築する。私の仕事では、ジュリアは基本的な機能のバージョンを提供しないことが多いので、私の仕事は(1)と(2)に焦点を当てています。必要なツールが増えるにつれて、パフォーマンスに集中しやすくなります。

メモリ使用量に関しては、パンダで使用されるものよりもはるかに効率が低い表形式のデータを解析するときに、一連のデータ構造を使用することが答えだと思います。 Pandasの方が良いとわかっていれば、効率の悪い場所をリストアップできますが、今のところ、明らかな失敗の1つは、全体を読んでいることだと推測します。ディスクからチャンクを取得するのではなく、データセットをメモリに挿入します。これは確実に回避でき、そうすることで問題が発生します。時間の問題です。

その点で、readtableコードはかなり読みやすいです。 readtableを高速化するための最も確実な方法は、Juliaプロファイラーを作成し、それが発見したパフォーマンスの欠陥の修正を開始することです。

44

Jacob QuinnによるCSV.jlと呼ばれる比較的新しいJuliaパッケージがあり、多くの場合、パンダと同等のはるかに高速なCSVパーサーを提供します。 https://github.com/JuliaData/CSV.jl

11
Jeff Bezanson

私はこの状況を部分的に助けることができるいくつかのことを見つけました。

  1. juliaでreaddlm()関数を使用すると、readtable()よりもかなり高速に動作するようです(たとえば、最近の試行では3倍)。もちろん、DataFrameオブジェクトタイプが必要な場合は、それに変換する必要があります。これにより、速度の向上のほとんどまたはすべてが消費される可能性があります。

  2. ファイルのサイズを指定すると、速度とメモリ割り当ての両方で大きな違いが生じる可能性があります。ディスク上の258.7MBのファイルでこのトライアル読み取りを実行しました。

    _Julia> @time Data = readdlm("MyFile.txt", '\t', Float32, skipstart = 1);
    19.072266 seconds (221.60 M allocations: 6.573 GB, 3.34% gc time)
    
    Julia> @time Data = readdlm("MyFile.txt", '\t', Float32, skipstart = 1, dims = (File_Lengths[1], 62));
    10.309866 seconds (87 allocations: 528.331 MB, 0.03% gc time)
    _
  3. オブジェクトの型指定は非常に重要です。たとえば、データに文字列が含まれている場合、読み込んだ配列のデータはAny型になりますが、これはメモリの面でコストがかかります。メモリが本当に問題になる場合は、最初に文字列を整数に変換し、計算を行ってから、元に戻すことによってデータを前処理することを検討することをお勧めします。また、大量の精度が必要ない場合は、Float64の代わりにFloat32タイプを使用すると、多くのスペースを節約できます。これは、ファイルを読み込むときに指定できます。例:

    Data = readdlm("file.csv", ',', Float32)

  4. メモリ使用量に関しては、特に、データに繰り返し値が多数ある場合、(DataArraysパッケージの)PooledDataArrayタイプがメモリ使用量の削減に役立つことがわかりました。このタイプに変換する時間は比較的長いので、これ自体は時間の節約にはなりませんが、少なくともメモリ使用量をいくらか減らすのに役立ちます。例えば。 1,900万行36列のデータセットをロードすると、そのうち8つは統計分析用のカテゴリ変数を表し、これによりオブジェクトのメモリ割り当てがディスク上のサイズの5倍から4倍に減少しました。繰り返される値がさらに多い場合、メモリの削減はさらに重要になる可能性があります(PooledDataArrayがメモリ割り当てを半分に削減する状況がありました)。

  5. また、データをロードしてフォーマットした後にgc()(ガベージコレクター)関数を実行して、不要なRAM割り当てをクリアすると役立つ場合もありますが、通常、Juliaはこれを自動的に実行します。

それでも、これらすべてにもかかわらず、大規模なデータセットの読み込みを高速化し、メモリ使用量を効率化するために、Juliaがさらに開発されることを楽しみにしています。

11

@timeから出力される「割り当てられたnバイト」は、割り当てられたすべてのオブジェクトの合計サイズであり、解放された可能性のあるオブジェクトの数は無視されます。この数は、多くの場合、メモリ内のライブオブジェクトの最終的なサイズよりもはるかに大きくなります。これがあなたのメモリサイズの見積もりに基づいているかどうかはわかりませんが、これを指摘したいと思います。

10
Jeff Bezanson

私の経験では、大きなテキストファイルを処理する最良の方法は、それらをJuliaにロードするのではなく、ストリーミングすることです。この方法には追加の固定費がかかりますが、通常は非常に高速に実行されます。いくつかの擬似コードはこれです:

function streamdat()
    mycsv=open("/path/to/text.csv", "r")   # <-- opens a path to your text file

    sumvec = [0.0]                # <-- store a sum  here
    i = 1
    while(!eof(mycsv))            # <-- loop through each line of the file
       row = readline(mycsv) 
       vector=split(row, "|")     # <-- split each line by |
       sumvec+=parse(Float64, vector[i]) 
       i+=1
    end
end

streamdat()

上記のコードは単純な合計ですが、このロジックはより複雑な問題に拡張できます。

5
Jeremy McNees