Skip to content
datahacks.dev
CSV

VisiData for Exploring CSV Interactively in the Terminal

Open a CSV in VisiData to scroll, sort, filter and profile it from the keyboard, build frequency tables, and save results, plus its batch mode for scripts.

7 min read
Summarize in AI
Flat illustration of a terminal session.

The most useful thing to do with an unfamiliar CSV is to look at it, and the most common tools for looking are a spreadsheet, which converts your data, and head, which shows 10 lines of a file with 80 columns. VisiData fills the gap. It is a terminal program that opens a file as a sheet of rows and columns, and every operation, sorting, filtering, grouping, profiling, is a keystroke. It never converts your values behind your back, and when you have found the question, it can save the result. I use it as the first stop on a file I have not seen, before deciding which of the tools in CSV tools worth knowing the real work belongs to.

Open a file

Install it into an environment (pip install visidata, or pipx install visidata to keep it isolated) and run:

Shell
vd orders.csv

The screen shows a table, with the column names in the top row and a status line at the bottom. VisiData detects the format from the extension and the delimiter from the contents. A CSV with a semicolon or a tab opens correctly, and vd -f tsv file.txt forces a format when the extension is wrong. Move with the arrow keys or h j k l, jump to the top with gg and the bottom with G, and quit the current sheet with q. Ctrl-H opens the help, and Space is a command prompt where you can type a command name.

Everything in VisiData is a sheet. The data is one sheet, a frequency table is another, a summary is another, and q closes the one you are on and returns to the previous. That stack model is the whole interface: you dig into the data and back out.

Sort, find and select

The keys to learn on day one are few. [ sorts the current column ascending and ] descending, and the sort applies to the whole sheet. / searches the current column for a regex, forward, and n jumps to the next match. | selects every row where the current column matches a regex, \ (backslash) unselects those matches and , selects all rows with the same value as the current cell, which is the fastest way to "show me all the rows like this one". Shift-S shows the stack of sheets and Shift-C the list of columns, where you can rename, hide or retype them.

Selection is the bridge to everything else. Select the rows you care about, then press " to open a new sheet that holds only the selected rows, and save that sheet, or run a frequency table on it. Rows you select stay selected in the original, so you can clear them (gu) and try another condition.

Frequency tables and summaries

The two commands that make VisiData worth installing are these. Shift-F on a column opens a frequency table for it: each distinct value, how many rows have it and a percentage. On the orders file with 200,000 rows, the table for status shows five lines, shipped, paid, refunded, pending and failed, each about 20 percent, which matches the counts I get from qsv frequency and from a SQL GROUP BY on the same file. A column that should have five values and has forty shows up immediately, and so does one dominated by a single value, which usually means a default or an empty string.

From the frequency table, you can select one or more values and press Enter to jump to the underlying rows matching them. Pressing q returns. That round trip, from "what values exist" to "show me the rows with this value", is the loop of data exploration, and in VisiData it takes two keystrokes.

Shift-I opens a summary sheet of the whole table: for each column, the type VisiData assigned, the number of rows, the number of nulls, the number of distinct values, and the min, max, mean and median. It is a one-screen profile, with the same purpose as qsv stats (qsv for fast CSV statistics), and interactive.

Types: text unless you say so

VisiData treats every column of a CSV as text until you change it. That is the safe default for the data this site is concerned with, and it is the opposite of a spreadsheet's. A zip code 02134 stays 02134, a 19-digit identifier stays whole and a date stays a string. The batch run below shows it: after converting a six-line CSV to JSON, every value, including the numeric ones, was written as a string:

Shell
vd -b small.csv -o small.json
head -c 200 small.json
Text
[
{"order_id": "1", "customer_id": "22313", "zip": "99154", "amount": "103.1", "created_at": "2025-11-22 13:08:24", ...

If you want to sum a column, set its type: # makes it an integer, % a float, @ a date, $ currency and ~ a string. Press the key with the cursor on the column, and the column's header gets a type marker. Aggregations (+ to add an aggregator, then the statistic) work on typed columns. The cost of the careful default is one extra keystroke; the benefit is that you never discover after saving that a column was rewritten.

Batch mode for scripts

VisiData has a batch mode that runs without the screen. -b is batch, -o names an output file and the extension decides the format, so converting between formats is one command:

Shell
vd -b orders.csv -o orders.json
vd -b orders.csv -o orders.xlsx
vd -b data.xlsx -o data.csv

The first command above, which I ran on a six-line sample, produced the JSON shown earlier. That makes VisiData a decent converter, though Miller or DuckDB are more flexible for scripted conversions (Miller for records and pipelines). VisiData also records the commands you run into a command log that can be saved and replayed (vd -p replay.vdj), which is how you turn an interactive session into a repeatable one. Check that against the documentation for your version before building a job on it, since I did not run a replay here.

Where it fits in a workflow

My use of it is narrow. Open the file, press Shift-I to see the columns and their types, Shift-F on the two or three categorical columns to see the values, sort the numeric ones both ways to see the extremes, search for the odd values (/ for blanks, NULL, N/A, -) and select suspicious rows to look at them in context. Five minutes of that shows the dialect problems, the outliers, the shifted rows and the placeholders, and tells me what to check in the loader.

Then I leave. The things I found go into a query or a script that someone can rerun, because a series of keystrokes in a terminal is not a record of what was done. VisiData helps with that to a degree through its command log, and a SQL file in version control is easier to review.

When a spreadsheet or SQL fits better

A spreadsheet is better when the audience is a person who wants to edit cells, and not look at them. VisiData can edit a cell (e), and its model is a viewer first. SQL is better for any question with joins, groups of several columns or window functions, and for any result you want to repeat (querying a CSV file with DuckDB instead of loading it). On a file of several gigabytes, VisiData loads rows into memory, so a large file wants a streaming tool for the first cut (working with CSV files in the shell).

If the terminal is a barrier for your team, this is not the tool, and a notebook or a spreadsheet import with typed columns does the same exploring with a mouse (opening CSV files in Excel and Sheets without damage). For everyone else, the documentation at visidata.org covers every command, the project repository tracks versions, and the built-in help (Ctrl-H) is faster than either.

Also inTooling
Share

Keep reading

Get new articles by email

We send one email when a new article is published. No spam.

Unsubscribe in one click. No spam, ever.