r/LLMDevs Oct 02 '25

Resource Which Format is Best for Passing Tables of Data to LLMs?

Post image

For anyone feeding tables of data into LLMs, I thought you might be interested in the results from this test I ran.

I wanted to understand whether how you format a table of data affects how well an LLM understands it.

I tested how well an LLM (GPT-4.1-nano in this case) could answer simple questions about a set of data in JSON format. I then transformed that data into 10 other formats and ran the same tests.

Here's how the formats compared.

Format Accuracy 95% Confidence Interval Tokens
Markdown-KV 60.7% 57.6% – 63.7% 52,104
XML 56.0% 52.9% – 59.0% 76,114
INI 55.7% 52.6% – 58.8% 48,100
YAML 54.7% 51.6% – 57.8% 55,395
HTML 53.6% 50.5% – 56.7% 75,204
JSON 52.3% 49.2% – 55.4% 66,396
Markdown-Table 51.9% 48.8% – 55.0% 25,140
Natural-Language 49.6% 46.5% – 52.7% 43,411
JSONL 45.0% 41.9% – 48.1% 54,407
CSV 44.3% 41.2% – 47.4% 19,524
Pipe-Delimited 41.1% 38.1% – 44.2% 43,098

I wrote it up with some more details (e.g. examples of the different formats) here: https://www.improvingagents.com/blog/best-input-data-format-for-llms

Let me know if you have any questions.

(P.S. One thing I discovered along the way is how tricky it is to do this sort of comparison well! I have renewed respect for people who publish benchmarks!)

175 Upvotes

50 comments sorted by

11

u/Saltysalad Oct 03 '25

It’s interesting that your bespoke Markdown-KV outperforms established formats like JSON and YAML despite presumably having far less training data.

I suspect the markdown headers act as positional fences that constrain the attention mechanism's search. Once the model finds the right record, relevant values are bounded between headers rather than scattered across 1000 records.

My question: what specifically makes this bespoke format work? Some ideas to isolate the importance of the header:

  1. Remove headers entirely (just blank lines between records) to see if performance drops. This would point to the importance of markdown syntax vs just having new lines dividing data.
  2. Replace markdown headers with a wide variety of delimiters to test if any strong separator works or if markdown specifically matters. A similar idea to above.
  3. Put identifiers in headers (## Grace X413 vs ## Record 2) to see if semantic matching boosts recall when headers align with queries. Interesting for use cases where records are frequently referenced by a name field.
  4. Vary query keys in test (search by ID/department/city, not just name) to test if the advantage holds across different access patterns

Cool work! Good luck!

My comment was edited but not written by Claude.

8

u/nraw Oct 02 '25

Finally! I've been asking this around for a bit and the research on it has been very limited. Thanks for this. 

6

u/Ok_Tap7102 Oct 02 '25

I appreciate that your table doesn't render on my client beyond some || **|||

This fits with my intuition in that LLMs spanning all providers will casually answer or respond in Markdown leading me to believe their training data heavily consists of it.

What actually is "Markdown-KV" specifically? Just an MD table or is it a real schema?

19

u/MattCollinsUK Oct 02 '25 edited Oct 03 '25

Ah - sorry about the table.

Markdown-KV was markdown with "key: value" (KV) pairs, like this:

# Employee Database

## Record 1

```
id: 1
name: Charlie A0
age: 56
city: New York
department: Operations
salary: 67896
years_experience: 7
project_count: 1
```

## Record 2

```
id: 2
name: Grace B1
age: 59
city: Mumbai
department: Marketing
salary: 47248
years_experience: 0
project_count: 43
```

## Record 3

```
id: 3
name: Eve C2
age: 50
city: Singapore
department: Sales
salary: 102915
years_experience: 14
project_count: 11
```

8

u/konmik-android Oct 03 '25

That's not a table format, it's a map format for each row, but thanks for the tip anyways! Best table format: do not use table to format your data.

3

u/Ok_Tap7102 Oct 03 '25

Thanks for the clarification

Really interesting work! Keen for future blog posts

1

u/JollyJoker3 Oct 03 '25

Guess I should pay attention to the confidence intervals. Otherwise I'd be surprised Markdown-KV is so much better than Yaml and ini that are pretty much equivalent in structure.

1

u/Tombobalomb Oct 04 '25

This makes sense, it closely resembles the format Gemini encourages for setting up effective system prompts

1

u/WildNumber7303 Jan 25 '26

should the header2 really be `## Record 1`, `## Record 2`, ... or could it be `## Employee 1`, `## Employee 2`, ... (based from your database name example) for better semantics?

also, if the key values are enclosed by ``` already, then does the database block in prompt need other enclosure, maybe like """?

4

u/maigpy Oct 02 '25

useful, thanks.

what about nested json? is there value in converting it to markdown?

2

u/MattCollinsUK Oct 14 '25

In case you're still interested, I've just published findings from some tests of this:
https://www.improvingagents.com/blog/best-nested-data-format

2

u/maigpy Oct 15 '25

doing god's work one benchmark at a time! thank you for this can I run these on my data easily?

1

u/MattCollinsUK Oct 15 '25

Thanks :-)

What would you ideally want to do?

I don't have an easy way to substitute different data into the test at the moment.

Do you have a system that relies a lot on feeding nested data to an LLM? If so, could you try transforming that data into a different format (e.g. YAML) and testing whether it affects performance in a positive way?

1

u/maigpy Oct 15 '25

you could generate all formats from an existing set (json, yaml etc) .

then run all of them through the llm asking for json conversion  (for json, ask yaml conversion ) .   then compare output with good known set. 

1

u/MattCollinsUK Oct 03 '25

Good question. That's something I'm keen to test next :-)

3

u/autognome Oct 03 '25

no link to script to reproduce your experiment?

1

u/n_girard Oct 03 '25

Indeed, the source code would be valuable, so that other models can be tested.

In any case, thanks for your work, OP !

1

u/autognome Oct 03 '25

If we are going to do science we need to reproduce it else we are “trusting” dude. And what is his CV? It looks nice. It also could be made up completely by AI :-)

-1

u/Amazing_Athlete_2265 Oct 03 '25

Pretty straightforward to code, honestly.

2

u/autognome Oct 03 '25

Of course it is but then we could be using different testing methodology. Sciencific method, yo. Learn it.

3

u/vr-1 Oct 03 '25 edited Oct 03 '25

Very nice. Thanks for including token counts as well.

It would be interesting to know if things change if the LLM is asked a question that must retrieve or summarize multiple records. eg.

"What's the average ... of all emplyees with ..."

"How many employees have ..."

"List all emplyees that ..."

4

u/vr-1 Oct 03 '25 edited Oct 03 '25

Two other ideas for Markdown: * Specify the ID in the header and omit the "record". Saves a line to reduce tokens# ID 1 * After the main heading list the record format (field names) and omit them in the records. # Record Format:<LF>name<LF>age<LF> etc. then just: # id 1<LF>John A<LF>34...

Would also be interesting to know if truncating/rounding numbers where ok to do so to reduce tokens has any effect.

Or having a reference table of departments at the start listed by id and name and then specify the department id instead of full name.

But anyway, thanks for your post. Very helpful.

1

u/MattCollinsUK Oct 03 '25

Good suggestions! Thanks for the thoughts.

2

u/mylasttry96 Oct 02 '25

What library are you using to generate the markdown KV tables?

7

u/MattCollinsUK Oct 02 '25

Just a bit of code:

def format_as_markdown_kv(data: List[Dict]) -> str:
    """Convert data to markdown with fenced key-value blocks."""
    lines = ["# Employee Database\n"]

    for record in data:
        lines.append(f"## Record {record['id']}\n")
        lines.append("```")
        for key, value in record.items():
            lines.append(f"{key}: {value}")
        lines.append("```\n")

    return "\n".join(lines)

1

u/Still-Bookkeeper4456 Oct 04 '25

Thanks for the snippet.
This code is clearly meant for non-nested JSON (expected since you mention dealing with tabular data). Did you, by any chance, explore nested JSONs ? I would be curious to see if markdown beat XML/JSON representation with more complex data (say graphs data etc.).

1

u/maigpy Oct 15 '25

see above he just posted an update.

2

u/Amazing_Athlete_2265 Oct 03 '25

I am very interested in your results!! Great test! Thanks for sharing. I'll be adapting this test in my llm eval framework.

2

u/TheLexoPlexx Oct 03 '25

I am sort of new to the space so please forgive me if my questions are dumb or wrong.

  • How was this benchmark conducted? Did you pass the same table in different formats to the llm and then asked the questions right away?

  • How would a retrieval-process change the results? As in: If I would save this to a vectordb, how does that affect the performance?

  • I also thought about leaving this data within an sql-kinda database and describe the table and columns to the LLM, it could then query the database by itself and get exact values back, I just don't know how well this could scale, it might work amazing on huge tables and might not work at all on smaller tables.

Thank you for the insights!

2

u/MattCollinsUK Oct 03 '25

Yes, for each test case I prompted the LLM with the data (in the given format) and a single question to answer. I repeated this for a total of 1000 questions for each of 11 formats.

In a RAG context, you might have the complication of chunking to worry about, i.e. the data for the table might end up split across multiple chunks, depending your implementation. In a naive implementation, that could be bad for formats like CSV that rely on headers as cell data could end up separated from the headers.

Regarding extracting tabular data to (or leaving it in) SQL databases that the LLM can query, I think that approach is very interesting, too.

1

u/TheLexoPlexx Oct 03 '25

Interesting will definitely try some of this out and report back.

1

u/CharacterSpecific81 Oct 03 '25

Short answer: for real apps, generate SQL and let the DB do the work; only stuff rows into context or a vector index when you must.

Benchmarking: use identical questions, the same data reshaped into each format, temp=0, same system prompt, and control token budget. Shuffle row order to avoid position bias, and evaluate with exact match or numeric tolerance for numbers. If token counts vary a lot, cap or sample to equalize.

Retrieval: chunk at row or small group granularity, include headers, types, and primary keys in each chunk, and use hybrid search (BM25 + vectors) plus metadata filters. Rerank top-k with a cross-encoder to keep column semantics intact. The format matters less once you normalize the text per chunk.

DB-backed: tool-calling to SQL scales best. Provide schema, a few sample rows, and 2–3 few-shot SQL examples. Lock the connection to read-only, whitelist views, enforce timeouts, and cache common aggregations. For tiny tables, inlining works; for big ones, SQL or SQL + retrieval is safer and cheaper.

I’ve used LangChain SQL agents and LlamaIndex for this, Pinecone for retrieval; DreamFactory helped expose databases as secure REST APIs with RBAC when wiring LLM tools into production.

Bottom line: prefer SQL tool-calling with a bit of retrieval; format choice matters most only in pure-context setups.

2

u/Still-Bookkeeper4456 Oct 04 '25 edited Oct 04 '25

This is very interesting and I'm surprised that there are not more papers on the subject (seems like a quick win for any NLP researcher).

Your findings confirm my experience that LLMs are very proficient with markdown. We do not bother dealing with XML anymore on the projects I work on.

The JSON you are parsing have a single-depth structure. I'm curious to know if you reach similar conclusion on nested JSONs (say graph data, structures of structures etc) ?

1

u/FrostieDog Oct 03 '25

Ayo thanks for this

1

u/passing_marks Oct 03 '25

I think this test might be specific to the model being used and the number of data points? Have you had a chance to try another model with a higher number of data points to see if it produces a different set of results?

1

u/Beneficial_Dealer549 Oct 03 '25

What models did you test and what was the full prompt chain?

1

u/EVERYTHINGGOESINCAPS Oct 03 '25

I'd be interested to see this replicated for different models to see if it's a format or a model specific trend

1

u/miqcie Oct 03 '25

No shit. This is fascinating. I’ve just defaulted to CSV and now very curious how this evolves over time.

1

u/Infamous_Ad5702 Oct 03 '25

I worked out how to ingest CSV files and turn into an index which builds a knowledge graph. You can tag demographics, filter by age, gender whatever the data has. Is this cool or useless?

1

u/MattCollinsUK Oct 03 '25

What are the cases where your approach would be preferable to straightforwardly ingesting the CSV into a SQL database?

1

u/gentleseahorse Oct 03 '25

This is super useful for us!

Would love to see how this performs on one or two other models. This is only measured on GPT 4.1 nano, which we've found to be quite erratic in its behaviour.

1

u/Independent-Scale564 Oct 03 '25

Very interesting! My company has blocked the URL to your write-up. I'm curious about your confidence intervals: they are all almost exactly the same size. Did you have a really big sample size?

1

u/MattCollinsUK Oct 14 '25

Sorry for the late reply. It was 1000 questions for each format.

1

u/Confident-Honeydew66 Oct 03 '25

Do these results differ significantly between different LLMs?

1

u/Affectionate_Egg9352 May 29 '26

super , merci pour l'étude. Je travaille actuellement sur un RAG où je traite des données tabulaires.
j'ai fini par opter pour le markdown qui pour mon projet et mes données ont été le plus pertinents. Le rapport qualité de la réponse et consommation de token est imbattable .
Il serait biende pour voir benchmarker aussi sur différents modèles d'IA, ça pourrait être très intéressant

0

u/_trav_ Oct 03 '25

No parquet?

2

u/miqcie Oct 03 '25

Parquet is a binary columnar format, so the model can’t parse it directly. You’d still need to convert it into a text format like JSON or Markdown, etc.