# How beginners misread documentation--Example: Me

**URL:** <https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577>\
**Category:** New to Julia\
**Tags:** documentation, csv\
**Created:** [October 28, 2021, 6:58pm UTC](https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577 "2021-10-28T18:58:04Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![blackeneth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blackeneth/32/10353_2.png) [@blackeneth](https://discourse.julialang.org/u/blackeneth)\
**Post date:** [October 28, 2021, 6:58pm UTC](https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577/1 "2021-10-28T18:58:05Z")

</div>

My apologies to the developers of CSV.jl . **This post is not the criticize or pick on them.** T_hey are be be lauded for writing such extensive documentation._ However, this morning I was trying to read a well formatted file into a DataFrame and ran into lots of problems. The below is to illustrated how documentation may be misinterpreted or misunderstood. A lot of Julia documentation will be misinterpreted and misunderstood the same ways.

file simple (tab delimited):

```julia
comment:	first design	date:	28-Oct-2021
rock	paper	scissors
granite	A4	big
white quartz	8x10	snippers

```

Read the first line of the file; it’s different and will be parsed separtely

```julia
path="C:\\Users\luser\\"
fname = "simple"
pathname = path*fname

fn = open(pathname,"r")

firstline = readline(fn);

header = split(firstline,"\t")

```

So far, so good. Now the fun begins.

Read the rest of the file into a dataframe. Here are relevant snippets of documentation:

```julia
CSV.read(source, sink::T; kwargs...) => T 

CSV.File(input; kwargs...) => CSV.File

```

First attempt:

```julia
df = DataFrame()
dfr = CSV.read(fn, df; delim="\t")

julia> dfr = CSV.read(fn, df; delim="\t")
ERROR: MethodError: objects of type DataFrame are not callable
Stacktrace:
 [1] |>(x::Tables.CopiedColumns{CSV.File}, f::DataFrame)
   @ Base .\operators.jl:858
 [2] read(source::IOStream, sink::DataFrame; copycols::Bool, kwargs::Base.Iterators.Pairs{Symbol, String, Tuple{Symbol}, NamedTuple{(:delim,), Tuple{String}}})
   @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\CSV.jl:91
 [3] top-level scope
   @ REPL[63]:1

```

Hmm, let’s try CSV.File and pipe it directly to dataframe:

```julia
julia> dff = CSV.File(fn; skipto=2) |> df
ERROR: MethodError: objects of type DataFrame are not callable
Stacktrace:
 [1] |>(x::CSV.File, f::DataFrame)
   @ Base .\operators.jl:858
 [2] top-level scope
   @ REPL[64]:1

```

Oops, I’m reusing “fn” – user the pathname instead:

```julia
julia> df = DataFrame()
0×0 DataFrame

julia> dfr = CSV.read(pathname, df; delim="\t", skipto=2)
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 1. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 2. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 3. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
ERROR: MethodError: objects of type DataFrame are not callable
Stacktrace:
 [1] |>(x::Tables.CopiedColumns{CSV.File}, f::DataFrame)
   @ Base .\operators.jl:858
 [2] read(source::String, sink::DataFrame; copycols::Bool, kwargs::Base.Iterators.Pairs{Symbol, Any, Tuple{Symbol, Symbol}, NamedTuple{(:delim, :skipto), Tuple{String, Int64}}})
   @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\CSV.jl:91
 [3] top-level scope
   @ REPL[66]:1

```

That didn’t work. Try CSV.File. I’ll try piping the output to my empty dataframe:

```julia
julia> dff = CSV.File(pathname; skipto=2) |> df
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 1. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 2. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 3. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
ERROR: MethodError: objects of type DataFrame are not callable
Stacktrace:
 [1] |>(x::CSV.File, f::DataFrame)
   @ Base .\operators.jl:858
 [2] top-level scope
   @ REPL[67]:1

```

Hmm, maybe when on the front page it said:

```julia
That's quite a bit! Let's boil down a TL;DR:

Just want to read a delimited file or collection of files and do basic stuff with data? Use CSV.File(file) or CSV.read(file, DataFrame)

```

… you actually type in “DataFrame” and not the variable name for the dataframe

Let’s give it a try:

```julia
julia> dfr = CSV.read(pathname, DataFrame; delim="\t", skipto=2)
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 1. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 2. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
┌ Warning: thread = 1 warning: only found 3 / 4 columns around data row: 3. Filling remaining columns with `missing`
└ @ CSV C:\Users\nxf79930\.julia\packages\CSV\nofYz\src\file.jl:634
3×4 DataFrame
 Row │ comment: first design date: 28-Oct-2021 
     │ String15 String7 String15 Missing     
─────┼───────────────────────────────────────────────────
   1 │ rock paper scissors missing 
   2 │ granite A4 big missing 
   3 │ white quartz 8x10 snippers missing 

```

Hey, it read some data in. But why is the first line in the header? I put in skipto=2.  
[break to re-read documentation on skipto]

Oh, it looks like the header is the line before the skipto line, so I need to put in header=false:

```julia
julia> dfr = CSV.read(pathname, DataFrame; delim="\t", skipto=2, header=false)
3×3 DataFrame
 Row │ Column1 Column2 Column3  
     │ String15 String7 String15 
─────┼─────────────────────────────────
   1 │ rock paper scissors
   2 │ granite A4 big
   3 │ white quartz 8x10 snippers

```

Success at last!

Let’s get CSV.File working too:

```julia
julia> dff = CSV.File(pathname; skipto=2, header=false) |> DataFrame
3×3 DataFrame
 Row │ Column1 Column2 Column3  
     │ String15 String7 String15 
─────┼─────────────────────────────────
   1 │ rock paper scissors
   2 │ granite A4 big
   3 │ white quartz 8x10 snippers

```

**Analysis**

1. Often confused by Julia documentation as to whether I need to write out _the type_, or provide a _variable of that type._ The use of “T” is the worst offender here – “Is that _variable_ T, or is that a _type_?” In the CSV.jl documentaton, they put in DataFrame as the type. OK, this requires me to remember that 1) A DataFrame (an object containing rows and columns of data) is also a type (DataFrame) in Julia, 2) type names are entered without quotes around them.

2. Confusion on opening the file. When reading 1 line out, I created a variable --“fn”-- for the IOStream. I then reused that when working with CSV. Needed just the pathname string instead. However, in my defense, most of the examples given for CSV use `IOBuffer(data)` instead of a filename.

**What makes the CSV.jl documentation confusing:**

1. The friendly front page really makes you feel good that CSV.jl is an easy way to read delimited files into a dataframe – especially when you get down the the “TL;DR” (recommendation: move that to the top). However, **not a simple example in the documentation shows to read a delimited file into a dataframe!** Ok, that’s a lie – there is _one example_ of reading a zipped file into a dataframe (but since I’m not reading a zipped file, I’m not going to look there).

2. Most of the examples use `IOBuffer(data)` as the source of the data. I get why – the data can be defined in the code, making it self-contained. You don’t have to create a file for the example which has to travel along with the code. However, I didn’t come to the CSV.jl documentation to read from a String, or an IOBuffer. I came to read from a file–and few examples read from a file.

**Recommendations**

1. Try to make a distinction, or call out, whether a variable is needed, or a Type is needed. Again, “T” is the worst offender, and Julia documentation (and C++ documentation, and …) use it both ways. If it is supposed to be a variable, use something other than T.

2. Documentation should have some dead simple, knuckle-dragging, examples that are some the simplest, or most common uses. For example, this simple example in the CSV.jl documentation would have helped me a lot:

file simple2.csv (comma delimited):

```julia
stockno,item,unit,price
10,"hammer","EA", 15.95
20,"ladder","EA", 84.95
30,"nails","LB", 6.95

```

```julia
julia> pathname="simple2.csv"
"simple2.csv"

julia> df1 = CSV.read(pathname, DataFrame; delim=",")
3×4 DataFrame
 Row │ stockno item unit price   
     │ Int64 String7 String3 Float64 
─────┼────────────────────────────────────
   1 │ 10 hammer EA 15.95
   2 │ 20 ladder EA 84.95
   3 │ 30 nails LB 6.95

julia> df2 = CSV.File(pathname; delim=",") |> DataFrame
3×4 DataFrame
 Row │ stockno item unit price   
     │ Int64 String7 String3 Float64 
─────┼────────────────────────────────────
   1 │ 10 hammer EA 15.95
   2 │ 20 ladder EA 84.95
   3 │ 30 nails LB 6.95

```

That’s it! The above would have been really helpful.

Now, recall what I said at the beginning regarding the developers of CSV.jl:

> **This post is not the criticize or pick on them.** T_hey are be be lauded for writing such extensive documentation._

I could write something similar for a lot of documentation (SQLite.jl, looking at you …).

The point of this post is just to illustrate how documentation can be misunderstood and give some general tips for making all documentation better.

---

<div class="post-metadata">

**Author:** ![isaacsas](https://avatars.discourse-cdn.com/v4/letter/i/f6c823/32.png) [@isaacsas](https://discourse.julialang.org/u/isaacsas)\
**Post date:** [October 28, 2021, 9:31pm UTC](https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577/2 "2021-10-28T21:31:31Z")

</div>

You might open a PR to CSV.jl adding your example to the docs to help the developers out.

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 28, 2021, 10:15pm UTC](https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577/3 "2021-10-28T22:15:25Z")

</div>

`T` is not a confusing name to people who have internalized Julia naming conventions: CamelCase for `Type` variables, snake\_case for non-type variables.

Since a variable can very well be a type and vice versa, the question “Is that variable T, or is that a type?” doesn’t really mean anything, it’s both.

To me the real problem in the documentation of CSV.jl is that the argument is named `sink`. To me, `df` is more “sink-like” than `DataFrame`. Renaming the argument `SinkType` or better yet `OutputType` would clear up a lot of the confusion.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [October 28, 2021, 10:51pm UTC](https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577/4 "2021-10-28T22:51:57Z")

</div>

> [@gustaphe](#):
>
> Renaming the argument `SinkType` or better yet `OutputType` would clear up a lot of the confusion.

This!

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [October 29, 2021, 8:14am UTC](https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577/5 "2021-10-29T08:14:21Z")

</div>

> [@gustaphe](#):
>
> Renaming the argument `SinkType` or better yet `OutputType` would clear up a lot of the confusion.

This argument is not a type, it’s effectively a function. Eg, a common way to read a CSV into a plain array-based Table is providing `sink=rowtable` or `columntable`.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [October 29, 2021, 8:46am UTC](https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577/6 "2021-10-29T08:46:02Z")

</div>

> [@gustaphe](#):
>
> `T` is not a confusing name to people who have internalized Julia naming conventions: CamelCase for `Type` variables, snake\_case for non-type variables.

I find it more than confusing in this case, I think it’s wrong 🙂

The convention is that `T` is a generic name for a type so for example you can replace `T` with `DataFrame` and get a correct signature. In this case it gives

`CSV.read(source, sink::DataFrame; kwargs...)` =\> DataFrame

It’s wrong because it says that `sink` is a value of type `DataFrame` such as `df`, **not** a value of type `Type` such as `DataFrame`. So according to the documentation @blackeneth was 100% right to pass `df` as parameter.

I also find the " =\> T" part a bit confusing. AFAIK the correct way to write this signature is

```julia
CSV.read(source, sink::Type{T}; kwargs...)::T where {T}

```

but it’s probably best to keep things simple and write

```julia
CSV.read(source, sink::Type; kwargs...)

```

then explain in the following text that “`CSV.read` returns a value of the given sink type, for example `CSV.read("data.csv", DataFrame)` returns a data frame”.

---

<div class="post-metadata">

**Author:** ![gustaphe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gustaphe/32/18174_2.png) [@gustaphe](https://discourse.julialang.org/u/gustaphe)\
**Post date:** [October 29, 2021, 11:58am UTC](https://discourse.julialang.org/t/how-beginners-misread-documentation-example-me/70577/7 "2021-10-29T11:58:47Z")

</div>

You are right, I read that wrong.
