Linux

How To Create Dummy Data using Python

Create Dummy Data using Python

Generating dummy data is an essential skill for developers, data scientists, and testers who need realistic datasets without using sensitive production information. Python offers powerful tools that make creating synthetic data straightforward and efficient. This comprehensive guide explores how to generate various types of dummy data using Python libraries, with practical examples and best practices to ensure your test data serves its purpose effectively.

Understanding the Need for Dummy Data

When developing applications, testing algorithms, or building data pipelines, using real data isn’t always feasible or advisable. Dummy data fills this gap by providing synthetic information that mimics real-world data patterns without exposing sensitive information.

Working with realistic fake data offers several advantages:

  • Data privacy protection – Eliminates the risk of exposing sensitive customer information during testing and development
  • Controlled testing environments – Allows for testing edge cases and specific scenarios that might be rare in production data
  • Regulatory compliance – Helps maintain compliance with data protection regulations like GDPR and HIPAA
  • Development speed – Enables parallel development without waiting for production data access
  • Consistent test results – Creates reproducible test conditions with known data characteristics

Many developers face challenges obtaining suitable test data, particularly when dealing with specialized information types or when they need large volumes of realistic records. Python’s data generation libraries solve these problems by providing programmable, customizable data creation tools.

Essential Python Libraries for Generating Dummy Data

Several Python libraries can generate dummy data, each with particular strengths and use cases. Understanding the capabilities of these tools helps select the right approach for your specific data needs.

Faker Library Introduction

The Faker library stands out as the most comprehensive and flexible tool for generating realistic fake data in Python. Heavily inspired by implementations in PHP, Perl, and Ruby, Faker provides a simple interface for creating human-readable test data.

This open-source package specializes in generating various data types, from basic personal information to complex structured records. Faker’s popularity stems from its ease of use, extensive documentation, and ability to create convincing fake records that maintain internal consistency.

NumPy and Pandas

For numerical and statistical dummy data generation, NumPy and Pandas offer powerful capabilities:

  • NumPy excels at generating random numbers with specific distributions, shapes, and statistical properties. Its functions enable creation of arrays filled with random integers, floating-point values, or boolean data.
  • Pandas works seamlessly with NumPy to transform raw random data into structured DataFrames. The combination allows for creating tabular data with properly formatted columns, appropriate data types, and meaningful relationships between fields.

These libraries are particularly useful when you need data with specific statistical properties for machine learning model testing or when working with numerical simulations.

Specialized Libraries

Beyond the standard libraries, specialized tools address specific dummy data needs:

  • Ficto generates realistic datasets directly to CSV and JSON formats with support for complex relational structures
  • Mimesis provides high-performance data generation with a focus on data schema definition
  • SDV (Synthetic Data Vault) creates synthetic datasets that maintain the statistical properties of original data

Each library offers unique advantages depending on your specific requirements for data complexity, generation speed, and output format.

Getting Started with Faker

Faker stands out as the most versatile Python library for generating dummy data across different categories. Let’s explore how to set up and configure this powerful tool.

Installation and Basic Setup

Getting started with Faker requires Python 3.6 or higher. Installation is straightforward using pip:

pip install faker

After installation, you can initialize the Faker generator with just a few lines of code:

from faker import Faker

# Create a Faker instance
fake = Faker()

# Generate some basic fake data
print(fake.name())          # Example output: "Meilana Maria"
print(fake.email())         # Example output: "mey@example.com"
print(fake.address())       # Example output: "2606 Mackenzie Tunnel Apt. 215"

This simple setup gives you immediate access to hundreds of data generation methods.

Configuring Faker

Faker offers several configuration options to customize its behavior:

1. Setting random seeds for reproducibility:

from faker import Faker

# Create the Faker instance
fake = Faker()

# Set seed for reproducible results
Faker.seed(42)

# Will generate the same values on each run
print(fake.name())
print(fake.address())

2. Locale settings for internationalization:

# Create a Faker instance for German data
fake_de = Faker('de_DE')
print(fake_de.name())       # German name
print(fake_de.address())    # German address

# Multiple locales
fake_multi = Faker(['en_US', 'ja_JP', 'fr_FR'])
print(fake_multi.name())    # Name in random locale

3. Custom provider configuration:

# Configure specific providers with arguments
fake = Faker()
print(fake.pystr(min_chars=10, max_chars=20))  # Random string with length 10-20
print(fake.date_between(start_date='-30d', end_date='today'))  # Date within last 30 days

These configuration options make Faker highly adaptable to a wide range of data generation needs.

Generating Different Types of Dummy Data

With Faker configured, you can generate a diverse array of data types to satisfy various testing and development requirements.

Personal Information

Creating realistic personal profiles is one of Faker’s primary strengths:

# Generate personal information
print(f"Name: {fake.name()}")                      # Full name
print(f"First name: {fake.first_name()}")          # First name only
print(f"Address: {fake.address()}")                # Complete address
print(f"Phone: {fake.phone_number()}")             # Phone number
print(f"Email: {fake.email()}")                    # Email address
print(f"SSN: {fake.ssn()}")                        # Social Security Number
print(f"Job: {fake.job()}")                        # Job title

For more comprehensive profiles, Faker provides the profile() method that generates multiple related personal fields at once:

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

This generates consistent information including name, address, birthdate, and other demographic details.

Text Content

When testing applications that handle text content, Faker offers various methods to generate text of different lengths and styles:

# Generate text content
print(fake.text())              # Random paragraph
print(fake.sentence())          # Single sentence 
print(fake.word())              # Single word
print(fake.paragraph(nb_sentences=5))  # Paragraph with 5 sentences
print(fake.text(max_nb_chars=200))     # Text with max 200 characters

For longer content or specific text patterns:

# Generate specialized text
print(fake.text(max_nb_chars=2000))    # Longer article
print(fake.lorem_ipsum(nb_paragraphs=3))  # Lorem ipsum text

Dates and Times

Working with temporal data often requires specific date and time formats or ranges:

# Generate dates and times
print(fake.date())              # Date in YYYY-MM-DD format
print(fake.time())              # Time in HH:MM:SS format
print(fake.date_time())         # DateTime object

# Date within a specific range
print(fake.date_between(start_date='-30y', end_date='today'))  # Date in last 30 years

# Time series data
print(fake.date_time_between(start_date='-1y', end_date='now', tzinfo=None))

For time-sensitive applications, you can generate dates with timezone information:

# Generate timezone-aware datetimes
print(fake.date_time_this_decade(tzinfo=None, before_now=True, after_now=False))

Numerical and Categorical Data

For quantitative analysis and testing, Faker provides methods to generate various numerical data types:

# Generate numerical data
print(fake.random_int(min=1, max=100))        # Random integer
print(fake.random_digit())                    # Single digit (0-9)
print(fake.pyfloat(left_digits=3, right_digits=2, positive=True))  # Float

# Categorical data
print(fake.boolean(chance_of_getting_true=50))  # Boolean with 50% true probability
print(fake.color_name())                      # Random color name
print(fake.currency_code())                   # Currency code (e.g., USD)

For structured numerical data:

# Financial data
print(fake.credit_card_number())              # Credit card number
print(fake.credit_card_full())                # Full credit card details
print(fake.cryptocurrency_code())             # Cryptocurrency code

Creating Structured Datasets

Individual data points are useful, but most applications require structured datasets with multiple records and relationships between fields.

Building DataFrames with Pandas

Combining Faker with Pandas enables creation of tabular datasets:

import pandas as pd
from faker import Faker

fake = Faker()

# Create a simple dataframe with customer data
def create_customers(num_records=100):
    data = []
    for _ in range(num_records):
        data.append({
            'name': fake.name(),
            'email': fake.email(),
            'address': fake.address(),
            'phone': fake.phone_number(),
            'signup_date': fake.date_between(start_date='-2y', end_date='today'),
            'is_active': fake.boolean(chance_of_getting_true=80)
        })
    return pd.DataFrame(data)

# Generate 100 customer records
customers_df = create_customers(100)
print(customers_df.head())

For more complex datasets, functions can generate multiple related dataframes:

# Create related product data
def create_products(num_products=50):
    categories = ['Electronics', 'Clothing', 'Books', 'Home & Kitchen', 'Toys']
    
    data = []
    for _ in range(num_products):
        data.append({
            'product_id': fake.uuid4(),
            'name': fake.catch_phrase(),
            'category': fake.random_element(elements=categories),
            'price': round(fake.random_number(digits=2) + fake.random_digit() / 10, 2),
            'in_stock': fake.random_int(min=0, max=500),
            'rating': round(fake.random_number(digits=1) + fake.random_digit() / 10, 1)
        })
    return pd.DataFrame(data)

Relationships Between Columns

Creating realistic relationships between data columns enhances dataset quality:

# Generate data with relationships
def create_orders(customers_df, products_df, num_orders=200):
    customer_ids = customers_df.index.tolist()
    product_ids = products_df.index.tolist()
    
    data = []
    for _ in range(num_orders):
        customer_id = fake.random_element(elements=customer_ids)
        product_id = fake.random_element(elements=product_ids)
        
        # Get customer details to ensure data consistency
        customer = customers_df.loc[customer_id]
        product = products_df.loc[product_id]
        
        # Create order with related data
        data.append({
            'order_id': fake.uuid4(),
            'customer_id': customer_id,
            'customer_name': customer['name'],
            'product_id': product_id,
            'product_name': product['name'],
            'quantity': fake.random_int(min=1, max=5),
            'price': product['price'],
            'order_date': fake.date_time_between(
                start_date=customer['signup_date'], 
                end_date='now'
            ),
            'shipping_address': customer['address']
        })
    return pd.DataFrame(data)

This approach ensures logical consistency, such as order dates being after customer signup dates and correct product information linked to each order.

Advanced Techniques

For specialized data generation needs, advanced techniques can enhance the realism and utility of your dummy data.

Custom Providers

When standard providers don’t meet your needs, create custom providers:

from faker.providers import BaseProvider

# Create a custom provider for medical data
class MedicalProvider(BaseProvider):
    blood_types = ['A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-']
    common_conditions = [
        'Hypertension', 'Diabetes Type 2', 'Asthma', 'Arthritis',
        'Obesity', 'Depression', 'Anxiety', 'GERD', 'Migraine',
        'Hypothyroidism', 'Hyperlipidemia'
    ]
    
    def blood_type(self):
        return self.random_element(self.blood_types)
    
    def medical_condition(self):
        return self.random_element(self.common_conditions)
    
    def height_cm(self):
        return self.random_int(min=150, max=200)
    
    def weight_kg(self):
        return self.random_int(min=45, max=120)

# Add the provider to your faker instance
fake = Faker()
fake.add_provider(MedicalProvider)

# Use the custom provider
print(fake.blood_type())
print(fake.medical_condition())

Realistic Data Constraints

Real-world data follows specific patterns and constraints, which can be implemented in data generation:

# Generate age-appropriate data
def generate_person_with_constraints():
    age = fake.random_int(min=18, max=85)
    birth_year = 2024 - age
    
    # Create education history based on age
    education = "High School"
    if age >= 22:
        education = fake.random_element(['Bachelor\'s', 'Master\'s', 'High School'])
    if age >= 26 and fake.boolean(chance_of_getting_true=30):
        education = 'PhD'
    
    # Create work experience proportional to age
    work_experience = max(0, age - 18 - fake.random_int(min=0, max=3))
    if education == 'PhD':
        work_experience = max(0, age - 26)
    
    return {
        'name': fake.name(),
        'age': age,
        'birth_date': f"{birth_year}-{fake.date_object().month}-{fake.date_object().day}",
        'education': education,
        'work_experience_years': work_experience
    }

Large Scale Data Generation

For large datasets, efficiency becomes crucial:

import concurrent.futures
import time

# Generate large datasets efficiently
def generate_large_dataset(records=1000000):
    start_time = time.time()
    
    data = []
    chunk_size = 10000
    num_chunks = records // chunk_size
    
    # Function to generate a chunk of records
    def generate_chunk(chunk_id):
        chunk_data = []
        chunk_fake = Faker()  # Local Faker instance per thread
        for i in range(chunk_size):
            chunk_data.append({
                'id': chunk_id * chunk_size + i,
                'name': chunk_fake.name(),
                'email': chunk_fake.email()
            })
        return chunk_data
    
    # Use multi-threading to generate data in parallel
    with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
        futures = [executor.submit(generate_chunk, i) for i in range(num_chunks)]
        for future in concurrent.futures.as_completed(futures):
            data.extend(future.result())
    
    end_time = time.time()
    print(f"Generated {len(data)} records in {end_time - start_time:.2f} seconds")
    
    return pd.DataFrame(data)

This multi-threaded approach significantly improves performance when generating massive datasets.

Practical Examples

Let’s explore practical examples of generating dummy data for specific domains.

Example 1: E-commerce Dataset

def create_ecommerce_database():
    # Customer data
    customers = pd.DataFrame([{
        'customer_id': fake.uuid4(),
        'name': fake.name(),
        'email': fake.email(),
        'phone': fake.phone_number(),
        'address': fake.address(),
        'signup_date': fake.date_time_between(start_date='-3y', end_date='now'),
        'loyalty_points': fake.random_int(min=0, max=10000)
    } for _ in range(500)])
    
    # Product categories
    categories = pd.DataFrame([{
        'category_id': i,
        'name': cat,
        'description': fake.text(max_nb_chars=200)
    } for i, cat in enumerate(['Electronics', 'Clothing', 'Books', 'Home & Kitchen', 'Toys'])])
    
    # Products
    products = pd.DataFrame([{
        'product_id': fake.uuid4(),
        'name': fake.bs(),
        'category_id': fake.random_element(elements=categories['category_id'].tolist()),
        'price': round(fake.random_number(digits=2) + fake.random_digit() / 10, 2),
        'description': fake.text(max_nb_chars=300),
        'in_stock': fake.random_int(min=0, max=500),
        'rating': round(fake.random_number(digits=1) + fake.random_digit() / 10, 1)
    } for _ in range(200)])
    
    # Orders
    orders = []
    order_items = []
    
    for _ in range(1000):
        customer_id = fake.random_element(elements=customers['customer_id'].tolist())
        customer = customers[customers['customer_id'] == customer_id].iloc[0]
        
        order_id = fake.uuid4()
        order_date = fake.date_time_between(
            start_date=customer['signup_date'], 
            end_date='now'
        )
        
        # Add order
        orders.append({
            'order_id': order_id,
            'customer_id': customer_id,
            'order_date': order_date,
            'shipping_address': customer['address'],
            'payment_method': fake.random_element(elements=['Credit Card', 'PayPal', 'Bank Transfer']),
            'shipping_cost': round(fake.random_number(digits=1) + fake.random_digit() / 10, 2),
            'status': fake.random_element(elements=['Pending', 'Shipped', 'Delivered', 'Cancelled'])
        })
        
        # Add order items
        num_items = fake.random_int(min=1, max=5)
        order_product_ids = fake.random_elements(
            elements=products['product_id'].tolist(),
            length=num_items,
            unique=True
        )
        
        for product_id in order_product_ids:
            product = products[products['product_id'] == product_id].iloc[0]
            order_items.append({
                'order_id': order_id,
                'product_id': product_id,
                'quantity': fake.random_int(min=1, max=5),
                'unit_price': product['price'],
                'discount': round(fake.random_number(digits=1) / 10, 2) if fake.boolean(chance_of_getting_true=30) else 0
            })
    
    orders_df = pd.DataFrame(orders)
    order_items_df = pd.DataFrame(order_items)
    
    return {
        'customers': customers,
        'categories': categories,
        'products': products,
        'orders': orders_df,
        'order_items': order_items_df
    }

Example 2: Healthcare Data

def create_healthcare_database():
    # Create custom provider for medical data if not done already
    if not hasattr(fake, 'blood_type'):
        fake.add_provider(MedicalProvider)
    
    # Patient data
    patients = pd.DataFrame([{
        'patient_id': fake.uuid4(),
        'name': fake.name(),
        'dob': fake.date_of_birth(minimum_age=18, maximum_age=90),
        'gender': fake.random_element(elements=['M', 'F']),
        'blood_type': fake.blood_type(),
        'height_cm': fake.height_cm(),
        'weight_kg': fake.weight_kg(),
        'address': fake.address(),
        'phone': fake.phone_number(),
        'insurance_provider': fake.company()
    } for _ in range(300)])
    
    # Doctors
    specialties = ['Cardiology', 'Dermatology', 'Endocrinology', 'Gastroenterology', 
                  'Neurology', 'Oncology', 'Pediatrics', 'Psychiatry', 'Surgery']
    
    doctors = pd.DataFrame([{
        'doctor_id': fake.uuid4(),
        'name': fake.name(),
        'specialty': fake.random_element(elements=specialties),
        'years_experience': fake.random_int(min=1, max=35),
        'office_number': f"Room {fake.random_int(min=100, max=500)}",
        'phone': fake.phone_number()
    } for _ in range(50)])
    
    # Appointments
    appointments = []
    
    for _ in range(1000):
        patient_id = fake.random_element(elements=patients['patient_id'].tolist())
        doctor_id = fake.random_element(elements=doctors['doctor_id'].tolist())
        
        appointment_date = fake.date_time_between(start_date='-1y', end_date='+3m')
        
        appointments.append({
            'appointment_id': fake.uuid4(),
            'patient_id': patient_id,
            'doctor_id': doctor_id,
            'appointment_date': appointment_date,
            'reason': fake.sentence(),
            'status': fake.random_element(
                elements=['Scheduled', 'Completed', 'Cancelled', 'No-show']
            )
        })
    
    appointments_df = pd.DataFrame(appointments)
    
    # Medical records
    medical_records = []
    
    for _, appointment in appointments_df[appointments_df['status'] == 'Completed'].iterrows():
        medical_records.append({
            'record_id': fake.uuid4(),
            'appointment_id': appointment['appointment_id'],
            'patient_id': appointment['patient_id'],
            'doctor_id': appointment['doctor_id'],
            'diagnosis': fake.medical_condition() if fake.boolean(chance_of_getting_true=80) else None,
            'prescription': fake.bs() if fake.boolean(chance_of_getting_true=70) else None,
            'notes': fake.text(max_nb_chars=500),
            'follow_up_needed': fake.boolean(chance_of_getting_true=40)
        })
    
    medical_records_df = pd.DataFrame(medical_records)
    
    return {
        'patients': patients,
        'doctors': doctors,
        'appointments': appointments_df,
        'medical_records': medical_records_df
    }

Example 3: Financial Transactions

def create_financial_database():
    # Bank accounts
    account_types = ['Checking', 'Savings', 'Investment', 'Credit Card']
    
    accounts = pd.DataFrame([{
        'account_id': fake.uuid4(),
        'customer_name': fake.name(),
        'account_type': fake.random_element(elements=account_types),
        'account_number': fake.bban(),
        'open_date': fake.date_time_between(start_date='-10y', end_date='now'),
        'balance': round(fake.random_number(digits=5) + fake.random_number(digits=2)/100, 2),
        'currency': 'USD',
        'interest_rate': round(fake.random_number(digits=1)/100, 4) if fake.boolean(chance_of_getting_true=70) else 0
    } for _ in range(200)])
    
    # Transactions
    transaction_types = ['Deposit', 'Withdrawal', 'Transfer', 'Payment', 'Refund', 'Fee']
    merchant_categories = ['Retail', 'Dining', 'Travel', 'Entertainment', 'Utilities', 'Healthcare']
    
    transactions = []
    
    for _ in range(5000):
        account_id = fake.random_element(elements=accounts['account_id'].tolist())
        account = accounts[accounts['account_id'] == account_id].iloc[0]
        
        transaction_type = fake.random_element(elements=transaction_types)
        
        if transaction_type in ['Withdrawal', 'Payment', 'Fee']:
            amount = -1 * round(fake.random_number(digits=3) + fake.random_number(digits=2)/100, 2)
        else:
            amount = round(fake.random_number(digits=3) + fake.random_number(digits=2)/100, 2)
        
        transactions.append({
            'transaction_id': fake.uuid4(),
            'account_id': account_id,
            'date': fake.date_time_between(start_date=account['open_date'], end_date='now'),
            'type': transaction_type,
            'amount': amount,
            'balance_after': round(fake.random_number(digits=5) + fake.random_number(digits=2)/100, 2),
            'description': fake.bs(),
            'merchant_name': fake.company() if transaction_type in ['Payment', 'Refund'] else None,
            'merchant_category': fake.random_element(elements=merchant_categories) if transaction_type in ['Payment', 'Refund'] else None,
            'reference_number': fake.ean(length=13) if fake.boolean(chance_of_getting_true=80) else None
        })
    
    transactions_df = pd.DataFrame(transactions)
    
    return {
        'accounts': accounts,
        'transactions': transactions_df
    }

Exporting and Using Generated Data

After generating dummy data, you’ll need to save it in appropriate formats and validate its quality.

File Formats

Export your dummy data to various formats based on your needs:

# Export to CSV
def export_to_csv(dataframes, output_dir='./dummy_data'):
    import os
    
    # Create output directory if it doesn't exist
    os.makedirs(output_dir, exist_ok=True)
    
    # Export each dataframe
    for name, df in dataframes.items():
        file_path = os.path.join(output_dir, f"{name}.csv")
        df.to_csv(file_path, index=False)
        print(f"Exported {len(df)} records to {file_path}")

# Export to JSON
def export_to_json(dataframes, output_dir='./dummy_data'):
    import os
    
    # Create output directory if it doesn't exist
    os.makedirs(output_dir, exist_ok=True)
    
    # Export each dataframe
    for name, df in dataframes.items():
        file_path = os.path.join(output_dir, f"{name}.json")
        df.to_json(file_path, orient='records', indent=2)
        print(f"Exported {len(df)} records to {file_path}")

# Export to database
def export_to_database(dataframes, connection_string):
    import sqlalchemy
    
    engine = sqlalchemy.create_engine(connection_string)
    
    # Export each dataframe
    for name, df in dataframes.items():
        df.to_sql(name, engine, if_exists='replace', index=False)
        print(f"Exported {len(df)} records to database table '{name}'")

Data Validation and Quality Assurance

Before using your dummy data, verify its quality and consistency:

def validate_data(dataframes):
    validation_results = {}
    
    for name, df in dataframes.items():
        # Check for missing values
        missing_values = df.isnull().sum().sum()
        
        # Check for duplicates (if id columns exist)
        id_columns = [col for col in df.columns if col.endswith('_id')]
        duplicates = 0
        if id_columns:
            duplicates = df.duplicated(subset=id_columns).sum()
        
        # Check data types
        data_types = df.dtypes.to_dict()
        
        # Store validation results
        validation_results[name] = {
            'row_count': len(df),
            'column_count': len(df.columns),
            'missing_values': missing_values,
            'duplicates': duplicates,
            'data_types': data_types
        }
    
    return validation_results

This validation ensures the generated data meets your requirements before using it in applications.

Best Practices and Considerations

Creating high-quality dummy data requires attention to several key best practices.

Reproducibility

For testing and debugging, reproducible data generation is essential:

# Ensure reproducibility
from faker import Faker

# Set global seed for reproducibility
SEED = 42
Faker.seed(SEED)

def generate_reproducible_data():
    # Create a Faker instance with the same seed
    fake = Faker()
    fake.seed_instance(SEED)
    
    # Generate data that will be identical across runs
    data = [{
        'id': i,
        'name': fake.name(),
        'email': fake.email()
    } for i in range(10)]
    
    return pd.DataFrame(data)

Document the seed values used to generate datasets, allowing recreation of the same data when needed.

Performance Optimization

For large datasets, consider these performance tips:

  1. Use batch generation instead of row-by-row creation
  2. Implement multi-threading for parallel generation
  3. Minimize dependencies between data elements
  4. Use more efficient data structures during generation
  5. Consider using compiled code (Cython) for critical performance bottlenecks

Security and Privacy

Even with fake data, be cautious about:

  1. Ensuring generated data doesn’t accidentally contain real information
  2. Avoiding patterns that resemble real-world sensitive data (like valid credit card formats)
  3. Not using dummy data generation code in production environments
  4. Being careful with dummy data that might be confused with real data by users

VPS Manage Service Offer
If you don’t have time to do all of this stuff, or if this is not your area of expertise, we offer a service to do “VPS Manage Service Offer”, starting from $10 (Paypal payment). Please contact us to get the best deal!

r00t

r00t is an experienced Linux enthusiast and technical writer with a passion for open-source software. With years of hands-on experience in various Linux distributions, r00t has developed a deep understanding of the Linux ecosystem and its powerful tools. He holds certifications in SCE and has contributed to several open-source projects. r00t is dedicated to sharing her knowledge and expertise through well-researched and informative articles, helping others navigate the world of Linux with confidence.
Back to top button