DEV Community

mbadr3227-sys
mbadr3227-sys

Posted on

I Kept Rewriting the Same Five Lines of Pandas, So I Built a Terminal Tool Instead

Every time I opened a new dataset, I typed the same thing:

df.shape
df.head()
df.isna().sum()
df.describe()
df.corr()

Five lines, every single time, in a fresh notebook cell. It took maybe forty seconds. But it was forty seconds of typing something I already knew I wanted, before I could start on the part I actually cared about.

So I built DataPeek — a terminal program that does all of it from a menu. You point it at a CSV, press a number, and read the answer.

This was my portfolio project for Codecademy's CS 101: Introduction to Programming, and I want to write about the parts that surprised me, not just what the tool does.
The shape of the thing

The whole program is two files.

main.py runs the interface: it prints a banner, loads a file, shows a menu, and loops until you quit. data_tools.py holds the analysis — one function per menu option, each taking a DataFrame and printing a report.

The piece that ties them together is a dictionary:
ACTIONS = {
"1": dt.show_overview,
"2": dt.show_head,
"3": dt.show_missing,
"4": dt.show_stats,
"5": dt.show_value_counts,
"6": dt.show_correlations,
"7": dt.filter_and_export,
}
My first version was a long if/elif chain. It worked, but every new feature meant editing the loop, and the loop kept growing. Storing the functions in a dictionary meant the loop stopped changing entirely:
action = ACTIONS.get(choice)
if action is None:
print("I don't know that option. Try a number from the menu, or 'q'.")
continue
action(df)
Adding a feature is now a function plus one line in the dictionary. That was the first moment the project taught me something I hadn't gone looking for: functions are values you can store, not just things you call.

Users type whatever they want

I had never written a program that asked for input before. Every prior exercise ran on data I controlled — a CSV I'd already inspected, a variable I'd set myself two lines earlier.

The first time I ran DataPeek, I broke it within thirty seconds. I typed python main.py at the prompt — the program was already running, and it was asking me for a file path, but my hands typed the command anyway out of habit. Then I pressed Enter on an empty prompt to see what would happen.

Both times, it held. It told me it needed a file path and asked again.

That only worked because I'd already spent most of my later commits writing guards instead of features. The number prompt became this:
def ask_int(prompt, default, low, high):
raw = input(prompt).strip()
if not raw:
return default
try:
value = int(raw)
except ValueError:
print(f"Not a number. Using {default}.")
return default
if not low <= value <= high:
print(f"Out of range. Using {default}.")
return default
return value
Eleven lines to read one number. It felt like overkill when I wrote it, and correct the first time the program survived something I hadn't planned for.

File loading got the same treatment — separate except blocks for a missing file, an empty file, a malformed CSV, and a permissions error, each with a message that says what to do next. pd.read_csv raises genuinely different exceptions for these, and catching them separately means the user gets "That doesn't look like a valid CSV" instead of a stack trace.

The general lesson: input validation isn't a finishing touch. It's most of the work of making a program usable by anyone but you.

Making a terminal readable

Terminal output has no styling. No bold, no colour, no layout. All you have is spaces and newlines, and it turns out that's enough if you're deliberate.

f-string alignment did most of the work:
print(f"{col[:24]:<25}{count:>10,}{pct:>9.1f}%")
Left-align the label, right-align the numbers, truncate anything too long, and the columns line up. Right-aligned numbers are easier to compare because the digits stack.

For missing values I added a crude bar:

bar = "#" * int(pct / 5)

One # per five percent. It's about as simple as a visualisation gets, and it still beats reading percentages down a column — you see the worst offenders without comparing anything.

The part that took longest wasn't the code

I want to be honest about the time breakdown, because the tutorials never are.

Writing the program took an afternoon. Getting it to run took considerably longer, and none of that time was spent on Python.

PowerShell refused to activate the virtual environment — Windows blocks script execution by default, and the fix is a single Set-ExecutionPolicy command that I only found by reading the error message properly instead of panicking at the red text. Then pip install pandas died partway through with [Errno 28] No space left on device. My drive had 130 MB free out of 75 GB. Clearing temporary files bought me a gigabyte, which was just enough.

Then Git wasn't installed. Then I ran git remote add pointing at a GitHub repository I hadn't created yet, and spent a minute confused by Repository not found before realising the repository genuinely did not exist.

None of these are interesting problems. All of them are real ones, and they're the actual content of "build a project on your own machine" — the step that tutorials compress into a single line that reads set up your environment.

Where the correlation code got interesting

df.corr() gives a full matrix. Every pair appears twice, and the diagonal is all 1.0. Useful to look at, annoying to rank.

To get a sorted list of distinct pairs, I walked only the upper triangle:

for i, col_a in enumerate(corr.columns):
for col_b in corr.columns[i + 1:]:
value = corr.loc[col_a, col_b]
if pd.notna(value):
pairs.append((col_a, col_b, value))

pairs.sort(key=lambda pair: abs(pair[2]), reverse=True)

Slicing from i + 1 skips the diagonal and everything below it. Sorting on abs() means a correlation of -0.8 ranks above +0.3, which is what you want — strength matters more than direction when you're scanning for what to look at next.

What I'd do next
ASCII histograms, so numeric distributions are visible not just summarised
Group-by summaries, since that's the next thing I always reach for
Saving a full report to a text file
What it actually taught me

I expected to learn pandas. I already knew pandas. What I learned was that the distance between code that works and code someone else can use is mostly made of error handling, clear messages, and structure that lets you add things without breaking what's there — plus a surprising amount of getting a machine to cooperate before any of it runs at all.

The code is on GitHub: github.com/mbadr3227-sys/datapeek

If you also keep typing the same five lines, feel free to steal it.

Content

Top comments (1)

Collapse
 
paw_dev6789 profile image
Paw

the "i typed the same five lines every time so i built a thing" story is basically how everything i've made started. did building the tool actually save you time yet, or was it mostly an excuse to stop retyping?