From Dirty Customer Data to AI Analysis with Python on Raspberry Pi
From Dirty Customer Data to AI Analysis with Python on Raspberry Pi
In our last example, Python and Pandas turned messy customer records into clean CSV and JSON files.
This time we'll take the idea one step further.
We'll start with another dirty customer dataset, clean it with Pandas, save the results for both people and software, and then send the cleaned records to an AI model for an evaluation of the data.
The pipeline becomes:
dirty CSV → Pandas → clean data → CSV + JSON → AI API → evaluation
The AI isn't being asked to fix our broken customer data.
That's deliberate.
Python handles the predictable data-cleaning work first. Only then do we give the clean records to a model and ask it to evaluate what it sees.
Another Messy Dataset
Create
customers_ai.csv:
first_name,last_name,city,age
Maria ,LOPEZ,DALLAS,42
james,wilson,austin,36
Robert,THOMAS,HOUSTON,51
Linda,Martin, dallas ,28
james,wilson,austin,36
Patricia,Anderson,,45
Michael,Taylor,AUSTIN,N/A
susan,Moore,Houston,33
Again, the problems are obvious once we look at the file.
Some names and cities have inconsistent capitalization. There are extra spaces. One customer has no city. Another has no usable age. And James Wilson appears twice.
Our program shouldn't simply hope that everything is okay.
It should clean the records first.
Cleaning the Customer Data
We'll use Pandas again:
import pandas as pd
# INPUT
df = pd.read_csv("customers_ai.csv")
# PROCESSING
# Remove extra spaces and normalize capitalization
df["first_name"] = (
df["first_name"]
.str.strip()
.str.title()
)
df["last_name"] = (
df["last_name"]
.str.strip()
.str.title()
)
df["city"] = (
df["city"]
.str.strip()
.str.title()
)
# Convert age to numeric values
df["age"] = pd.to_numeric(
df["age"],
errors="coerce"
)
# Make missing cities explicit
df["city"] = df["city"].fillna("Unknown")
# Remove duplicate customer records
df = df.drop_duplicates()
# Reset row numbers after removing duplicates
df = df.reset_index(drop=True)
# OUTPUT
df.to_csv(
"customers_ai_clean.csv",
index=False
)
df.to_json(
"customers_ai_clean.json",
orient="records",
indent=2
)
print(df)
print("\nCreated customers_ai_clean.csv")
print("Created customers_ai_clean.json")
Run it:
python3 clean_customers_ai.py
Once again we have the same basic pattern:
input → processing → output
Pandas reads the file, cleans its contents according to our rules, removes the duplicate and writes clean versions.
But now we have another potential output.
An AI model.
Giving the Data to an AI Model
For this experiment we'll use OpenRouter's Free Models Router. Its
documentation says
openrouter/free
automatically selects from available free models and describes the router as
suitable for experimentation, learning, and low-volume use cases.
That distinction matters:
openrouter/free
isn't one fixed model.
The router chooses an available free model for the request.
You'll need an API key. Rather than putting that key directly inside the Python program, set it as an environment variable:
export OPENROUTER_API_KEY="your-key-here"
We'll also need the Requests library:
python3 -m pip install requests
Now create
analyze_customers.py:
import json
import os
import requests
API_KEY = os.environ["OPENROUTER_API_KEY"]
with open("customers_ai_clean.json", "r") as file:
customer_data = json.load(file)
prompt = f"""
You are evaluating a small set of cleaned customer records.
Briefly describe the customer data in plain English.
Identify any noticeable patterns in the cities and ages.
Point out missing or unknown information that could limit
the analysis.
Do not invent customer information or make claims that are
not supported by the supplied data.
Customer records:
{json.dumps(customer_data, indent=2)}
"""
response = requests.post(
"https://openrouter.ai/api/v1/responses",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"model": "openrouter/free",
"input": prompt,
},
timeout=60,
)
response.raise_for_status()
result = response.json()
message = next(
item
for item in reversed(result["output"])
if item["type"] == "message"
)
analysis = next(
part["text"]
for part in message["content"]
if part["type"] == "output_text"
)
evaluation = {
"model": result["model"],
"analysis": analysis
}
with open("customer_ai_evaluation.json", "w") as file:
json.dump(
evaluation,
file,
indent=2
)
print(analysis)
print("\nModel used:", result["model"])
print("Created customer_ai_evaluation.json")
Run it:
python3 analyze_customers.py
Example AI Evaluation
A response might look something like this:
The customer records are distributed across Dallas, Austin, and Houston, with one customer whose city is unknown. The available ages range from 28 to 51. Dallas and Austin each appear more than once in the dataset, while one customer's age is missing. Because this is a small dataset with incomplete information, these observations should be treated as a basic summary rather than a broad customer trend.
The exact response will vary depending on the model selected and how it
interprets the data. What's important is that the evaluation is no longer just
text scrolling past in the terminal. Our Python program saves it in
customer_ai_evaluation.json, giving us a third artifact from the pipeline.
This uses the same Responses API pattern documented by OpenRouter: send
model: "openrouter/free"
and an
input
to the Responses endpoint, then retrieve the returned message and the model
that was actually selected.
But we've added one small piece of our own.
We're saving the model's evaluation.
Python Cleans. AI Evaluates.
There's an important architectural distinction hiding inside this little Raspberry Pi project.
We didn't throw dirty customer records at an AI model and ask it to sort everything out.
We gave deterministic work to normal software.
Pandas removes extra spaces. Pandas normalizes capitalization. Pandas converts ages into numeric data. Pandas removes the duplicate. Pandas writes the cleaned CSV and JSON files.
Only after that pipeline has produced predictable input do we invoke a model for the less deterministic part of the problem:
What does this customer dataset appear to tell us?
The model can describe patterns it notices while our prompt explicitly tells it not to invent information that isn't present in the records.
That division of labor is worth noticing.
Raw data enters. Pandas cleans. Code validates. AI evaluates.
One Pipeline, Three Artifacts
This time the Raspberry Pi produces three useful artifacts.
customers_ai_clean.csv
contains the cleaned customer records in a format convenient for Excel and
other tabular tools.
customers_ai_clean.json
contains those same cleaned records in a structured form intended for
software.
And
customer_ai_evaluation.json
preserves the AI evaluation itself.
That last file introduces something important.
The AI response doesn't have to disappear after scrolling across a terminal window. We can capture it as data and keep it alongside the information that produced it.
Our complete pipeline now looks like this:
dirty customer CSV → Python/Pandas → clean data → CSV for people + JSON for software → AI API → JSON evaluation
That's still a remarkably small project.
But we've moved beyond simply cleaning a CSV file.
We're building a pipeline in which traditional software handles predictable transformations, while an AI model is brought in for interpretation after the data has been prepared.
And because the resulting AI evaluation is saved as JSON, that output can become input to something else later.
That's where these little Raspberry Pi data pipelines start getting very interesting.
