以下のGoコードは、10,000レコードのCSV(タイムスタンプtimes
およびfloat values
)を読み取り、データに対していくつかの操作を実行してから、元の値を追加の列とともに別のCSVに書き込みますscore
の場合。しかし、それはひどく遅い(つまり、数時間ですが、そのほとんどはcalculateStuff()
)であり、CSVの読み取り/書き込みに対処できる非効率性があるかどうか知りたいです。
package main
import (
"encoding/csv"
"log"
"os"
"strconv"
)
func ReadCSV(filepath string) ([][]string, error) {
csvfile, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer csvfile.Close()
reader := csv.NewReader(csvfile)
fields, err := reader.ReadAll()
return fields, nil
}
func main() {
// load data csv
records, err := ReadCSV("./path/to/datafile.csv")
if err != nil {
log.Fatal(err)
}
// write results to a new csv
outfile, err := os.Create("./where/to/write/resultsfile.csv"))
if err != nil {
log.Fatal("Unable to open output")
}
defer outfile.Close()
writer := csv.NewWriter(outfile)
for i, record := range records {
time := record[0]
value := record[1]
// skip header row
if i == 0 {
writer.Write([]string{time, value, "score"})
continue
}
// get float values
floatValue, err := strconv.ParseFloat(value, 64)
if err != nil {
log.Fatal("Record: %v, Error: %v", floatValue, err)
}
// calculate scores; THIS EXTERNAL METHOD CANNOT BE CHANGED
score := calculateStuff(floatValue)
valueString := strconv.FormatFloat(floatValue, 'f', 8, 64)
scoreString := strconv.FormatFloat(prob, 'f', 8, 64)
//fmt.Printf("Result: %v\n", []string{time, valueString, scoreString})
writer.Write([]string{time, valueString, scoreString})
}
writer.Flush()
}
このCSV読み取り/書き込みテンプレートコードを可能な限り高速にするためのヘルプを探しています。この質問の範囲では、calculateStuff
メソッドについて心配する必要はありません。
最初にファイルをメモリにロードしてから処理しますが、大きなファイルでは遅くなる可能性があります。
ループして.Read
を呼び出し、一度に1行ずつ処理する必要があります。
func processCSV(rc io.Reader) (ch chan []string) {
ch = make(chan []string, 10)
go func() {
r := csv.NewReader(rc)
if _, err := r.Read(); err != nil { //read header
log.Fatal(err)
}
defer close(ch)
for {
rec, err := r.Read()
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
ch <- rec
}
}()
return
}
// DaveCのコメントに大まかに基づいていることに注意してください。
これは本質的にコメントセクションからの Dave C の答えです:
_package main
import (
"encoding/csv"
"log"
"os"
"strconv"
)
func main() {
// setup reader
csvIn, err := os.Open("./path/to/datafile.csv")
if err != nil {
log.Fatal(err)
}
r := csv.NewReader(csvIn)
// setup writer
csvOut, err := os.Create("./where/to/write/resultsfile.csv"))
if err != nil {
log.Fatal("Unable to open output")
}
w := csv.NewWriter(csvOut)
defer csvOut.Close()
// handle header
rec, err := r.Read()
if err != nil {
log.Fatal(err)
}
rec = append(rec, "score")
if err = w.Write(rec); err != nil {
log.Fatal(err)
}
for {
rec, err = r.Read()
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
// get float value
value := rec[1]
floatValue, err := strconv.ParseFloat(value, 64)
if err != nil {
log.Fatal("Record, error: %v, %v", value, err)
}
// calculate scores; THIS EXTERNAL METHOD CANNOT BE CHANGED
score := calculateStuff(floatValue)
scoreString := strconv.FormatFloat(score, 'f', 8, 64)
rec = append(rec, scoreString)
if err = w.Write(rec); err != nil {
log.Fatal(err)
}
w.Flush()
}
}
_
もちろん、ロジックはすべてmain()
に詰め込まれていることに注意してください。複数の関数に分割する方が良いでしょうが、それはこの質問の範囲を超えています。
encoding/csv
は、多くの割り当てを実行するため、大きなファイルでは実際に非常に遅くなります。あなたのフォーマットはとてもシンプルなので、代わりにstrings.Split
を使用することをお勧めします。
それでも十分に速くない場合は、アセンブリに実装されているstrings.IndexByte
を使用して、自分で解析を実装することを検討できます。 http://golang.org/src/strings/strings_decl.go?s=274: 310#L1
そうは言っても、ファイルがメモリよりも大きい場合は、ReadAll
の使用も再検討する必要があります。