Statology Sprint: Advanced Synthetic Data Generation with Faker

Statology Sprint: Advanced Synthetic Data Generation with Faker
Image by Editor

What are Statology Sprints?

Statology Sprints are carefully curated learning paths that distill complex concepts into practical tools you can apply immediately. We fuse foundational statistical material with expert insights to enhance your skills while skipping unnecessary filler. Each Sprint includes:

  • Core concepts explained clearly
  • Practical code examples you can adapt
  • Connections between related ideas
  • Links to deeper coverage when you need it

Creating realistic data for development and testing is a persistent challenge across the data science and software development landscape. Using actual user information often violates privacy regulations like GDPR and HIPAA, while manually creating synthetic data is time-consuming and error-prone. This Statology Sprint brings together our most valuable content on Faker, Python’s powerful synthetic data generation library, to help you create realistic, privacy-compliant test data for your projects.

Getting Started with Faker

Faker is a Python library designed to generate realistic, diverse data that protects privacy. It’s extremely useful for various purposes: populating development databases, building prototypes, creating demonstration environments, generating sample datasets, and enhancing testing frameworks.

To begin using Faker, you’ll first need to install it:

 
pip install Faker

Let’s start by creating our first fake data:

 
from faker import Faker

# Initialize the Faker generator
fake = Faker()

# Set seed for reproducibility
Faker.seed(12345)

# Generate sample data
print("Name:", fake.name())
print("Email:", fake.email())
print("Address:", fake.address())
print("Phone:", fake.phone_number())
print("Birthday:", fake.date_of_birth(minimum_age=18, maximum_age=90))

When you run this code, you’ll see output similar to this:

 
Name: Adam Bryan
Email: melissa34@example.net
Address: 64829 Michael Street Apt. 869
East Jamie, CA 25400
Phone: 350-875-0860x02924
Birthday: 1942-01-05

Each piece of data follows realistic patterns:

  • Email addresses combine names with domains in common formats
  • Addresses include realistic street names, cities, and postal codes
  • Phone numbers follow standard formatting with area codes and extensions
  • Birthdates are calculated to match the specified age range

Faker’s Provider Architecture

Faker uses a modular provider architecture, organizing data generation into specialized components. Each provider focuses on a specific type of data – from personal information and addresses to internet data and financial details. This approach makes Faker extensible and maintainable, allowing developers to use only the providers they need. The library comes with many built-in providers covering common use cases, but also supports custom providers for specialized requirements. Let’s explore what providers are available:

 
from faker import Faker
fake = Faker()

# List all providers
print("Faker Provider List:")
for provider in fake.providers:
    print(f"- {provider}")

This will reveal the full list of providers currently available: 

 
Faker Provider List:
- <faker.providers.user_agent.Provider object at 0x1106aefa0>
- <faker.providers.ssn.en_US.Provider object at 0x1106aeee0>
- <faker.providers.sbn.Provider object at 0x1106aef70>
- <faker.providers.python.Provider object at 0x1106aedf0>
- <faker.providers.profile.Provider object at 0x1106aee20>
- <faker.providers.phone_number.en_US.Provider object at 0x1106aed00>
- <faker.providers.person.en_US.Provider object at 0x1106aea90>
- <faker.providers.passport.en_US.Provider object at 0x1106ae970>
- <faker.providers.misc.en_US.Provider object at 0x1106ae730>
- <faker.providers.lorem.en_US.Provider object at 0x1106ae610>
- <faker.providers.job.en_US.Provider object at 0x1106ae6d0>
- <faker.providers.isbn.en_US.Provider object at 0x1106ae5b0>
- <faker.providers.internet.en_US.Provider object at 0x1106ae460>
- <faker.providers.geo.en_US.Provider object at 0x1106ae2e0>
- <faker.providers.file.Provider object at 0x1106ae310>
- <faker.providers.emoji.Provider object at 0x110691fd0>
- <faker.providers.date_time.en_US.Provider object at 0x110691f70>
- <faker.providers.currency.en_US.Provider object at 0x110691d90>
- <faker.providers.credit_card.en_US.Provider object at 0x110691ca0>
- <faker.providers.company.en_US.Provider object at 0x110691e80>
- <faker.providers.color.en_US.Provider object at 0x110691670>
- <faker.providers.barcode.en_US.Provider object at 0x1106915e0>
- <faker.providers.bank.en_GB.Provider object at 0x110683e80>
- <faker.providers.automotive.en_US.Provider object at 0x10ef05ee0>
- <faker.providers.address.en_US.Provider object at 0x11065dee0>

Each provider in this list represents a specialized data generator:

  • The address provider creates realistic street addresses and postal codes
  • The person provider generates names and personal information
  • The date_time provider handles dates and times with various formats
  • The credit_card provider generates valid-format credit card information

At present, there are total 25 providers available within the Faker library. Understanding these providers is essential for effective synthetic data generation, as each one offers specific methods tailored to different data needs.

➡️ Learn more about how to get started with Faker and the provider architecture

Working with the Profile Provider

Faker’s profile provider offers a convenient way to generate complete user profiles containing multiple related attributes in a single call. This is especially useful when you need coherent user data with multiple fields that work together realistically. Let’s see how to use this provider:

 
from faker import Faker

# Initialize Faker with a seed for reproducibility
fake = Faker()
Faker.seed(23456)

# Generate a complete user profile
profile = fake.profile()
print("Sample Profile:")
for key, value in profile.items():
    print(f"{key}: {value}")

When you run this code, you’ll get output similar to this:

 
Sample Profile:
job: Producer, television/film/video
company: Murray Group
ssn: 243-82-9822
residence: 60456 Robinson Field Suite 411
West Adam, OK 51089
current_location: (Decimal('-34.7828005'), Decimal('-149.796833'))
blood_group: A-
website: ['https://www.hughes.net/', 'http://ho-paul.com/', 'https://johnson-jones.com/']
username: douglas50
name: Vanessa Pena
sex: F
address: 1057 Susan Inlet Apt. 780
Moorestad, ND 96603
mail: abigailhayden@gmail.com
birthdate: 1913-07-19

The profile provider generates a rich set of interconnected user data in a single operation. Each profile includes:

  • Personal information (name, sex, birthdate, blood type)
  • Contact details (email, address, username)
  • Professional information (job title, company)
  • Location data (current coordinates, residence)
  • Online presence (website, username)
  • Identification (SSN)

You can also customize the profile by excluding certain fields or specifying particular requirements:

 
# Generate a profile with specific fields excluded
custom_profile = fake.profile(fields=['name', 'address', 'mail'], sex='F')
print("\nCustomized Profile (female, limited fields):")
for key, value in custom_profile.items():
    print(f"{key}: {value}")

The above customized profile should generate: 

 
Customized Profile (female, limited fields):
name: Wendy Johnson
address: 6986 Miller Row Suite 353
Fischerstad, NJ 54956
mail: williambrown@hotmail.com

This flexibility makes the profile provider ideal for quickly generating realistic test data for user-centric applications. While the default profile generation works well for many use cases, you might sometimes need more control over the statistical distributions of certain attributes.

➡️ Learn more about analyzing and enhancing Faker’s profile data distribution patterns

Understanding Data Distributions in Faker

When generating synthetic data with Faker, it’s important to understand the statistical distributions that underlie the data you’re creating. While Faker provides convenient methods for generating realistic-looking data, the default distributions may not always match real-world patterns. By examining and verifying these distributions, you can ensure your synthetic data accurately represents the scenarios you’re trying to test or model. This is particularly important in applications where statistical properties matter, such as financial modeling, user behavior simulation, or healthcare data analysis.

Our analysis of Faker’s date generation capabilities revealed interesting insights about temporal patterns. By default, Faker produces a nearly uniform distribution of months (with a relative standard deviation of just 0.097), but shows slight natural variations in days of the month (0.190 relative standard deviation) that reflect calendar realities like fewer occurrences of days 29-31. This subtle built-in awareness of calendar structures makes Faker’s date generation particularly useful for applications requiring realistic temporal data. However, when specific seasonal patterns are needed—such as summer-weighted dates for a vacation booking system—custom weighting approaches can be implemented to skew the distribution appropriately.

For price generation, we discovered that Faker employs sophisticated patterns that mirror real-world pricing distributions. Rather than producing a simple uniform or normal distribution, Faker creates a power-law distribution with multiple price bands that closely resembles actual market data. Our statistical analysis showed a dramatic difference between mean (USD12,512.43) and median (USD507.62) values, reflecting the natural pattern where most items are moderately priced but high-value outliers pull the average upward. This built-in sophistication makes Faker’s price generation immediately useful for financial applications without requiring additional customization.

➡️ Learn more about analyzing and controlling date distributions in Faker
➡️ Learn more about price distributions in Faker

Understanding these distribution patterns allows you to make informed decisions about when to use Faker’s default generation methods and when custom approaches might be necessary. By taking the time to analyze the statistical properties of your synthetic data, you can create more realistic test scenarios and gain deeper insights into how your applications will behave with real-world data patterns.

Validating Synthetic Credit Card Data

While understanding data distributions is essential for realistic synthetic data, some Faker providers require additional validation beyond statistical patterns. Credit card data exemplifies this need, as real card numbers must adhere to specific industry standards. Our analysis of Faker’s credit card implementation revealed mixed results: perfect compliance with mathematical validation (100% of generated numbers pass the Luhn algorithm check) but significant deviations from industry standards. Only 35.2% of generated Visa cards start with the required prefix “4”, and cards rarely match their advertised length specifications (with compliance rates as low as 9.3%). These findings highlight the importance of understanding both the statistical and rule-based aspects of synthetic data generation.

For applications that process financial transactions, these limitations could lead to missed validation issues during testing. While Faker provides mathematically valid card numbers, developers should be aware that additional filtering or custom generation logic may be needed to create truly representative test data that meets all industry specifications. Credit card validation serves as an excellent case study in the broader challenge of synthetic data generation – creating data that is not just statistically plausible but also compliant with domain-specific business rules.

➡️ Learn more about auditing credit card number generation in Faker

Bringing It All Together: International User Profiles

After exploring Faker’s capabilities for generating various types of data, analyzing their distributions, and validating critical aspects like credit card numbers, let’s bring everything together with a practical application. In this section, we’ll create internationalized user profiles that combine multiple Faker providers while ensuring realistic data distributions across different regions.

Let’s create a function that generates culturally appropriate user profiles for multiple countries:

 
from faker import Faker
import pandas as pd
from datetime import datetime, timedelta

def create_international_profiles(countries=['en_US', 'fr_FR', 'ja_JP', 'de_DE'], count=5):
    """Generate culturally appropriate user profiles for multiple countries."""
    
    all_profiles = []
    
    for locale in countries:
        # Create a localized Faker instance
        fake = Faker(locale)
        Faker.seed(42)  # For reproducibility
        
        for _ in range(count):
            # Generate age-appropriate registration date (younger users more recently)
            age = fake.random_int(min=18, max=80)
            max_years_ago = min(age - 18, 10)  # Max 10 years or when they turned 18
            days_ago = fake.random_int(min=1, max=365 * max_years_ago)
            reg_date = datetime.now() - timedelta(days=days_ago)
            
            profile = {
                'country': locale.split('_')[1],
                'locale': locale,
                'name': fake.name(),
                'address': fake.address().replace('\n', ', '),
                'age': age,
                'occupation': fake.job(),
                'registration_date': reg_date.strftime('%Y-%m-%d'),
                'favorite_color': fake.color_name(),
                'credit_score': fake.random_int(min=300, max=850)
            }
            all_profiles.append(profile)
    
    return pd.DataFrame(all_profiles)

# Generate international profiles
profiles_df = create_international_profiles()

# Display some sample profiles
profiles_df.sample(5)

This function creates realistic user profiles with culturally appropriate names, addresses, and other details for multiple locales. The registration date is intelligently generated based on the user’s age – younger users tend to have more recent registration dates, while older users might have been registered for longer periods. The credit score follows the common 300-850 range used in many financial systems. Here is a sample output of 5 randomly selected profiles: 


Table showing user demographics and scores. 
Image By Author
Now, let’s enhance these profiles with localized payment preferences that reflect regional differences:

 
def add_payment_preferences(profiles_df):
    """Add culturally appropriate payment preferences to user profiles."""
    
    # Define region-specific payment methods
    payment_methods = {
        'US': ['Credit Card', 'PayPal', 'Venmo', 'Apple Pay', 'Google Pay'],
        'FR': ['Carte Bancaire', 'PayPal', 'Apple Pay', 'Virement Bancaire'],
        'JP': ['Credit Card', 'Konbini Payment', 'PayPay', 'LINE Pay', 'Suica'],
        'DE': ['SEPA Direct Debit', 'Giropay', 'PayPal', 'Sofort', 'Klarna']
    }
    
    # Apply preferences based on country
    payment_prefs = []
    
    for _, row in profiles_df.iterrows():
        country = row['country']
        fake = Faker(row['locale'])
        
        # Get available payment methods for this country
        country_methods = payment_methods.get(country, payment_methods['US'])
        
        # Select 1-3 preferred payment methods
        num_methods = fake.random_int(min=1, max=min(3, len(country_methods)))
        selected_methods = fake.random_elements(elements=country_methods, 
                                               length=num_methods, unique=True)
        
        payment_prefs.append(', '.join(selected_methods))
    
    # Add to the dataframe
    profiles_df['payment_preferences'] = payment_prefs
    return profiles_df

# Enhance profiles with payment preferences
enhanced_profiles = add_payment_preferences(profiles_df)

# Display some sample profiles
profiles_df.sample(5)

By adding localized payment preferences, we’ve made these profiles even more realistic. Each country has its own set of common payment methods – Venmo in the US, Carte Bancaire in France, Konbini payments in Japan, and SEPA Direct Debit in Germany. The function randomly selects 1-3 preferred payment methods for each user based on their country, creating realistic variation. Here is a sample of 5 randomly selected profiles that have been enhanced: 


User table with payment preferences.
Image By Author
 
Finally, let’s analyze our synthetic user base to identify interesting patterns and generate business insights:

 
def analyze_user_profiles(profiles_df):
    """Analyze the synthetic user profiles and extract insights."""
    
    print("\n===== SYNTHETIC USER BASE ANALYSIS =====")
    
    # User demographics
    print(f"Total users: {len(profiles_df)}")
    print("\nUsers by country:")
    country_counts = profiles_df['country'].value_counts()
    for country, count in country_counts.items():
        print(f"  {country}: {count} users ({count/len(profiles_df)*100:.1f}%)")
    
    # Age distribution
    print(f"\nAge statistics:")
    print(f"  Average age: {profiles_df['age'].mean():.1f} years")
    print(f"  Youngest user: {profiles_df['age'].min()} years")
    print(f"  Oldest user: {profiles_df['age'].max()} years")
    
    # Registration patterns
    profiles_df['reg_date'] = pd.to_datetime(profiles_df['registration_date'])
    profiles_df['reg_year'] = profiles_df['reg_date'].dt.year
    
    print("\nRegistration years:")
    year_counts = profiles_df['reg_year'].value_counts().sort_index()
    for year, count in year_counts.items():
        print(f"  {year}: {count} users")
    
    # Payment preferences analysis
    all_methods = []
    for prefs in profiles_df['payment_preferences']:
        all_methods.extend([method.strip() for method in prefs.split(',')])
    
    method_counts = pd.Series(all_methods).value_counts()
    
    print("\nTop payment methods:")
    for method, count in method_counts.head(5).items():
        print(f"  {method}: {count} users")

# Analyze the enhanced profiles
analyze_user_profiles(enhanced_profiles)

This analysis function extracts meaningful insights from our synthetic user data, revealing patterns that closely mirror real-world user demographics. Our dataset has a perfect balance of users from each region (25% per country), with an average age of 49.5 years spanning from 23 to 80 years old. The registration pattern shows a realistic distribution over time, with more recent registrations (6 users in 2024) reflecting typical user growth patterns in real applications:

 
===== SYNTHETIC USER BASE ANALYSIS =====
Total users: 20

Users by country:
  US: 5 users (25.0%)
  FR: 5 users (25.0%)
  JP: 5 users (25.0%)
  DE: 5 users (25.0%)

Age statistics:
  Average age: 49.5 years
  Youngest user: 23 years
  Oldest user: 80 years

Registration years:
  2015: 1 users
  2016: 2 users
  2017: 2 users
  2019: 1 users
  2020: 4 users
  2021: 1 users
  2022: 3 users
  2024: 6 users

Top payment methods:
  Apple Pay: 7 users
  Credit Card: 6 users
  PayPal: 4 users
  Venmo: 3 users
  Carte Bancaire: 3 users

The payment preferences analysis reveals cross-cultural trends, with Apple Pay emerging as the most popular payment method overall (7 users), followed by traditional credit cards (6 users). This aligns with the global trend of digital wallet adoption while maintaining strong usage of traditional payment methods. The appearance of region-specific options like Carte Bancaire (France) demonstrates how our synthetic data captures cultural preferences in payment ecosystems.

By combining Faker’s various providers with thoughtful data generation logic, we’ve created a rich, internationally diverse dataset that reflects real-world patterns. This approach can be extended to create synthetic data for testing and development of global applications, ensuring that your systems work correctly with different cultural formats, preferences, and patterns.

The key to effective synthetic data generation is understanding both the technical capabilities of tools like Faker and the real-world patterns they aim to replicate. By considering aspects like age-appropriate registration dates, region-specific payment methods, and proper localization, you can create test data that truly resembles your production environment while maintaining complete privacy compliance.

Leave a Reply

Your email address will not be published. Required fields are marked *