Arrow to CSV Converter
An Arrow to CSV converter turns an Arrow IPC or Feather v2 file into a spreadsheet. Four of Arrow's value types do not stringify into anything useful on their own, and getting them wrong changes your numbers rather than erroring, so this page is mostly about those four. The file is read in your browser and never uploaded.
Want to query it rather than export it? Open the app
Four types that do not stringify
Most Arrow columns convert to text without thinking. Utf8 is already text, Float64 and Bool print as themselves. Four do not, and each fails in its own way.
Int64 and Uint64 arrive as BigInt. A 64-bit integer does not fit in a JavaScript double, so Arrow hands back a BigInt. Passing that through Number() is the obvious move and it silently rounds anything past about nine quadrillion. Snowflake ids, some database primary keys and nanosecond timestamps all live up there, and the last three or four digits change without a word. They are written out in full decimal here, so nothing moves.
Decimal128 arrives as four 32-bit words. Arrow stores a decimal as a raw 128-bit two's complement integer plus a declared scale, and the JavaScript binding gives you a Uint32Array. Reading the first word gives a number that is wrong rather than imprecise, and the sign is in the top bit of the last word rather than anywhere obvious. The words are combined through BigInt, the two's complement sign is applied, and the decimal point is placed using the column's scale.
Timestamps are integers with a unit attached. A timestamp value is a count since the epoch, and whether that count is seconds, milliseconds, microseconds or nanoseconds is part of the column's type rather than the value. Left alone the cell reads 1730629920000000. The declared unit is used to convert, and the result is ISO 8601 text.
List, Struct and Map arrive as Arrow objects. Their toString is not JSON and is not much use. They are serialised as JSON so nothing is lost, with any BigInt inside stringified on the way so the serialisation does not throw.
A worked example
An Arrow file whose schema is:
event_id: Int64
amount: Decimal128(18, 2)
occurred: Timestamp[us, tz=UTC]
tags: List<Utf8>
region: Utf8
comes out as:
event_id,amount,occurred,tags,region
9007199254740993,1234.56,2024-11-03T09:12:00.000Z,"[""new"",""priority""]",EMEA
9007199254740994,-89.10,2024-11-03T09:14:22.000Z,"[""renewal""]",AMER
The event_id values are past 2 to the 53, which is exactly where a double stops being able to tell consecutive integers apart. Through Number() both rows would come back as the same value. In full decimal they stay distinct.
The negative amount is a two's complement value in the raw words, and it comes back as -89.10 with the scale of 2 applied, not as a very large positive number.
The microsecond timestamp is divided by the right factor for its unit before becoming ISO text. A converter that assumed milliseconds would put that event in the year 56,861.
The list column is JSON, quoted as a CSV field, with its internal quotes doubled the way RFC 4180 requires.
Each of these is named in the warnings with a count, so you know which columns were transformed and how.
Layouts, Feather versions and error messages
Both IPC layouts are accepted. The file layout has a footer with a batch index; the stream layout does not. Since both routinely carry the .arrow extension, refusing one of them would mean a confusing failure on a perfectly valid file. That is deliberately more forgiving than pyarrow.ipc.open_file, which takes only the file layout.
.feather and .ipc open too, because Feather v2 and Arrow IPC are the same format. Feather v1, from before that unification, is a genuinely different format and will not open, and the error says so and suggests re-exporting as Feather v2 or Parquet. That is much more useful than being told the bytes are invalid.
Arrow is binary, so the file has to be a file. There is no paste tab on this page, because pasted text cannot carry the bytes.
The summary reports the row count, the column count, how many record batches the file holds and the first few column types. The batch count is worth a glance: a file written by a streaming producer may have thousands of tiny batches, which is fine for reading and worth knowing if you were expecting one.
Everything arrives in the CSV as text, so nothing gets re-typed on the way out and a zero-padded string column keeps its zeros. If you want the data typed again on the other side, Parquet or Arrow will do that from the CSV, or the app will profile the columns for you.
Questions
Do 64-bit integers keep their precision?
Yes. Arrow's Int64 and Uint64 arrive in JavaScript as BigInt, and they are written out in full decimal rather than being passed through Number, which would silently round anything past nine quadrillion. Snowflake ids, some database primary keys and nanosecond timestamps all live in that range, and a naive conversion changes the last few digits without saying anything.
What happens to a Decimal column?
It is reassembled and rescaled correctly. Arrow stores a Decimal128 as a raw 128-bit two's complement integer plus a scale, which arrives as four 32-bit words. Reading only the first word, or converting through a double, gives a wrong number rather than a rounded one. The words are combined through BigInt, the sign is handled, and the decimal point is placed by the column's declared scale.
How are timestamps written?
As ISO 8601 text. A raw Arrow timestamp is an integer count since the epoch, and the unit is part of the column type: seconds, milliseconds, microseconds or nanoseconds. Left alone it looks like 1730629920000000, which is not a date to anyone. The column's declared unit is used to convert it, and a Date32 or Date64 column comes out as a plain date.
What about list and struct columns?
They become JSON text in the cell, and the count is reported. A CSV cell cannot nest, so the choice is JSON or dropping the column, and JSON keeps everything. Any BigInt inside a nested value is stringified on the way so the serialisation does not throw.
Does it read Feather files?
Feather v2, yes, because Feather v2 and Arrow IPC are the same format under two names. Feather v1, the original format from before that unification, is a different thing and will not open. If you have one, re-export it from pandas as Feather v2 or as Parquet. The error message says so rather than just calling the file invalid.
Does it accept both IPC layouts?
Yes. The reader takes the file layout, which has a footer, and the stream layout, which does not, so a .arrow, .feather or .ipc file opens regardless of which writer produced it. That is deliberately more forgiving than pyarrow.ipc.open_file, which accepts only the file layout.
Is my file uploaded?
No. The Arrow reader runs in your browser tab and the file's bytes never leave your machine. Nothing is stored between visits and there is no row cap beyond the memory your tab has. Since Arrow is a binary format it has to be a file rather than pasted text.
Related
Convert your Arrow file to CSV
No sign-up, no upload, no row cap. Full-precision integers, correct decimals, real dates, nested types kept as JSON.
Back to the converter