How to download and parse a csv file in Racket? - csv

How do I download and parse a csv file in Racket?

Use get-pure-port to download the file, and use the Planet library (require (planet neil/csv)) to parse it.
The following example downloads and parses a csv file containing
data on the size of the various Galapagos islands and how many species were found on each island.
#lang racket
(require (planet neil/csv:1:=7) net/url)
(define galapagos-url
(string->url
"http://www.stat.washington.edu/~handcock/536/Data/galapagos.csv"))
(define make-galapagos-csv-reader
(make-csv-reader-maker
'((separator-chars #\,)
(strip-leading-whitespace? . #t)
(strip-trailing-whitespace? . #t))))
(define (all-rows url make-reader)
(define next-row (make-reader (get-pure-port url)))
(define (loop)
(define row (next-row))
(if (empty? row)
'()
(cons row (loop))))
(loop))
(all-rows galapagos-url make-galapagos-csv-reader)
The first rows returned are:
'(("Island"
"Observed.species"
"Native.species"
"Area(km^2)"
"Elevation(m)"
"Distance.nearest.island(km)"
"Distance.Santa.Cruz(km)"
"Area.adj.island(km^2)")
("Baltra" "58" "23" "25.09" "" "0.6" "0.6" "1.84")
("Bartolome" "31" "21" "1.24" "109" "0.6" "26.3" "572.33")
("Caldwell" "3" "3" "0.21" "114" "2.8" "58.7" "0.78")

Neil has a new library, csv-reading, so use that instead.
First, install the package using raco:
raco pkg install csv-reading
To convert a CSV file to a list, here is a helper function:
(require csv-reading)
(define (csvfile->list filename)
(call-with-input-file filename
csv->list))
To download a CSV file and convert to a list, do this:
(require net/url)
((compose csv->list get-pure-port string->url) "http://example.com")
See here for the csv-reading library: csv-reading library, it is the latest version (and the other answers are using deprecated ones).

This answer got me where I was looking to go, but I thought I'd chime in for future intrepid explorers. There is an easier invocation (perhaps with newer versions of the csv library?) for doing what you ask. This of course assumes you want the comma delimiter, and to strip leading / trailing whitespace as in the above example.
#lang racket
(require (planet neil/csv:2:0) net/url)
;; Continuing with the UW data sources examples
(define iver-url
(string->url
"http://faculty.washington.edu/cadolph/vis/iver.csv"))
(csv->list (get-pure-port iver-url))
In this version, the csv->list function automatically creates a csv-reader with the aforementioned defaults. You can override the defaults (e.g. if you had a different delimiter or didn't want to strip trailing and leading whitespace) by instead passing a your custom csv reader constructed with make-csv-reader to csv->list.

Use get-pure-port to download the file, then use read-csv-file (from the 2htdp/batch-io library included in Racket) to parse the data.
Download the file
#lang racket
(require net/url)
;; Get the data.
(define the-url (string->url "http://www.example.com/data.csv"))
(define the-data (port->bytes (get-pure-port the-url)))
;; Write the data to a file.
(define outfile (open-output-file "data.csv"))
(write-bytes the-data outfile)
(close-output-port outfile)
Reference: https://www.monolune.com/how-to-download-files-using-racket/
WARNING: By default, when using HTTPS, Racket will not verify the server's certificate. Read the documentation (get-pure-port) on how to enable server certificates verification when using HTTPS.
Parse the data
#lang racket
(require 2htdp/batch-io)
(read-csv-file "data.csv") ; Returns a list.

Related

Function argument as part of path to a file in Clojure

Suppose I have the same file name, placed in five different sub-folders. The file in question has the same name in all folders but otherwise has different values.
Suppose my file is called something like test.csv. My folder are A1,A2,A3,A4 and A5.
My data-read structure looks as so:
(defn my-data [folder]
(loop [SUB-FOLDER (str 'folder)
X []
data (with-open [file (clojure.java.io/reader "./resources/SUB-FOLDER/test.csv")]
(doall (csv/read-csv file)))]
(def row (first data))
(if (nil? row)
[(lazy-seq X)]
(recur (conj X (map #(Float/parseFloat %) row))
(rest data)))))
I would call this function as so
(def Y (map vec (get (my-data A1) 0)))
Where I am trying to access the file test.csv in the sub-folder A1 which I am passing as an argument to my csv-read function.
Now obviously the above code does not work, but it provides an overview of what I am trying to achieve.
How could one make the minimal example work?
Thanks.
Something like this should put your data from one folder into a sequence of sequences:
(defn read-data [folder]
(let [data (csv/read-csv (slurp (str "./resources/" folder "/test.csv")))]
(for [line data]
(map #(Float/parseFloat %) line))))
Change map to mapv if you want to have vectors, and wrap the for with into [] if you want a vector of vectors. This assumes folder is a string. Because your files are very small you don't need to open them with a reader, it's easier to slurp them and then parse them.
A fe issues with your original code:
You're always trying to read a file called "./resources/SUB-FOLDER/test.csv" instead of replacing the name of your folder.
You don't want to use def inside a function. See let.
Calling lazy-seq on an already realized sequence doesn't do anything.
By calling doall on your reader you're forcing all your data into memory instead of reading it and processing it as you go (that's why I replaced it with slurp). If you're going to process it in a loop as you read it, you want the with-open outside the loop (and no doall).

Emacs function automaticly accepting prompt

I am trying to find a way to automatically accept the first proposal from the minibuffer.
(defun find-file-at-point-without-prompt ()
(interactive )
(find-file-at-point)
)
Calling results in the prompt: "Find file or URL: ......". I just want an automatic "yes".
Passing arguments does not work. It might be interesting for other cases as well. I used a macro before that would just call find-file-at-point followed by a RET.
It seems there is no variable to automatically accept the prompt.
You can redefine a function ffap-read-file-or-url by removing a part which is doing the prompt. It remains something like this
(defun ffap-read-file-or-url (prompt guess)
"Read file or URL from minibuffer, with PROMPT and initial GUESS."
(or guess (setq guess default-directory))
(let (dir)
;; Tricky: guess may have or be a local directory, like "w3/w3.elc"
;; or "w3/" or "../el/ffap.el" or "../../../"
(or (ffap-url-p guess)
(progn
(or (ffap-file-remote-p guess)
(setq guess
(abbreviate-file-name (expand-file-name guess))
))
(setq dir (file-name-directory guess))))
;; Do file substitution like (interactive "F"), suggested by MCOOK.
(or (ffap-url-p guess) (setq guess (substitute-in-file-name guess)))
;; Should not do it on url's, where $ is a common (VMS?) character.
;; Note: upcoming url.el package ought to handle this automatically.
guess))

Clojure : missing namespace errors when running "use" at the REPL

I have a .clj file that starts like this :
(ns clojure_crawl.core)
(require '[clj-http.client :as client])
(use 'clojure.contrib.json)
Followed by several function definitions :
(defn f1 [] "" (+ 1 1))
(defn f2 [] "" (+ 2 2))
etc...
However, when I run the command "(use 'myfile.core :reload)"
Some of my functions , although visible at the REPL, cannot run do to "missing namespace" errors.
How do I add the dependencies so that the REPL can run any of the functions defined in my file ?
If your code is in "clojure_crawl/core.clj", its namespace should be clojure-crawl.core (note the hyphen). See http://clojure.org/libs
As Joost already said, you have to be careful with hyphens and underscores: wherever you use a hyphen in your namespace names, replace it with an underscore in the corresponding file/directory names (and vice versa).
Also, the use of the require and use functions in clj source files is discouraged. Instead, declare the libraries you need directly in the ns macro:
(ns clojure-crawl.core
(:require [clj-http.client :as client])
(:use clojure.contrib.json))
This also takes the burden of properly quoting the required namespaces from you.

Is there a simple method for writing traces from multiple sbcl threads to standard output through MCLIDE/swank?

Using SBCL, I'm writing a small server and I would like to trace the server thread, but when I use mclide/swank, I do not see any output from the server thread.
? (require 'sb-posix)
NIL
? (sb-thread:make-thread (lambda () (format t "hi from the thread")))
?
When I try the same thing from sbcl directly, I see what I expect:
(require 'sb-posix)
; loading system definition from
; /opt/local/var/macports/software/sbcl/1.0.39_0+html+threads/opt/local/lib/sbcl/sb-grovel/sb-grovel.asd
; into #
; registering # as SB-GROVEL
("SB-POSIX" "SB-GROVEL" "ASDF")
(sb-thread:make-thread (lambda () (format t "hi from the thread")))
hi from the thread#
*
Does swank have issues capturing standard output from non-foreground threads? If I used slime, would this kind of thing work?

Emacs mode to edit JSON

Does anybody know a good Emacs mode to edit JSON? An app I am working on uses a JSON based communication protocol and having the data nicely indented and syntax-highlighted would help me a lot in the process of figuring it out.
+1 for Josh's json-mode -- works well for me. I added
(defun beautify-json ()
(interactive)
(let ((b (if mark-active (min (point) (mark)) (point-min)))
(e (if mark-active (max (point) (mark)) (point-max))))
(shell-command-on-region b e
"python -m json.tool" (current-buffer) t)))
and
(define-key json-mode-map (kbd "C-c C-f") 'beautify-json)
to json-mode.el to make the shell command invocation easier.
UPDATE: For those of you with a need/desire to do this with unicode, see my question here. The upshot is rather than using:
python -m json.tool
you will want to use
python -c 'import sys,json; data=json.loads(sys.stdin.read()); print json.dumps(data,sort_keys=True,indent=4).decode("unicode_escape").encode("utf8","replace")'
This both beautifies the JSON as well as preserving the original Unicode content.
js-mode supports syntax highlighting and indentation for json files.
This is as of Emacs 23.2, when espresso-mode was incorporated into Emacs and renamed js-mode.
Check it out:
http://www.nongnu.org/espresso/
Have you tried Steve Yegge's js2-mode for Emacs?
If you want something lightweight try this major-mode I hacked together: https://github.com/joshwnj/json-mode
It's actually no more than some extra syntax highlighting on top of javascript-mode, but for my purposes I've found it to work quite well.
Another common use-case is auto-formatting a JSON file (eg. if it's whitespace-compressed and you want more readability). To do this I'm just piping the buffer through a command-line script: C-u M-|
I've prepared a workaround for js2-mode so it parses json files without errors.
You can find it in my comment: http://code.google.com/p/js2-mode/issues/detail?id=50#c7
(I wanted to post it as a comment do J.F. Sebastian solution, but it seems I'm not allowed to do so (no 'add comment' link))
json.el by Edward O'Connor is part of GNU Emacs since 23.1 (2008).
While it isn't a syntax highlighter, it has a useful function to format JSON:
M-x json-pretty-print-buffer RET
So, if you have a recent version of Emacs, there is no need for jq or python -m json.tool.
Since JSON is a subset of YAML, yaml-mode works too (I don't know how it compares to js-mode and json-mode, though).
Install (from emacs): M-x package-install yaml-mode.
Association of yaml-mode with YAML and JSON files, in ~/.emacs.d/init.el:
(add-to-list 'auto-mode-alist '("\\.yaml$" . yaml-mode))
(add-to-list 'auto-mode-alist '("\\.json$" . yaml-mode))
JSON is supported by espresso-mode
js3-mode:https://github.com/thomblake/js3-mode
js3-mode is an improved js2-mode
This package can be installed by package-list-packages command
I will also second Josh's json-mode, but also recommend flymake-json as an addition. It helps highlight syntax errors.
I don't like using python -mjson.tool because it reorders items in JSON objects.
I find (prog-indent-sexp) works just fine to reindent, and using jsonlint
instead of python -mjson.tool works for pretty printing/reformatting in beautify-json
(eval-after-load "json-mode"
'(progn
(require 'flymake-json)
;; flymake-cursor displays error in minibuffer message area instead of requiring hover
(require 'flymake-cursor)
(add-hook 'json-mode-hook 'flymake-json-load)
(define-key json-mode-map "\C-c\C-n" (function flymake-goto-next-error))
)
)
I've expanded on Mariusz Nowak's workaround, to make it usable as a major mode in its own right. Little modification was required beyond simply deriving the mode; the only change Nowak's work actually needed was the ability to recognize buffers not associated with files, or associated with files whose names don't end in .json, as JSON, which we accomplish with a buffer-local variable.
Here's the augmented workaround:
(make-variable-buffer-local 'js2-parse-as-json)
(defadvice js2-reparse (before json)
(setq js2-buffer-file-name buffer-file-name))
(ad-activate 'js2-reparse)
(defadvice js2-parse-statement (around json)
(if (and (= tt js2-LC)
js2-buffer-file-name
(or js2-parse-as-json
(string-equal (substring js2-buffer-file-name -5) ".json"))
(eq (+ (save-excursion
(goto-char (point-min))
(back-to-indentation)
(while (eolp)
(next-line)
(back-to-indentation))
(point)) 1) js2-ts-cursor))
(setq ad-return-value (js2-parse-assign-expr))
ad-do-it))
(ad-activate 'js2-parse-statement)
(define-derived-mode json-mode js2-mode "JSON"
"Major mode for editing JSON data."
:group 'json
(setq js2-parse-as-json t)
(js2-reparse t))
(add-to-list 'auto-mode-alist '("\\.json$" . json-mode))
If you already use js2-mode, this may be a better option than js-mode plus flymake-json because you need not install anything new (js2-mode already does syntax checking, no need for an external tool), and because this mode will inherit your js2-mode configuration, which js-mode will not.
I would also recommand js2-mode.
JSON stands for JavaScript Object Notation. It's not another language and it's even not a data container like yaml or xml are. JSON could be used as a data container if there's no function (or in this case we should say method) inside a JSON object, but it's not the primary goal of JSON :-)
var myJSObject = {
attr: {foo: "bar", baz: ["quux", "truc", "pouet"]},
fooAlert: function (num) {
alert(this.attr.foo+' '+num);
}
};
myJSObject.fooAlert(42);