{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Load and save DataFrames\n", "\n", "We do not cover all features of the packages. Please refer to their documentation to learn them.\n", "\n", "- https://github.com/ExpandingMan/Arrow.jl\n", "- https://github.com/invenia/JLSO.jl\n", "- https://github.com/JuliaData/JSONTables.jl\n", "- https://github.com/xiaodaigh/JDF.jl\n", "\n", "Here we'll load `CSV.jl` to read and write CSV files and `Arrow.jl`, `JLSO.jl`, and serialization, which allow us to work with a binary format and `JSONTables.jl` for JSON interaction. Finally we consider a custom `JDF.jl` format." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:24.878000Z", "iopub.status.busy": "2024-06-04T16:21:24.460000Z", "iopub.status.idle": "2024-06-04T16:21:38.509000Z", "shell.execute_reply": "2024-06-04T16:21:38.457000Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mPrecompiling IJuliaExt [2f4121a4-3b3a-5ce6-9c5e-1f2673ce168a]\n" ] } ], "source": [ "using DataFrames\n", "using Arrow\n", "using CSV\n", "using Serialization\n", "using JLSO\n", "using JSONTables\n", "using CodecZlib\n", "using ZipFile\n", "using JDF\n", "using StatsPlots ## for charts\n", "using Mmap ## for compression" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's create a simple `DataFrame` for testing purposes," ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:38.725000Z", "iopub.status.busy": "2024-06-04T16:21:38.511000Z", "iopub.status.idle": "2024-06-04T16:21:41.177000Z", "shell.execute_reply": "2024-06-04T16:21:41.177000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String?Char?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String? & Char?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String? \u001b[0m\u001b[90m Char? \u001b[0m\n", "─────┼──────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = DataFrame(\n", " A=[true, false, true], B=[1, 2, missing],\n", " C=[missing, \"b\", \"c\"], D=['a', missing, 'c']\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and use `eltypes` to look at the columnwise types." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:41.180000Z", "iopub.status.busy": "2024-06-04T16:21:41.180000Z", "iopub.status.idle": "2024-06-04T16:21:42.835000Z", "shell.execute_reply": "2024-06-04T16:21:42.835000Z" } }, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Type}:\n", " Bool\n", " Union{Missing, Int64}\n", " Union{Missing, String}\n", " Union{Missing, Char}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eltype.(eachcol(x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## CSV.jl\n", "Let's use `CSV` to save `x` to disk; make sure `x1.csv` does not conflict with some file in your working directory." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:42.837000Z", "iopub.status.busy": "2024-06-04T16:21:42.837000Z", "iopub.status.idle": "2024-06-04T16:21:43.839000Z", "shell.execute_reply": "2024-06-04T16:21:43.838000Z" } }, "outputs": [ { "data": { "text/plain": [ "\"x1.csv\"" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "CSV.write(\"x1.csv\", x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can see how it was saved by reading `x.csv`." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:43.841000Z", "iopub.status.busy": "2024-06-04T16:21:43.841000Z", "iopub.status.idle": "2024-06-04T16:21:43.845000Z", "shell.execute_reply": "2024-06-04T16:21:43.845000Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "A,B,C,D\n", "true,1,,a\n", "false,2,b,\n", "true,,c,c\n" ] } ], "source": [ "print(read(\"x1.csv\", String))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also load it back." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:43.847000Z", "iopub.status.busy": "2024-06-04T16:21:43.847000Z", "iopub.status.idle": "2024-06-04T16:21:48.127000Z", "shell.execute_reply": "2024-06-04T16:21:48.127000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String1?String1?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String1? & String1?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String1? \u001b[0m\u001b[90m String1? \u001b[0m\n", "─────┼────────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = CSV.read(\"x1.csv\", DataFrame)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that when loading in a `DataFrame` from a `CSV` the column type for columns `:C` `:D` have changed to use special strings defined in the InlineStrings.jl package." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:48.130000Z", "iopub.status.busy": "2024-06-04T16:21:48.130000Z", "iopub.status.idle": "2024-06-04T16:21:48.136000Z", "shell.execute_reply": "2024-06-04T16:21:48.136000Z" } }, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Type}:\n", " Bool\n", " Union{Missing, Int64}\n", " Union{Missing, String1}\n", " Union{Missing, String1}" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eltype.(eachcol(y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Serialization by JDF.jl and JLSO.jl\n", "\n", "Now we use serialization to save `x`.\n", "\n", "There are two ways to perform serialization. The first way is to use the `Serialization.serialize` as below:\n", "\n", "Note that in general, this process _will not work_ if the reading and writing are done by different versions of Julia, or an instance of Julia with a different system image." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:48.138000Z", "iopub.status.busy": "2024-06-04T16:21:48.138000Z", "iopub.status.idle": "2024-06-04T16:21:48.327000Z", "shell.execute_reply": "2024-06-04T16:21:48.327000Z" } }, "outputs": [], "source": [ "open(\"x.bin\", \"w\") do io\n", " serialize(io, x)\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we load back the saved file to `y` variable. Again `y` is identical to `x`. However, please beware that if you session does not have DataFrames.jl loaded, then it may not recognise the content as DataFrames.jl" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:48.329000Z", "iopub.status.busy": "2024-06-04T16:21:48.329000Z", "iopub.status.idle": "2024-06-04T16:21:48.479000Z", "shell.execute_reply": "2024-06-04T16:21:48.479000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String?Char?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String? & Char?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String? \u001b[0m\u001b[90m Char? \u001b[0m\n", "─────┼──────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = open(deserialize, \"x.bin\")" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:48.482000Z", "iopub.status.busy": "2024-06-04T16:21:48.482000Z", "iopub.status.idle": "2024-06-04T16:21:48.487000Z", "shell.execute_reply": "2024-06-04T16:21:48.487000Z" } }, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Type}:\n", " Bool\n", " Union{Missing, Int64}\n", " Union{Missing, String}\n", " Union{Missing, Char}" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eltype.(eachcol(y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### JDF.jl\n", "\n", "`JDF.jl` is a relatively new package designed to serialize DataFrames. You can save a DataFrame with the `savejdf` function.\n", "\n", "For more details about design assumptions and limitations of `JDF.jl` please check out https://github.com/xiaodaigh/JDF.jl." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:48.489000Z", "iopub.status.busy": "2024-06-04T16:21:48.489000Z", "iopub.status.idle": "2024-06-04T16:21:49.592000Z", "shell.execute_reply": "2024-06-04T16:21:49.592000Z" } }, "outputs": [], "source": [ "JDF.save(\"x.jdf\", x);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To load the saved JDF file, one can use the `loadjdf` function" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:49.594000Z", "iopub.status.busy": "2024-06-04T16:21:49.594000Z", "iopub.status.idle": "2024-06-04T16:21:51.336000Z", "shell.execute_reply": "2024-06-04T16:21:51.336000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String?Char?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String? & Char?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String? \u001b[0m\u001b[90m Char? \u001b[0m\n", "─────┼──────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x_loaded = JDF.load(\"x.jdf\") |> DataFrame" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can see that they are the same" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:51.339000Z", "iopub.status.busy": "2024-06-04T16:21:51.339000Z", "iopub.status.idle": "2024-06-04T16:21:51.574000Z", "shell.execute_reply": "2024-06-04T16:21:51.574000Z" } }, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isequal(x_loaded, x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "JDF.jl offers the ability to load only certain columns from disk to help with working with large files" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:51.577000Z", "iopub.status.busy": "2024-06-04T16:21:51.577000Z", "iopub.status.idle": "2024-06-04T16:21:51.763000Z", "shell.execute_reply": "2024-06-04T16:21:51.763000Z" } }, "outputs": [ { "data": { "text/plain": [ "JDFFile{String}(\"x.jdf\")" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# set up a JDFFile which is a on disk representation of `x` backed by JDF.jl\n", "x_ondisk = jdf\"x.jdf\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see all the names of `x` without loading it into memory" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:51.765000Z", "iopub.status.busy": "2024-06-04T16:21:51.765000Z", "iopub.status.idle": "2024-06-04T16:21:52.967000Z", "shell.execute_reply": "2024-06-04T16:21:52.967000Z" } }, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Symbol}:\n", " :A\n", " :B\n", " :C\n", " :D" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "names(x_ondisk)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The below is an example of how to load only columns `:A` and `:D`" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:52.970000Z", "iopub.status.busy": "2024-06-04T16:21:52.970000Z", "iopub.status.idle": "2024-06-04T16:21:53.033000Z", "shell.execute_reply": "2024-06-04T16:21:53.033000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×2 DataFrame
RowAD
BoolChar?
1truea
2falsemissing
3truec
" ], "text/latex": [ "\\begin{tabular}{r|cc}\n", "\t& A & D\\\\\n", "\t\\hline\n", "\t& Bool & Char?\\\\\n", "\t\\hline\n", "\t1 & 1 & a \\\\\n", "\t2 & 0 & \\emph{missing} \\\\\n", "\t3 & 1 & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×2 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Char? \u001b[0m\n", "─────┼────────────────\n", " 1 │ true a\n", " 2 │ false \u001b[90m missing \u001b[0m\n", " 3 │ true c" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xd = JDF.load(x_ondisk; cols=[\"A\", \"D\"]) |> DataFrame" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### JLSO.jl\n", "Another way to perform serialization is by using the [JLSO.jl](https://github.com/invenia/JLSO.jl) library:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:53.036000Z", "iopub.status.busy": "2024-06-04T16:21:53.036000Z", "iopub.status.idle": "2024-06-04T16:21:56.778000Z", "shell.execute_reply": "2024-06-04T16:21:56.778000Z" } }, "outputs": [], "source": [ "JLSO.save(\"x.jlso\", :data => x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can load back the file to `y`" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:56.780000Z", "iopub.status.busy": "2024-06-04T16:21:56.780000Z", "iopub.status.idle": "2024-06-04T16:21:58.654000Z", "shell.execute_reply": "2024-06-04T16:21:58.654000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String?Char?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String? & Char?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String? \u001b[0m\u001b[90m Char? \u001b[0m\n", "─────┼──────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = JLSO.load(\"x.jlso\")[:data]" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:58.657000Z", "iopub.status.busy": "2024-06-04T16:21:58.657000Z", "iopub.status.idle": "2024-06-04T16:21:58.663000Z", "shell.execute_reply": "2024-06-04T16:21:58.662000Z" } }, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Type}:\n", " Bool\n", " Union{Missing, Int64}\n", " Union{Missing, String}\n", " Union{Missing, Char}" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eltype.(eachcol(y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## JSONTables.jl\n", "Often you might need to read and write data stored in JSON format. JSONTables.jl provides a way to process them in row-oriented or column-oriented layout. We present both options below." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:58.664000Z", "iopub.status.busy": "2024-06-04T16:21:58.664000Z", "iopub.status.idle": "2024-06-04T16:21:58.893000Z", "shell.execute_reply": "2024-06-04T16:21:58.893000Z" } }, "outputs": [ { "data": { "text/plain": [ "106" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "open(io -> arraytable(io, x), \"x1.json\", \"w\")" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:58.896000Z", "iopub.status.busy": "2024-06-04T16:21:58.896000Z", "iopub.status.idle": "2024-06-04T16:21:59.083000Z", "shell.execute_reply": "2024-06-04T16:21:59.083000Z" } }, "outputs": [ { "data": { "text/plain": [ "76" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "open(io -> objecttable(io, x), \"x2.json\", \"w\")" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:59.086000Z", "iopub.status.busy": "2024-06-04T16:21:59.086000Z", "iopub.status.idle": "2024-06-04T16:21:59.086000Z", "shell.execute_reply": "2024-06-04T16:21:59.086000Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[{\"A\":true,\"B\":1,\"C\":null,\"D\":\"a\"},{\"A\":false,\"B\":2,\"C\":\"b\",\"D\":null},{\"A\":true,\"B\":null,\"C\":\"c\",\"D\":\"c\"}]" ] } ], "source": [ "print(read(\"x1.json\", String))" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:59.088000Z", "iopub.status.busy": "2024-06-04T16:21:59.088000Z", "iopub.status.idle": "2024-06-04T16:21:59.089000Z", "shell.execute_reply": "2024-06-04T16:21:59.089000Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\"A\":[true,false,true],\"B\":[1,2,null],\"C\":[null,\"b\",\"c\"],\"D\":[\"a\",null,\"c\"]}" ] } ], "source": [ "print(read(\"x2.json\", String))" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:59.091000Z", "iopub.status.busy": "2024-06-04T16:21:59.091000Z", "iopub.status.idle": "2024-06-04T16:21:59.524000Z", "shell.execute_reply": "2024-06-04T16:21:59.524000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String?String?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String? & String?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String? \u001b[0m\u001b[90m String? \u001b[0m\n", "─────┼──────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y1 = open(jsontable, \"x1.json\") |> DataFrame" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:59.526000Z", "iopub.status.busy": "2024-06-04T16:21:59.526000Z", "iopub.status.idle": "2024-06-04T16:21:59.532000Z", "shell.execute_reply": "2024-06-04T16:21:59.532000Z" } }, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Type}:\n", " Bool\n", " Union{Missing, Int64}\n", " Union{Missing, String}\n", " Union{Missing, String}" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eltype.(eachcol(y1))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:59.534000Z", "iopub.status.busy": "2024-06-04T16:21:59.534000Z", "iopub.status.idle": "2024-06-04T16:21:59.942000Z", "shell.execute_reply": "2024-06-04T16:21:59.942000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String?String?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String? & String?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String? \u001b[0m\u001b[90m String? \u001b[0m\n", "─────┼──────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y2 = open(jsontable, \"x2.json\") |> DataFrame" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:59.945000Z", "iopub.status.busy": "2024-06-04T16:21:59.945000Z", "iopub.status.idle": "2024-06-04T16:21:59.950000Z", "shell.execute_reply": "2024-06-04T16:21:59.950000Z" } }, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Type}:\n", " Bool\n", " Union{Missing, Int64}\n", " Union{Missing, String}\n", " Union{Missing, String}" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eltype.(eachcol(y2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arrow.jl\n", "Finally we use Apache Arrow format that allows, in particular, for data interchange with R or Python." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:21:59.952000Z", "iopub.status.busy": "2024-06-04T16:21:59.952000Z", "iopub.status.idle": "2024-06-04T16:22:05.408000Z", "shell.execute_reply": "2024-06-04T16:22:05.408000Z" } }, "outputs": [ { "data": { "text/plain": [ "\"x.arrow\"" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Arrow.write(\"x.arrow\", x)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:05.411000Z", "iopub.status.busy": "2024-06-04T16:22:05.411000Z", "iopub.status.idle": "2024-06-04T16:22:07.782000Z", "shell.execute_reply": "2024-06-04T16:22:07.782000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String?Char?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String? & Char?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String? \u001b[0m\u001b[90m Char? \u001b[0m\n", "─────┼──────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = Arrow.Table(\"x.arrow\") |> DataFrame" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:07.785000Z", "iopub.status.busy": "2024-06-04T16:22:07.785000Z", "iopub.status.idle": "2024-06-04T16:22:07.790000Z", "shell.execute_reply": "2024-06-04T16:22:07.790000Z" } }, "outputs": [ { "data": { "text/plain": [ "4-element Vector{Type}:\n", " Bool\n", " Union{Missing, Int64}\n", " Union{Missing, String}\n", " Union{Missing, Char}" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eltype.(eachcol(y))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that columns of `y` are immutable" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:07.792000Z", "iopub.status.busy": "2024-06-04T16:22:07.792000Z", "iopub.status.idle": "2024-06-04T16:22:08.401000Z", "shell.execute_reply": "2024-06-04T16:22:08.401000Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ReadOnlyMemoryError()" ] } ], "source": [ "try\n", " y.A[1] = false\n", "catch e\n", " show(e)\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is because `Arrow.Table` uses memory mapping and thus uses a custom vector types:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:08.404000Z", "iopub.status.busy": "2024-06-04T16:22:08.404000Z", "iopub.status.idle": "2024-06-04T16:22:08.832000Z", "shell.execute_reply": "2024-06-04T16:22:08.831000Z" } }, "outputs": [ { "data": { "text/plain": [ "3-element Arrow.BoolVector{Bool}:\n", " 1\n", " 0\n", " 1" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y.A" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:08.834000Z", "iopub.status.busy": "2024-06-04T16:22:08.834000Z", "iopub.status.idle": "2024-06-04T16:22:09.276000Z", "shell.execute_reply": "2024-06-04T16:22:09.275000Z" } }, "outputs": [ { "data": { "text/plain": [ "3-element Arrow.Primitive{Union{Missing, Int64}, Vector{Int64}}:\n", " 1\n", " 2\n", " missing" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y.B" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can get standard Julia Base vectors by copying a data frame" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:09.278000Z", "iopub.status.busy": "2024-06-04T16:22:09.278000Z", "iopub.status.idle": "2024-06-04T16:22:09.287000Z", "shell.execute_reply": "2024-06-04T16:22:09.287000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
RowABCD
BoolInt64?String?Char?
1true1missinga
2false2bmissing
3truemissingcc
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& A & B & C & D\\\\\n", "\t\\hline\n", "\t& Bool & Int64? & String? & Char?\\\\\n", "\t\\hline\n", "\t1 & 1 & 1 & \\emph{missing} & a \\\\\n", "\t2 & 0 & 2 & b & \\emph{missing} \\\\\n", "\t3 & 1 & \\emph{missing} & c & c \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m A \u001b[0m\u001b[1m B \u001b[0m\u001b[1m C \u001b[0m\u001b[1m D \u001b[0m\n", " │\u001b[90m Bool \u001b[0m\u001b[90m Int64? \u001b[0m\u001b[90m String? \u001b[0m\u001b[90m Char? \u001b[0m\n", "─────┼──────────────────────────────────\n", " 1 │ true 1 \u001b[90m missing \u001b[0m a\n", " 2 │ false 2 b \u001b[90m missing \u001b[0m\n", " 3 │ true \u001b[90m missing \u001b[0m c c" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y2 = copy(y)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:09.289000Z", "iopub.status.busy": "2024-06-04T16:22:09.289000Z", "iopub.status.idle": "2024-06-04T16:22:09.549000Z", "shell.execute_reply": "2024-06-04T16:22:09.549000Z" } }, "outputs": [ { "data": { "text/plain": [ "3-element Vector{Bool}:\n", " 1\n", " 0\n", " 1" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y2.A" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:09.552000Z", "iopub.status.busy": "2024-06-04T16:22:09.552000Z", "iopub.status.idle": "2024-06-04T16:22:09.886000Z", "shell.execute_reply": "2024-06-04T16:22:09.885000Z" } }, "outputs": [ { "data": { "text/plain": [ "3-element Vector{Union{Missing, Int64}}:\n", " 1\n", " 2\n", " missing" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y2.B" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic bechmarking\n", "\n", "Next, we'll create some files, so be careful that you don't already have these files in your working directory!\n", "\n", "In particular, we'll time how long it takes us to write a `DataFrame` with 1000 rows and 100000 columns." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:09.888000Z", "iopub.status.busy": "2024-06-04T16:22:09.888000Z", "iopub.status.idle": "2024-06-04T16:22:10.249000Z", "shell.execute_reply": "2024-06-04T16:22:10.249000Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "First run\n" ] } ], "source": [ "bigdf = DataFrame(rand(Bool, 10^4, 1000), :auto)\n", "\n", "bigdf[!, 1] = Int.(bigdf[!, 1])\n", "bigdf[!, 2] = bigdf[!, 2] .+ 0.5\n", "bigdf[!, 3] = string.(bigdf[!, 3], \", as string\")\n", "\n", "println(\"First run\")" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:10.251000Z", "iopub.status.busy": "2024-06-04T16:22:10.251000Z", "iopub.status.idle": "2024-06-04T16:22:46.811000Z", "shell.execute_reply": "2024-06-04T16:22:46.811000Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CSV.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 4.520346 seconds (44.60 M allocations: 1.126 GiB, 4.27% gc time, 63.40% compilation time)\n", "Serialization\n", " 0.179294 seconds (148.06 k allocations: 9.352 MiB, 26.73% compilation time)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "JDF.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.095084 seconds (46.82 k allocations: 147.657 MiB, 11.32% gc time, 111.51% compilation time)\n", "JLSO.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 1.029119 seconds (174.96 k allocations: 14.867 MiB, 5.98% compilation time)\n", "Arrow.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 3.256308 seconds (3.02 M allocations: 208.636 MiB, 1.15% gc time, 96.99% compilation time)\n", "JSONTables.jl arraytable\n", " 11.686247 seconds (229.61 M allocations: 5.423 GiB, 11.29% gc time, 0.07% compilation time)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "JSONTables.jl objecttable\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.372464 seconds (60.98 k allocations: 308.136 MiB, 35.64% gc time, 18.88% compilation time)\n", "Second run\n", "CSV.jl\n", " 1.618924 seconds (44.40 M allocations: 1.113 GiB, 12.99% gc time)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Serialization\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.141854 seconds (14.32 k allocations: 397.188 KiB, 5.90% compilation time)\n", "JDF.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.081677 seconds (28.12 k allocations: 146.393 MiB, 9.51% gc time)\n", "JLSO.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.968076 seconds (30.11 k allocations: 4.701 MiB)\n", "Arrow.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.098084 seconds (52.79 k allocations: 5.619 MiB)\n", "JSONTables.jl arraytable\n", " 11.693286 seconds (229.61 M allocations: 5.423 GiB, 10.47% gc time, 0.07% compilation time)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "JSONTables.jl objecttable\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.337134 seconds (15.94 k allocations: 305.186 MiB, 38.97% gc time, 2.39% compilation time)\n" ] }, { "data": { "text/plain": [ "0.337387923" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "println(\"CSV.jl\")\n", "csvwrite1 = @elapsed @time CSV.write(\"bigdf1.csv\", bigdf)\n", "println(\"Serialization\")\n", "serializewrite1 = @elapsed @time open(io -> serialize(io, bigdf), \"bigdf.bin\", \"w\")\n", "println(\"JDF.jl\")\n", "jdfwrite1 = @elapsed @time JDF.save(\"bigdf.jdf\", bigdf)\n", "println(\"JLSO.jl\")\n", "jlsowrite1 = @elapsed @time JLSO.save(\"bigdf.jlso\", :data => bigdf)\n", "println(\"Arrow.jl\")\n", "arrowwrite1 = @elapsed @time Arrow.write(\"bigdf.arrow\", bigdf)\n", "println(\"JSONTables.jl arraytable\")\n", "jsontablesawrite1 = @elapsed @time open(io -> arraytable(io, bigdf), \"bigdf1.json\", \"w\")\n", "println(\"JSONTables.jl objecttable\")\n", "jsontablesowrite1 = @elapsed @time open(io -> objecttable(io, bigdf), \"bigdf2.json\", \"w\")\n", "println(\"Second run\")\n", "println(\"CSV.jl\")\n", "csvwrite2 = @elapsed @time CSV.write(\"bigdf1.csv\", bigdf)\n", "println(\"Serialization\")\n", "serializewrite2 = @elapsed @time open(io -> serialize(io, bigdf), \"bigdf.bin\", \"w\")\n", "println(\"JDF.jl\")\n", "jdfwrite2 = @elapsed @time JDF.save(\"bigdf.jdf\", bigdf)\n", "println(\"JLSO.jl\")\n", "jlsowrite2 = @elapsed @time JLSO.save(\"bigdf.jlso\", :data => bigdf)\n", "println(\"Arrow.jl\")\n", "arrowwrite2 = @elapsed @time Arrow.write(\"bigdf.arrow\", bigdf)\n", "println(\"JSONTables.jl arraytable\")\n", "jsontablesawrite2 = @elapsed @time open(io -> arraytable(io, bigdf), \"bigdf1.json\", \"w\")\n", "println(\"JSONTables.jl objecttable\")\n", "jsontablesowrite2 = @elapsed @time open(io -> objecttable(io, bigdf), \"bigdf2.json\", \"w\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Exclude JSONTables.jl arraytable due to timing" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:46.814000Z", "iopub.status.busy": "2024-06-04T16:22:46.814000Z", "iopub.status.idle": "2024-06-04T16:22:51.024000Z", "shell.execute_reply": "2024-06-04T16:22:51.024000Z" } }, "outputs": [ { "data": { "image/png": "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", "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ], "text/html": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "groupedbar(\n", " repeat([\"CSV.jl\", \"Serialization\", \"JDF.jl\", \"JLSO.jl\", \"Arrow.jl\", \"JSONTables.jl\\nobjecttable\"],\n", " inner=2),\n", " [csvwrite1, csvwrite2, serializewrite1, serializewrite1, jdfwrite1, jdfwrite2,\n", " jlsowrite1, jlsowrite2, arrowwrite1, arrowwrite2, jsontablesowrite2, jsontablesowrite2],\n", " group=repeat([\"1st\", \"2nd\"], outer=6),\n", " ylab=\"Second\",\n", " title=\"Write Performance\\nDataFrame: bigdf\\nSize: $(size(bigdf))\"\n", ")" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:51.026000Z", "iopub.status.busy": "2024-06-04T16:22:51.026000Z", "iopub.status.idle": "2024-06-04T16:22:51.766000Z", "shell.execute_reply": "2024-06-04T16:22:51.766000Z" } }, "outputs": [ { "data": { "text/html": [ "
6×2 DataFrame
Rowfilesize
StringInt64
1bigdf.arrow1742770
2bigdf.bin5201790
3bigdf.jdf5222771
4bigdf1.csv55085173
5bigdf2.json55089174
6bigdf1.json124030281
" ], "text/latex": [ "\\begin{tabular}{r|cc}\n", "\t& file & size\\\\\n", "\t\\hline\n", "\t& String & Int64\\\\\n", "\t\\hline\n", "\t1 & bigdf.arrow & 1742770 \\\\\n", "\t2 & bigdf.bin & 5201790 \\\\\n", "\t3 & bigdf.jdf & 5222771 \\\\\n", "\t4 & bigdf1.csv & 55085173 \\\\\n", "\t5 & bigdf2.json & 55089174 \\\\\n", "\t6 & bigdf1.json & 124030281 \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m6×2 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m file \u001b[0m\u001b[1m size \u001b[0m\n", " │\u001b[90m String \u001b[0m\u001b[90m Int64 \u001b[0m\n", "─────┼────────────────────────\n", " 1 │ bigdf.arrow 1742770\n", " 2 │ bigdf.bin 5201790\n", " 3 │ bigdf.jdf 5222771\n", " 4 │ bigdf1.csv 55085173\n", " 5 │ bigdf2.json 55089174\n", " 6 │ bigdf1.json 124030281" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data_files = [\"bigdf1.csv\", \"bigdf.bin\", \"bigdf.arrow\", \"bigdf1.json\", \"bigdf2.json\"]\n", "df = DataFrame(file=data_files, size=getfield.(stat.(data_files), :size))\n", "append!(df, DataFrame(file=\"bigdf.jdf\", size=reduce((x, y) -> x + y.size,\n", " stat.(joinpath.(\"bigdf.jdf\", readdir(\"bigdf.jdf\"))),\n", " init=0)))\n", "sort!(df, :size)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:51.769000Z", "iopub.status.busy": "2024-06-04T16:22:51.769000Z", "iopub.status.idle": "2024-06-04T16:22:52.671000Z", "shell.execute_reply": "2024-06-04T16:22:52.670000Z" } }, "outputs": [ { "data": { "image/png": "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", "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ], "text/html": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@df df plot(:file, :size / 1024^2, seriestype=:bar, title=\"Format File Size (MB)\", label=\"Size\", ylab=\"MB\")" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:22:52.674000Z", "iopub.status.busy": "2024-06-04T16:22:52.674000Z", "iopub.status.idle": "2024-06-04T16:23:24.667000Z", "shell.execute_reply": "2024-06-04T16:23:24.667000Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "First run\n", "CSV.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 1.979127 seconds (1.93 M allocations: 155.763 MiB, 0.96% gc time, 244.67% compilation time)\n", "Serialization\n", " 0.433973 seconds (9.49 M allocations: 155.130 MiB, 10.95% gc time, 5.89% compilation time)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "JDF.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.180052 seconds (123.89 k allocations: 161.520 MiB, 122.57% compilation time)\n", "JLSO.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.359403 seconds (9.50 M allocations: 157.726 MiB, 7.37% gc time, 5.84% compilation time)\n", "Arrow.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.363759 seconds (347.75 k allocations: 22.543 MiB, 98.50% compilation time)\n", " 0.053692 seconds (12.50 k allocations: 10.319 MiB)\n", "JSONTables.jl arraytable\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 13.347633 seconds (30.24 M allocations: 2.530 GiB, 4.69% gc time)\n", "JSONTables.jl objecttable\n", " 0.347721 seconds (7.05 k allocations: 282.602 MiB, 5.33% gc time, 0.04% compilation time)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Second run\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.392585 seconds (160.96 k allocations: 35.388 MiB)\n", "Serialization\n", " 0.399444 seconds (9.48 M allocations: 154.650 MiB, 6.76% gc time)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "JDF.jl\n", " 0.039274 seconds (69.69 k allocations: 157.945 MiB)\n", "JLSO.jl\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0.336919 seconds (9.50 M allocations: 157.323 MiB)\n", "Arrow.jl\n", " 0.004663 seconds (67.53 k allocations: 3.292 MiB)\n", " 0.053299 seconds (12.50 k allocations: 10.319 MiB)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "JSONTables.jl arraytable\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 12.999409 seconds (30.24 M allocations: 2.530 GiB, 2.74% gc time)\n", "JSONTables.jl objecttable\n", " 0.328286 seconds (6.06 k allocations: 282.532 MiB, 2.58% gc time)\n" ] } ], "source": [ "println(\"First run\")\n", "println(\"CSV.jl\")\n", "csvread1 = @elapsed @time CSV.read(\"bigdf1.csv\", DataFrame)\n", "println(\"Serialization\")\n", "serializeread1 = @elapsed @time open(deserialize, \"bigdf.bin\")\n", "println(\"JDF.jl\")\n", "jdfread1 = @elapsed @time JDF.load(\"bigdf.jdf\") |> DataFrame\n", "println(\"JLSO.jl\")\n", "jlsoread1 = @elapsed @time JLSO.load(\"bigdf.jlso\")\n", "println(\"Arrow.jl\")\n", "arrowread1 = @elapsed @time df_tmp = Arrow.Table(\"bigdf.arrow\") |> DataFrame\n", "arrowread1copy = @elapsed @time copy(df_tmp)\n", "println(\"JSONTables.jl arraytable\")\n", "jsontablesaread1 = @elapsed @time open(jsontable, \"bigdf1.json\")\n", "println(\"JSONTables.jl objecttable\")\n", "jsontablesoread1 = @elapsed @time open(jsontable, \"bigdf2.json\")\n", "println(\"Second run\")\n", "csvread2 = @elapsed @time CSV.read(\"bigdf1.csv\", DataFrame)\n", "println(\"Serialization\")\n", "serializeread2 = @elapsed @time open(deserialize, \"bigdf.bin\")\n", "println(\"JDF.jl\")\n", "jdfread2 = @elapsed @time JDF.load(\"bigdf.jdf\") |> DataFrame\n", "println(\"JLSO.jl\")\n", "jlsoread2 = @elapsed @time JLSO.load(\"bigdf.jlso\")\n", "println(\"Arrow.jl\")\n", "arrowread2 = @elapsed @time df_tmp = Arrow.Table(\"bigdf.arrow\") |> DataFrame\n", "arrowread2copy = @elapsed @time copy(df_tmp)\n", "println(\"JSONTables.jl arraytable\")\n", "jsontablesaread2 = @elapsed @time open(jsontable, \"bigdf1.json\")\n", "println(\"JSONTables.jl objecttable\")\n", "jsontablesoread2 = @elapsed @time open(jsontable, \"bigdf2.json\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Exclude JSON arraytable due to much longer timing" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:24.669000Z", "iopub.status.busy": "2024-06-04T16:23:24.669000Z", "iopub.status.idle": "2024-06-04T16:23:24.711000Z", "shell.execute_reply": "2024-06-04T16:23:24.711000Z" } }, "outputs": [ { "data": { "image/png": "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", "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ], "text/html": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "groupedbar(\n", " repeat([\"CSV.jl\", \"Serialization\", \"JDF.jl\", \"JLSO.jl\", \"Arrow.jl\", \"Arrow.jl\\ncopy\", #\"JSON\\narraytable\",\n", " \"JSON\\nobjecttable\"], inner=2),\n", " [csvread1, csvread2, serializeread1, serializeread2, jdfread1, jdfread2, jlsoread1, jlsoread2,\n", " arrowread1, arrowread2, arrowread1 + arrowread1copy, arrowread2 + arrowread2copy,\n", " # jsontablesaread1, jsontablesaread2,\n", " jsontablesoread1, jsontablesoread2],\n", " group=repeat([\"1st\", \"2nd\"], outer=7),\n", " ylab=\"Second\",\n", " title=\"Read Performance\\nDataFrame: bigdf\\nSize: $(size(bigdf))\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using gzip compression\n", "A common user requirement is to be able to load and save CSV that are compressed using gzip. Below we show how this can be accomplished using `CodecZlib.jl`. The same pattern is applicable to `JSONTables.jl` compression/decompression.\n", "\n", "Again make sure that you do not have file named `df_compress_test.csv.gz` in your working directory.\n", "\n", "We first generate a random data frame." ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:24.715000Z", "iopub.status.busy": "2024-06-04T16:23:24.715000Z", "iopub.status.idle": "2024-06-04T16:23:24.785000Z", "shell.execute_reply": "2024-06-04T16:23:24.785000Z" } }, "outputs": [ { "data": { "text/html": [ "
10×1000 DataFrame
900 columns omitted
Rowx1x2x3x4x5x6x7x8x9x10x11x12x13x14x15x16x17x18x19x20x21x22x23x24x25x26x27x28x29x30x31x32x33x34x35x36x37x38x39x40x41x42x43x44x45x46x47x48x49x50x51x52x53x54x55x56x57x58x59x60x61x62x63x64x65x66x67x68x69x70x71x72x73x74x75x76x77x78x79x80x81x82x83x84x85x86x87x88x89x90x91x92x93x94x95x96x97x98x99x100
Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64
11281032688831568228610113104994319366171089336253177105102103452945771059823238783829612293958766674941934101
28147101021475107453633981071857324472911821410621056534194143645103106525710710108332828888632629469258996168810
3103264333107592637106758391063863874442104933491021066108511410101953874657101052971467613723107161038299972101061026
45210113465515612413968276815710298142252141066531067510862239681857333410899107629731058454104265477135266425
521096284369469781875515321019376612106454782736831274510644645109766781109795491099366684459932622217346124
646109468294739109254582324106568142649108210917326421092466351654777106101976105323959875196679107492185493277
710993499797919523371082192786210985132768672548725103637126274124218314666991336145292747223210610185869101
8103126739510438235286566810884104212248768610318310761033855575715101085476352136799856323749682137592179103108
9953411039593102341071034874319379869381010221022638275356458155210104423281069104978717210102394385101101171010242928
10795479511247497410177107434961010516357569232109767884855274681110610858573889107187288414661342471529196847
" ], "text/latex": [ "\\begin{tabular}{r|ccccccccccccc}\n", "\t& x1 & x2 & x3 & x4 & x5 & x6 & x7 & x8 & x9 & x10 & x11 & x12 & \\\\\n", "\t\\hline\n", "\t& Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & \\\\\n", "\t\\hline\n", "\t1 & 1 & 2 & 8 & 10 & 3 & 2 & 6 & 8 & 8 & 8 & 3 & 1 & $\\dots$ \\\\\n", "\t2 & 8 & 1 & 4 & 7 & 10 & 10 & 2 & 1 & 4 & 7 & 5 & 10 & $\\dots$ \\\\\n", "\t3 & 10 & 3 & 2 & 6 & 4 & 3 & 3 & 3 & 10 & 7 & 5 & 9 & $\\dots$ \\\\\n", "\t4 & 5 & 2 & 10 & 1 & 1 & 3 & 4 & 6 & 5 & 5 & 1 & 5 & $\\dots$ \\\\\n", "\t5 & 2 & 10 & 9 & 6 & 2 & 8 & 4 & 3 & 6 & 9 & 4 & 6 & $\\dots$ \\\\\n", "\t6 & 4 & 6 & 10 & 9 & 4 & 6 & 8 & 2 & 9 & 4 & 7 & 3 & $\\dots$ \\\\\n", "\t7 & 10 & 9 & 9 & 3 & 4 & 9 & 9 & 7 & 9 & 7 & 9 & 1 & $\\dots$ \\\\\n", "\t8 & 10 & 3 & 1 & 2 & 6 & 7 & 3 & 9 & 5 & 10 & 4 & 3 & $\\dots$ \\\\\n", "\t9 & 9 & 5 & 3 & 4 & 1 & 10 & 3 & 9 & 5 & 9 & 3 & 10 & $\\dots$ \\\\\n", "\t10 & 7 & 9 & 5 & 4 & 7 & 9 & 5 & 1 & 1 & 2 & 4 & 7 & $\\dots$ \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m10×1000 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m x1 \u001b[0m\u001b[1m x2 \u001b[0m\u001b[1m x3 \u001b[0m\u001b[1m x4 \u001b[0m\u001b[1m x5 \u001b[0m\u001b[1m x6 \u001b[0m\u001b[1m x7 \u001b[0m\u001b[1m x8 \u001b[0m\u001b[1m x9 \u001b[0m\u001b[1m x10 \u001b[0m\u001b[1m x\u001b[0m ⋯\n", " │\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m I\u001b[0m ⋯\n", "─────┼──────────────────────────────────────────────────────────────────────────\n", " 1 │ 1 2 8 10 3 2 6 8 8 8 ⋯\n", " 2 │ 8 1 4 7 10 10 2 1 4 7\n", " 3 │ 10 3 2 6 4 3 3 3 10 7\n", " 4 │ 5 2 10 1 1 3 4 6 5 5\n", " 5 │ 2 10 9 6 2 8 4 3 6 9 ⋯\n", " 6 │ 4 6 10 9 4 6 8 2 9 4\n", " 7 │ 10 9 9 3 4 9 9 7 9 7\n", " 8 │ 10 3 1 2 6 7 3 9 5 10\n", " 9 │ 9 5 3 4 1 10 3 9 5 9 ⋯\n", " 10 │ 7 9 5 4 7 9 5 1 1 2\n", "\u001b[36m 990 columns omitted\u001b[0m" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = DataFrame(rand(1:10, 10, 1000), :auto)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:24.788000Z", "iopub.status.busy": "2024-06-04T16:23:24.788000Z", "iopub.status.idle": "2024-06-04T16:23:26.608000Z", "shell.execute_reply": "2024-06-04T16:23:26.608000Z" } }, "outputs": [], "source": [ "# GzipCompressorStream comes from CodecZlib\n", "open(\"df_compress_test.csv.gz\", \"w\") do io\n", " stream = GzipCompressorStream(io)\n", " CSV.write(stream, df)\n", " close(stream)\n", "end" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:26.610000Z", "iopub.status.busy": "2024-06-04T16:23:26.610000Z", "iopub.status.idle": "2024-06-04T16:23:26.710000Z", "shell.execute_reply": "2024-06-04T16:23:26.710000Z" } }, "outputs": [ { "data": { "text/html": [ "
10×1000 DataFrame
900 columns omitted
Rowx1x2x3x4x5x6x7x8x9x10x11x12x13x14x15x16x17x18x19x20x21x22x23x24x25x26x27x28x29x30x31x32x33x34x35x36x37x38x39x40x41x42x43x44x45x46x47x48x49x50x51x52x53x54x55x56x57x58x59x60x61x62x63x64x65x66x67x68x69x70x71x72x73x74x75x76x77x78x79x80x81x82x83x84x85x86x87x88x89x90x91x92x93x94x95x96x97x98x99x100
Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64Int64
11281032688831568228610113104994319366171089336253177105102103452945771059823238783829612293958766674941934101
28147101021475107453633981071857324472911821410621056534194143645103106525710710108332828888632629469258996168810
3103264333107592637106758391063863874442104933491021066108511410101953874657101052971467613723107161038299972101061026
45210113465515612413968276815710298142252141066531067510862239681857333410899107629731058454104265477135266425
521096284369469781875515321019376612106454782736831274510644645109766781109795491099366684459932622217346124
646109468294739109254582324106568142649108210917326421092466351654777106101976105323959875196679107492185493277
710993499797919523371082192786210985132768672548725103637126274124218314666991336145292747223210610185869101
8103126739510438235286566810884104212248768610318310761033855575715101085476352136799856323749682137592179103108
9953411039593102341071034874319379869381010221022638275356458155210104423281069104978717210102394385101101171010242928
10795479511247497410177107434961010516357569232109767884855274681110610858573889107187288414661342471529196847
" ], "text/latex": [ "\\begin{tabular}{r|ccccccccccccc}\n", "\t& x1 & x2 & x3 & x4 & x5 & x6 & x7 & x8 & x9 & x10 & x11 & x12 & \\\\\n", "\t\\hline\n", "\t& Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & Int64 & \\\\\n", "\t\\hline\n", "\t1 & 1 & 2 & 8 & 10 & 3 & 2 & 6 & 8 & 8 & 8 & 3 & 1 & $\\dots$ \\\\\n", "\t2 & 8 & 1 & 4 & 7 & 10 & 10 & 2 & 1 & 4 & 7 & 5 & 10 & $\\dots$ \\\\\n", "\t3 & 10 & 3 & 2 & 6 & 4 & 3 & 3 & 3 & 10 & 7 & 5 & 9 & $\\dots$ \\\\\n", "\t4 & 5 & 2 & 10 & 1 & 1 & 3 & 4 & 6 & 5 & 5 & 1 & 5 & $\\dots$ \\\\\n", "\t5 & 2 & 10 & 9 & 6 & 2 & 8 & 4 & 3 & 6 & 9 & 4 & 6 & $\\dots$ \\\\\n", "\t6 & 4 & 6 & 10 & 9 & 4 & 6 & 8 & 2 & 9 & 4 & 7 & 3 & $\\dots$ \\\\\n", "\t7 & 10 & 9 & 9 & 3 & 4 & 9 & 9 & 7 & 9 & 7 & 9 & 1 & $\\dots$ \\\\\n", "\t8 & 10 & 3 & 1 & 2 & 6 & 7 & 3 & 9 & 5 & 10 & 4 & 3 & $\\dots$ \\\\\n", "\t9 & 9 & 5 & 3 & 4 & 1 & 10 & 3 & 9 & 5 & 9 & 3 & 10 & $\\dots$ \\\\\n", "\t10 & 7 & 9 & 5 & 4 & 7 & 9 & 5 & 1 & 1 & 2 & 4 & 7 & $\\dots$ \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m10×1000 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m x1 \u001b[0m\u001b[1m x2 \u001b[0m\u001b[1m x3 \u001b[0m\u001b[1m x4 \u001b[0m\u001b[1m x5 \u001b[0m\u001b[1m x6 \u001b[0m\u001b[1m x7 \u001b[0m\u001b[1m x8 \u001b[0m\u001b[1m x9 \u001b[0m\u001b[1m x10 \u001b[0m\u001b[1m x\u001b[0m ⋯\n", " │\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m I\u001b[0m ⋯\n", "─────┼──────────────────────────────────────────────────────────────────────────\n", " 1 │ 1 2 8 10 3 2 6 8 8 8 ⋯\n", " 2 │ 8 1 4 7 10 10 2 1 4 7\n", " 3 │ 10 3 2 6 4 3 3 3 10 7\n", " 4 │ 5 2 10 1 1 3 4 6 5 5\n", " 5 │ 2 10 9 6 2 8 4 3 6 9 ⋯\n", " 6 │ 4 6 10 9 4 6 8 2 9 4\n", " 7 │ 10 9 9 3 4 9 9 7 9 7\n", " 8 │ 10 3 1 2 6 7 3 9 5 10\n", " 9 │ 9 5 3 4 1 10 3 9 5 9 ⋯\n", " 10 │ 7 9 5 4 7 9 5 1 1 2\n", "\u001b[36m 990 columns omitted\u001b[0m" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df2 = CSV.File(transcode(GzipDecompressor, Mmap.mmap(\"df_compress_test.csv.gz\"))) |> DataFrame" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:26.713000Z", "iopub.status.busy": "2024-06-04T16:23:26.713000Z", "iopub.status.idle": "2024-06-04T16:23:26.736000Z", "shell.execute_reply": "2024-06-04T16:23:26.736000Z" } }, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df == df2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using zip files\n", "\n", "Sometimes you may have files compressed inside a zip file.\n", "\n", "In such a situation you may use [ZipFile.jl](https://github.com/fhs/ZipFile.jl) in conjunction an an appropriate reader to read the files.\n", "\n", "Here we first create a ZIP file and then read back its contents into a `DataFrame`." ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:26.738000Z", "iopub.status.busy": "2024-06-04T16:23:26.738000Z", "iopub.status.idle": "2024-06-04T16:23:26.740000Z", "shell.execute_reply": "2024-06-04T16:23:26.740000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
Rowx1x2x3x4
Int64Int64Int64Int64
18361
26391
35466
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& x1 & x2 & x3 & x4\\\\\n", "\t\\hline\n", "\t& Int64 & Int64 & Int64 & Int64\\\\\n", "\t\\hline\n", "\t1 & 8 & 3 & 6 & 1 \\\\\n", "\t2 & 6 & 3 & 9 & 1 \\\\\n", "\t3 & 5 & 4 & 6 & 6 \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m x1 \u001b[0m\u001b[1m x2 \u001b[0m\u001b[1m x3 \u001b[0m\u001b[1m x4 \u001b[0m\n", " │\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\n", "─────┼────────────────────────────\n", " 1 │ 8 3 6 1\n", " 2 │ 6 3 9 1\n", " 3 │ 5 4 6 6" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df1 = DataFrame(rand(1:10, 3, 4), :auto)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:26.742000Z", "iopub.status.busy": "2024-06-04T16:23:26.742000Z", "iopub.status.idle": "2024-06-04T16:23:26.744000Z", "shell.execute_reply": "2024-06-04T16:23:26.744000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
Rowx1x2x3x4
Int64Int64Int64Int64
16341
29237
310321
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& x1 & x2 & x3 & x4\\\\\n", "\t\\hline\n", "\t& Int64 & Int64 & Int64 & Int64\\\\\n", "\t\\hline\n", "\t1 & 6 & 3 & 4 & 1 \\\\\n", "\t2 & 9 & 2 & 3 & 7 \\\\\n", "\t3 & 10 & 3 & 2 & 1 \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m x1 \u001b[0m\u001b[1m x2 \u001b[0m\u001b[1m x3 \u001b[0m\u001b[1m x4 \u001b[0m\n", " │\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\n", "─────┼────────────────────────────\n", " 1 │ 6 3 4 1\n", " 2 │ 9 2 3 7\n", " 3 │ 10 3 2 1" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df2 = DataFrame(rand(1:10, 3, 4), :auto)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we show yet another way to write a `DataFrame` into a CSV file:\n", "writing a CSV file into the zip file" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:26.746000Z", "iopub.status.busy": "2024-06-04T16:23:26.746000Z", "iopub.status.idle": "2024-06-04T16:23:27.182000Z", "shell.execute_reply": "2024-06-04T16:23:27.182000Z" } }, "outputs": [], "source": [ "w = ZipFile.Writer(\"x.zip\")\n", "\n", "f1 = ZipFile.addfile(w, \"x1.csv\")\n", "write(f1, sprint(show, \"text/csv\", df1))\n", "\n", "# write a second CSV file into zip file\n", "f2 = ZipFile.addfile(w, \"x2.csv\", method=ZipFile.Deflate)\n", "write(f2, sprint(show, \"text/csv\", df2))\n", "\n", "close(w)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we read the CSV we have written:" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:27.184000Z", "iopub.status.busy": "2024-06-04T16:23:27.184000Z", "iopub.status.idle": "2024-06-04T16:23:27.481000Z", "shell.execute_reply": "2024-06-04T16:23:27.481000Z" } }, "outputs": [], "source": [ "z = ZipFile.Reader(\"x.zip\");" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:27.483000Z", "iopub.status.busy": "2024-06-04T16:23:27.483000Z", "iopub.status.idle": "2024-06-04T16:23:27.618000Z", "shell.execute_reply": "2024-06-04T16:23:27.618000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
Rowx1x2x3x4
Int64Int64Int64Int64
18361
26391
35466
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& x1 & x2 & x3 & x4\\\\\n", "\t\\hline\n", "\t& Int64 & Int64 & Int64 & Int64\\\\\n", "\t\\hline\n", "\t1 & 8 & 3 & 6 & 1 \\\\\n", "\t2 & 6 & 3 & 9 & 1 \\\\\n", "\t3 & 5 & 4 & 6 & 6 \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m x1 \u001b[0m\u001b[1m x2 \u001b[0m\u001b[1m x3 \u001b[0m\u001b[1m x4 \u001b[0m\n", " │\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\n", "─────┼────────────────────────────\n", " 1 │ 8 3 6 1\n", " 2 │ 6 3 9 1\n", " 3 │ 5 4 6 6" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# find the index index of file called x1.csv\n", "index_xcsv = findfirst(x -> x.name == \"x1.csv\", z.files)\n", "# to read the x1.csv file in the zip file\n", "df1_2 = CSV.read(read(z.files[index_xcsv]), DataFrame)" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:27.621000Z", "iopub.status.busy": "2024-06-04T16:23:27.621000Z", "iopub.status.idle": "2024-06-04T16:23:27.622000Z", "shell.execute_reply": "2024-06-04T16:23:27.622000Z" } }, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df1_2 == df1" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:27.624000Z", "iopub.status.busy": "2024-06-04T16:23:27.624000Z", "iopub.status.idle": "2024-06-04T16:23:27.671000Z", "shell.execute_reply": "2024-06-04T16:23:27.671000Z" } }, "outputs": [ { "data": { "text/html": [ "
3×4 DataFrame
Rowx1x2x3x4
Int64Int64Int64Int64
16341
29237
310321
" ], "text/latex": [ "\\begin{tabular}{r|cccc}\n", "\t& x1 & x2 & x3 & x4\\\\\n", "\t\\hline\n", "\t& Int64 & Int64 & Int64 & Int64\\\\\n", "\t\\hline\n", "\t1 & 6 & 3 & 4 & 1 \\\\\n", "\t2 & 9 & 2 & 3 & 7 \\\\\n", "\t3 & 10 & 3 & 2 & 1 \\\\\n", "\\end{tabular}\n" ], "text/plain": [ "\u001b[1m3×4 DataFrame\u001b[0m\n", "\u001b[1m Row \u001b[0m│\u001b[1m x1 \u001b[0m\u001b[1m x2 \u001b[0m\u001b[1m x3 \u001b[0m\u001b[1m x4 \u001b[0m\n", " │\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\u001b[90m Int64 \u001b[0m\n", "─────┼────────────────────────────\n", " 1 │ 6 3 4 1\n", " 2 │ 9 2 3 7\n", " 3 │ 10 3 2 1" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# find the index index of file called x2.csv\n", "index_xcsv = findfirst(x -> x.name == \"x2.csv\", z.files)\n", "# to read the x2.csv file in the zip file\n", "df2_2 = CSV.read(read(z.files[index_xcsv]), DataFrame)" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:27.674000Z", "iopub.status.busy": "2024-06-04T16:23:27.673000Z", "iopub.status.idle": "2024-06-04T16:23:27.674000Z", "shell.execute_reply": "2024-06-04T16:23:27.674000Z" } }, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df2_2 == df2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that once you read a given file from `z` object its stream is all used-up (it is at its end). Therefore to read it again you need to close `z` and open it again.\n", "\n", "Also do not forget to close the zip file once done." ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "execution": { "iopub.execute_input": "2024-06-04T16:23:27.677000Z", "iopub.status.busy": "2024-06-04T16:23:27.676000Z", "iopub.status.idle": "2024-06-04T16:23:27.681000Z", "shell.execute_reply": "2024-06-04T16:23:27.681000Z" } }, "outputs": [], "source": [ "close(z)" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.10.1", "language": "julia", "name": "julia-1.10" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.10.3" } }, "nbformat": 4, "nbformat_minor": 3 }