Synthetic Data in Fraud: Patterns, Stories, and Data Anomalies
The challenge of modeling fraud with synthetic data in a world driven by real patterns and unpredictable stories
In the timeless words of King Solomon — ancient, yet brilliantly sharp: “What has been is what will be, and what has been done is what will be done. There is nothing new under the sun.”
I believe that this philosophy is right. Not everything repeats exactly one-to-one, but events do recur — at least at the level of their semantic definitions. And if not, you can always climb the category. Patterns emerge, reframe, and reappear, which is what makes them powerful — and also what makes fraud detection so challenging.The world, in many ways, is patterned. It repeats itself. Whether in human behavior, nature, or even financial systems — there are rhythms, echoes, and recurrences. Understanding these patterns is what allows us to predict, to prepare, and to protect. This is the foundation of insurance.
And yet, there seems to be a contradiction between this worldview and how we act — especially in the world of fraud detection.It is true that there are patterns. But reality, especially when it comes to fraud, has a tendency to create new combinations of known elements — new stories built from familiar pieces. And it is precisely this combination that becomes the new thing.
So when we talk about building fraud models based on synthetic information — simulated data that mimics patterns of the past — we encounter a serious problem. These models may reproduce known behaviors, but they often lack the unexpected creativity that real fraud cases bring.When building a fraud model and pouring information into it to verify events against reality, what I’m mainly missing is the story. The human narrative. The core question: was there fraud or not?
Synthetic information shows its limits here. For a machine learning model to understand fraud, it first needs labeled data — a human being must tell the model what was fraud and what wasn’t. Only then can it begin to learn.Fraud doesn’t always follow the old playbook. Sometimes, it writes its own. And that’s the risk we face if we rely too heavily on synthetic data. The past is full of lessons — but the present still has the power to astonish.
Fraud is a broad concept. It’s not just a forged document or a false claim. It’s a story — a narrative that someone tries to push as truth. If, God forbid, you ever find yourself in an investigation, you’ll likely be asked to repeat your story again and again. Why? Because the investigators are looking for anomalies — inconsistencies that reveal a break from the truth.From this insight, we can extract something useful: it is possible to create synthetic information by generating stories that deliberately contain anomalies against aggregated, consistent information. This, of course, won’t cover everything. But it opens the door to producing synthetic data that reflects the logic of fraud — not just its surface.
Example: Synthetic Claims and Anomalies
We created 20 synthetic insurance claim records that simulate real-world conditions where anomalies may occur. Each record represents a road accident reported by a driver. We generated contextual data such as time, vehicle, reported visibility, actual weather conditions, and lighting data.
One such story:
Get Oren Atia’s stories in your inbox
Join Medium for free to get updates from this writer.
Claim ID: 83721
Driver: Daniel Cohen
Vehicle: Toyota Corolla
Incident Time: 03:45 a.m. on Highway 4
Claim Description: “There was heavy fog, I couldn’t see, and I swerved to avoid another vehicle.”
Estimated Damage: 18,000 ILS
Aggregated Data:
- No fog alert
- Clear visibility (85%) from road cameras
- Operational street lighting
- No other accidents reported at the time
Anomaly Detected: The driver described poor visibility and heavy fog, but real-time weather data showed clear skies and no visibility issues. This mismatch flags the claim as potentially fraudulent.
We repeated this process 20 times to simulate a dataset of claim reports. Here’s what we found:
- Around 30% of claims presented such anomalies.
- These anomalies were often tied to inconsistencies between reported fog and actual weather conditions.
Well, it’s impossible without sample code — here’s the Python code used to generate the data:
import pandas as pd
import random
import numpy as np
from datetime import datetime
# Set random seed for reproducibility
random.seed(42)
np.random.seed(42)
# Base data options
drivers = ['Daniel Cohen', 'Rina Levi', 'Omer Tal', 'Yael Shapira', 'Tomer Azulay']
vehicles = ['Toyota Corolla', 'Honda Civic', 'Mazda 3', 'Hyundai i30', 'Kia Picanto']
locations = [
'A1(M), 52.5606, -1.9641',
'M25, 51.2971, 0.5569',
'M6, 53.4545, -2.2671',
'A40, 51.5194, -0.1551',
'M4, 51.4700, -0.4760'
]
def generate_claim(location=None, estimated_damage=None):
# Create a list of synthetic narrative descriptions
fake_stories = [
"I exited the tunnel and suddenly noticed a figure on the road. I swerved to avoid it, lost control due to the wet surface, and crashed into the barrier. It was dark and visibility was extremely low.",
"A vehicle ahead stopped abruptly, and due to the fog I didn’t see it in time. I tried braking, but I lost control and hit the side rail.",
"I was driving slowly because of the poor lighting, and suddenly an animal crossed the road. I tried to avoid it and veered into the ditch.",
"Visibility was bad due to rain, and I couldn’t see the road markings clearly. I skidded and collided with another car.",
"The street lights were out and I didn’t notice the traffic island in time. My car hit it directly and the front bumper was damaged."
]
fake_story = random.choice(fake_stories)
driver = random.choice(drivers)
vehicle = random.choice(vehicles)
location = location if location else random.choice(locations)
claim_time = datetime(2025, 1, 12, random.randint(0, 5), random.randint(0, 59))
fog_reported = random.choice([True, False])
fog_actual = random.choices([True, False], weights=[0.2, 0.8])[0]
visibility = "poor" if fog_reported else "clear"
actual_visibility = "poor" if fog_actual else "clear"
lighting_operational = random.choices([True, False], weights=[0.9, 0.1])[0]
damage = estimated_damage if estimated_damage else round(random.uniform(1500, 7500), 2)
anomaly = fog_reported != fog_actual
return {
"Driver": driver,
"Vehicle": vehicle,
"Location": location,
"Claim Time": claim_time.strftime('%Y-%m-%d %H:%M'),
"Reported Visibility": visibility,
"Actual Visibility": actual_visibility,
"Fog Reported by Driver": fog_reported,
"Fog in Weather Data": fog_actual,
"Street Lighting Operational": lighting_operational,
"Estimated Damage (USD)": damage,
"Anomaly": anomaly,
"Claim Description": fake_story
}
# # Generate 20 synthetic claims with optional parameter overrides
declared_location = f"Random Road {random.randint(100, 999)}, {round(random.uniform(51.0, 55.0), 4)}, {round(random.uniform(-3.0, 1.0), 4)}"
# declared_damage = 15000
# Function to generate a random road location in England
def generate_random_road():
road_prefix = random.choice(['A', 'B', 'M'])
road_number = random.randint(1, 999)
lat = round(random.uniform(51.0, 55.0), 4)
lon = round(random.uniform(-3.0, 1.0), 4)
return f"{road_prefix}{road_number}, {lat}, {lon}"
# Generate 20 synthetic claims with optional parameter overrides
declared_location = generate_random_road()
claims_data = [generate_claim(location= generate_random_road()) for _ in range(20)]
df_claims = pd.DataFrame(claims_data)
display (df_claims)So what is the solution?
The solution I find appropriate to bring to the table is a hybrid solution. Synthetic information for fraud must be grounded in real cases, but enhanced by combining as much relevant data as possible. After all, a model, by design, tries to predict by linking — or approaching — a pattern it has already learned. Based on that principle, it’s possible to build synthetic fraud patterns. Are they always accurate? No. But they can serve a purpose — especially in cases where there isn’t enough real data to build a model. In those moments, this hybrid approach may be the only viable way to move forward.
Fraud evolves. Our models must evolve, too. Not just by remembering the past, but by staying alert to the present’s ability to surprise.








