Common Lisp 中的快速 CSV 读取
Fast CSV reading in Common Lisp
在 CL 中以下列方式读取 csv 文件的最快方法是什么:
1) 第一行中的所有字段都进入一个名为列名的数组
2) 以下所有行的第一个字段进入另一个
名为行名的数组
3)所有其他字段进入另一个名为值的数组
?
我的文件具有以下形式,只是有更多的列和行:
"";"ES1 Index";"VG1 Index";"TY1 Comdty";"RX1 Comdty";"GC1 Comdty"
"1999-01-04";1391.12;3034.53;66.515625;86.2;441.39
"1999-01-05";1404.86;3072.41;66.3125;86.17;440.63
"1999-01-06";1435.12;3156.59;66.4375;86.32;441.7
"1999-01-07";1432.32;3106.08;66.25;86.22;447.67
我想要的结果是:
#("1999-01-04" "1999-01-05" "1999-01-06" "1999-01-07" )
#("" "ES1 Index" "VG1 Index" "TY1 Comdty" "RX1 Comdty" "GC1 Comdty")
#(1391.12 3034.53 66.515625 86.2 441.39 1404.86 3072.41 66.3125 86.17 440.63
1435.12 3156.59 66.4375 86.32 441.7 1432.32 3106.08 66.25 86.22 447.67)
您是否知道某些 CL 库已经这样做了?
关于 I/O 性能,是否有任何我应该注意的一般性问题,也许是特定于编译器的问题?
这是我现在的做法:
(with-open-file (stream "my-file.csv" :direction :input)
(let* ((header (read-line stream nil))
(columns-list (mapcar #'read-from-string
(cl-ppcre:split ";" header)))
(number-of-columns (length columns-list))
(column-names (make-array number-of-columns
:initial-contents columns-list))
(rownames (make-array 1 :adjustable t :fill-pointer 0))
(values (make-array 1 :adjustable t :fill-pointer 0)))
(set-syntax-from-char #\; #\ )
(loop
:for reader = (read stream nil stream)
:until (eq reader stream)
:do (progn (vector-push-extend reader row-names)
(loop
:for count :from 2 :upto number-of-columns
:do (vector-push-extend (read stream nil)
values)))
:finally (return (values row-names
column-names
values)))))
注意:我不会在实际代码中使用 set-syntax-from-char,我使用它只是为了这个例子。
要读取 csv 文件,我发现 cl-csv 包 (https://github.com/AccelerationNet/cl-csv) 非常有用且速度很快。例如,要解决您的问题,可以使用以下代码:
(let ((data (cl-csv:read-csv #P"my-file.csv" :separator #\;)))
(values (apply #'vector (first data))
(apply #'vector (rest (mapcar #'first data)))
(apply #'vector
(mapcar #'read-from-string (loop :for row :in (rest data)
:append (rest row))))))
cl-csv:read-csv
returns 一个列表,每行包含一个字符串列表,这些字符串是单元格的内容。
我怀疑 I/O 是这里最慢的部分。如果您重复使用 READ-SEQUENCE rather than calling READ-LINE,您可能会变得更快 I/O。所以你的代码可能看起来像这样:
(with-open-file (s "my-file.csv")
(let* ((len (file-length s))
(data (make-array len)))
(read-sequence data s)
data))
然后用换行符拆分 data
并添加您的逻辑。
无论是否有帮助,它对您分析代码很有帮助,例如使用 :sb-sprof
,查看大部分时间花在了哪些地方。
在 CL 中以下列方式读取 csv 文件的最快方法是什么: 1) 第一行中的所有字段都进入一个名为列名的数组 2) 以下所有行的第一个字段进入另一个 名为行名的数组 3)所有其他字段进入另一个名为值的数组 ?
我的文件具有以下形式,只是有更多的列和行:
"";"ES1 Index";"VG1 Index";"TY1 Comdty";"RX1 Comdty";"GC1 Comdty"
"1999-01-04";1391.12;3034.53;66.515625;86.2;441.39
"1999-01-05";1404.86;3072.41;66.3125;86.17;440.63
"1999-01-06";1435.12;3156.59;66.4375;86.32;441.7
"1999-01-07";1432.32;3106.08;66.25;86.22;447.67
我想要的结果是:
#("1999-01-04" "1999-01-05" "1999-01-06" "1999-01-07" )
#("" "ES1 Index" "VG1 Index" "TY1 Comdty" "RX1 Comdty" "GC1 Comdty")
#(1391.12 3034.53 66.515625 86.2 441.39 1404.86 3072.41 66.3125 86.17 440.63
1435.12 3156.59 66.4375 86.32 441.7 1432.32 3106.08 66.25 86.22 447.67)
您是否知道某些 CL 库已经这样做了? 关于 I/O 性能,是否有任何我应该注意的一般性问题,也许是特定于编译器的问题?
这是我现在的做法:
(with-open-file (stream "my-file.csv" :direction :input)
(let* ((header (read-line stream nil))
(columns-list (mapcar #'read-from-string
(cl-ppcre:split ";" header)))
(number-of-columns (length columns-list))
(column-names (make-array number-of-columns
:initial-contents columns-list))
(rownames (make-array 1 :adjustable t :fill-pointer 0))
(values (make-array 1 :adjustable t :fill-pointer 0)))
(set-syntax-from-char #\; #\ )
(loop
:for reader = (read stream nil stream)
:until (eq reader stream)
:do (progn (vector-push-extend reader row-names)
(loop
:for count :from 2 :upto number-of-columns
:do (vector-push-extend (read stream nil)
values)))
:finally (return (values row-names
column-names
values)))))
注意:我不会在实际代码中使用 set-syntax-from-char,我使用它只是为了这个例子。
要读取 csv 文件,我发现 cl-csv 包 (https://github.com/AccelerationNet/cl-csv) 非常有用且速度很快。例如,要解决您的问题,可以使用以下代码:
(let ((data (cl-csv:read-csv #P"my-file.csv" :separator #\;)))
(values (apply #'vector (first data))
(apply #'vector (rest (mapcar #'first data)))
(apply #'vector
(mapcar #'read-from-string (loop :for row :in (rest data)
:append (rest row))))))
cl-csv:read-csv
returns 一个列表,每行包含一个字符串列表,这些字符串是单元格的内容。
我怀疑 I/O 是这里最慢的部分。如果您重复使用 READ-SEQUENCE rather than calling READ-LINE,您可能会变得更快 I/O。所以你的代码可能看起来像这样:
(with-open-file (s "my-file.csv")
(let* ((len (file-length s))
(data (make-array len)))
(read-sequence data s)
data))
然后用换行符拆分 data
并添加您的逻辑。
无论是否有帮助,它对您分析代码很有帮助,例如使用 :sb-sprof
,查看大部分时间花在了哪些地方。