Skip to content

The Data Scientist

Chart of Accounts

Your Chart of Accounts Is a Data Model: How to Design Financial Data SMEs Can Actually Analyze

The request sounded simple. “Can you show us margin by product line?”

The analyst opened the general ledger export and found 214 accounts. The biggest expense line was called “Misc Expense 2.” Product lines appeared nowhere, except occasionally inside account names like “Travel Sydney Launch (old).” Three days later, the answer was a spreadsheet full of assumptions and a footnote nobody read.

The data wasn’t dirty. It was designed badly. And the design document behind it has a name every accountant knows and most data people ignore: the chart of accounts.

A chart of accounts is the list of every account a business uses to classify transactions in its general ledger. For a data team, it is something more specific: the schema of the company’s financial data. Get the schema right and analysis becomes a query. Get it wrong and every question becomes a project.

What Is a Chart of Accounts? A Data Scientist’s Definition

In accounting terms, it groups every account into assets, liabilities, equity, revenue and expenses, and every journal entry posts to at least two of them.

In data terms, it is a controlled vocabulary with a hierarchy. Every financial event in the business must be labeled with one of its values. That makes it the single most important dimension in your accounting data, and the one you usually inherit rather than design.

Which is exactly why so many financial data analysis projects stall before they start.

Chart of Accounts Smells That Break Financial Data Analysis

Developers talk about code smells: patterns that compile fine but signal trouble ahead. Account structures have their own.

The Miscellaneous Sink

Every ledger has a “Miscellaneous” or “Other” account. The smell is when it becomes one of the largest lines in the business. At that point it isn’t a category, it’s a landfill, and anything inside it is invisible to analysis.

Dimensions Hiding in Account Names

“Travel, Sydney, Project Falcon” is three facts crammed into one label. Now you can’t total travel across projects or total Project Falcon across expense types without string parsing and prayer. Location, project and department are dimensions. They don’t belong in the account.

Numbering Without a Grammar

If account 6400 is rent and 6401 is a software subscription added five years later, the numbers carry no meaning. Good chart of accounts numbering is a grammar: the first digit tells you the statement, the next tells you the group. Break the grammar and you lose the ability to roll up with a simple range.

Accounts That Quietly Change Meaning

An account called “Marketing” that held only advertising until 2024, then absorbed agency fees and events, will produce a beautiful trend line that means nothing. Time series only work when the definition holds still.

One-Off Accounts for One-Off Events

A new account for every trade show, every office move, every grant. Five years later you have hundreds of accounts with three transactions each. Cardinality explodes, reports sprawl, and nobody remembers what “Event Costs 2023B” was.

Designing the Chart of Accounts Like a Schema

Chart of Accounts Numbering as a Hierarchy

Treat the account number as a hierarchical key. A common four-digit pattern looks like this:

RangeStatementGroup
1000 to 1999Balance sheetAssets
2000 to 2999Balance sheetLiabilities
3000 to 3999Balance sheetEquity
4000 to 4999Income statementRevenue
5000 to 5999Income statementCost of sales
6000 to 6999Income statementOperating expenses

Inside each range, reserve blocks for groups (6100s for people costs, 6200s for premises) and leave gaps for growth. The payoff is that account // 1000 gives you the statement line, and account // 100 gives you the group. Roll-ups become arithmetic.

Accounts Hold “What,” Dimensions Hold “Where” and “Who”

This is the single rule that fixes most smells. The account answers what kind of money moved: rent, salaries, product sales. Everything else, such as which branch, which product line, which project or which client, belongs in separate dimensions, usually implemented as cost centers or tags on each transaction line.

The difference shows up immediately in the numbers. One “Travel” account tagged with a project dimension replaces dozens of “Travel, Project X” accounts, and every project can now be analyzed across every expense type. Modern chart of accounts software lets you keep the account list lean and attach cost centers to journal lines as they are posted, so the dimensions exist in the data from day one instead of being reverse engineered later.

A Star Schema for the General Ledger

If you are loading accounting data into a warehouse, dimensional modeling fits the general ledger naturally. Each journal line becomes a row in a fact table. Accounts, cost centers and dates become dimension tables around it:

  • fact_gl_entry: entry_id, posting_date, account_id, cost_center_id, amount
  • dim_account: account_id, account_name, statement, line, group
  • dim_cost_center: cost_center_id, name, type (branch, product line, project)
  • dim_date: the usual calendar and fiscal period attributes

With that in place, a quarterly profit and loss by cost center is one query:

SELECT a.line,

       cc.name        AS cost_center,

       SUM(f.amount)  AS total

FROM fact_gl_entry AS f

JOIN dim_account     AS a  ON a.account_id      = f.account_id

JOIN dim_cost_center AS cc ON cc.cost_center_id = f.cost_center_id

WHERE f.posting_date BETWEEN ‘2026-07-01’ AND ‘2026-09-30’

GROUP BY a.line, cc.name

ORDER BY a.line, cc.name;

No string parsing. No lookup spreadsheet. No footnotes.

From Ledger to Insight: A Short Python Example

You don’t need a warehouse to see the benefit. Here is a small general ledger extract in pandas, using the numbering grammar above to build a mini profit and loss by cost center:

import pandas as pd

gl = pd.DataFrame({

    “account”:     [4010, 4020, 5010, 5020, 6110, 6210, 4010, 5010],

    “cost_center”: [“Retail”, “Online”, “Retail”, “Online”,

                    “Retail”, “Online”, “Online”, “Online”],

    “amount”:      [52000, 31000, -24000, -15500, -6200, -4800, 9000, -4100],

})

bands = {4: “Revenue”, 5: “Cost of sales”, 6: “Operating expenses”}

gl[“line”] = (gl[“account”] // 1000).map(bands)

pnl = gl.pivot_table(index=”line”, columns=”cost_center”,

                     values=”amount”, aggfunc=”sum”, fill_value=0)

pnl.loc[“Gross profit”] = pnl.loc[“Revenue”] + pnl.loc[“Cost of sales”]

print(pnl)

The output:

cost_center         Online  Retail

line

Cost of sales       -19600  -24000

Operating expenses   -4800   -6200

Revenue              40000   52000

Gross profit         20400   28000

Online runs at roughly 51% gross margin and Retail at about 54%. That took one integer division and one pivot, because the account structure did the hard work before the data ever reached Python.

Try the same exercise on a ledger with a miscellaneous sink and project names buried in account labels. You will spend the afternoon writing regular expressions instead of answering the question.

Chart of Accounts Best Practices for Small Businesses

If you are figuring out how to set up a chart of accounts for a small business, or cleaning up an inherited one, a few rules go a long way:

  • Keep it as small as your reporting needs allow. Most small businesses need far fewer accounts than they have; detail belongs in dimensions
  • Define a numbering grammar before adding a single account, and leave gaps in every range
  • Name accounts for what they are, never for where, who or when
  • Set a materiality rule for miscellaneous accounts and review them every month
  • Keep a change log: when an account is added, merged or redefined, record the date and the reason
  • Restructure at a period boundary, ideally a new fiscal year, and map old accounts to new ones so history stays comparable

That last point matters to anyone doing financial analytics over time. A mapping table between old and new accounts is the difference between a clean five-year trend and a chart with a cliff in the middle.

Key Takeaways

  1. A chart of accounts is the schema of a company’s financial data, and schema quality decides analysis quality.
  2. Miscellaneous sinks, dimensions hidden in names and meaningless numbering are the most common design smells.
  3. Accounts should describe what happened; cost centers and other dimensions describe where and for whom.
  4. A numbering grammar turns roll-ups into simple integer arithmetic or range queries.
  5. Modeling the general ledger as a star schema makes profit and loss by any dimension a single query.
  6. Restructure at period boundaries and keep a mapping table so historical comparisons survive the change.

Margin by Product Line, Take Two

Back to that original request.

With a lean account list, a numbering grammar and product line as a cost center dimension, “margin by product line” is no longer a three-day investigation. It’s a GROUP BY.

The analyst still gets the question. They just get to answer it before lunch.

Frequently Asked Questions

What is a chart of accounts?

A chart of accounts is the complete list of accounts a business uses to record transactions in its general ledger, organized into assets, liabilities, equity, revenue and expenses. In data terms, it is the core classification schema for all financial data.

How many accounts should a chart of accounts have?

As few as your reporting needs allow. There is no fixed number; the right size is the smallest set of accounts that produces the financial statements and management reports you need, with extra detail captured through dimensions such as cost centers.

What is the difference between cost centers and accounts?

Accounts classify the type of transaction, such as rent or product sales. Cost centers classify where or for whom it happened, such as a branch, department, project or product line. Keeping them separate lets you analyze any account by any cost center.

How do you analyze accounting data?

Export journal lines from the general ledger with their account and dimension codes, map accounts to statement lines using the numbering hierarchy, then aggregate by period and dimension in SQL or Python. The cleaner the account structure, the less transformation you need.

About the Author

Omar El Bahr is a Senior Digital Growth Specialist at Enerpize, where he leads SEO, content strategy, and organic growth across international markets. He is a Forbes Communications Council contributor and has written for Entrepreneur on business communication and digital strategy.

Free Starter Course • The Data Scientist

Building Autonomous AI & Data Agents with Python

Hands-on engineering course with ReAct agents, MCP tool integrations, and LangChain/CrewAI production patterns.