Clean and Analyze GA4 CSV Data with Tkinter and Pandas

Tkinter interface for loading and cleaning Google Analytics CSV data with Pandas

This project uses Tkinter and Pandas to prepare a Google Analytics 4 report exported as a CSV file.

The user selects the Analytics CSV through a Tkinter file browser. The application detects the report header, creates a DataFrame, removes empty data, optionally removes query strings from page paths, creates Python-friendly column names and saves the cleaned DataFrame as another CSV file.

GA4 report
    |
    v
Download CSV
    |
    v
Tkinter file browser
    |
    v
Detect header row
    |
    v
pd.read_csv()
    |
    v
Clean DataFrame
    |
    v
Save cleaned CSV
GA4 note: older Universal Analytics tutorials used reports such as Behavior > Site Content > All Pages. This project is designed for CSV exports from the current Google Analytics reporting interface.

Download a Google Analytics 4 Pages Report Top ↑

In Google Analytics 4, open the Pages and screens report and set the required date range and report configuration before exporting the data.

Download the report as a CSV file. The exact columns in the downloaded file depend on the dimension, metrics and report configuration you selected.

A page report can contain columns such as:

Page path and screen class
Views
Active users
Views per active user
Average engagement time per active user
Event count
Key events
Total revenue

Do not build the Python program around one fixed list of Analytics metrics. A report can contain a different set of columns.

Why We Do Not Use a Fixed skiprows Value Top ↑

The older version of this tutorial used:

df=pd.read_csv(
    file,
    skiprows=6
)

That depended on the format of an older Analytics export.

A more reusable approach is:

Analytics CSV
     |
     v
Scan first rows
     |
     v
Find page-dimension header
     |
     v
Use that row as CSV header

This lets the application handle leading report information without assuming that the header is always on a particular physical line.

The application looks for common Pages and screens dimensions:

PAGE_DIMENSIONS=[
    'Page path and screen class',
    'Page path + query string and screen class',
    'Page title and screen class',
    'Page title and screen name'
]

The first part of the CSV file is scanned:

def detect_header_row(file_path):
    with open(
        file_path,
        'r',
        encoding='utf-8-sig',
        newline=''
    ) as file:
        reader=csv.reader(file)

        for row_number,row in enumerate(reader):
            values=[
                value.strip()
                for value in row
            ]

            if any(
                name in values
                for name in PAGE_DIMENSIONS
            ):
                return row_number

If no expected page dimension is found, the application reports that the selected file does not look like the expected GA4 page report.

Create a Pandas DataFrame from the GA4 CSV Top ↑

After detecting the header row:

header_row=detect_header_row(
    file_path
)

df=pd.read_csv(
    file_path,
    skiprows=header_row
)

The selected header row becomes the DataFrame column header.

The application preserves two DataFrames:

source_df
cleaned_df

source_df represents the imported Analytics report. cleaned_df is created when the cleaning operation runs.

Detect the Page Dimension Top ↑

Instead of assuming that the page field is called simply Page, the application detects which supported GA4 dimension is present.

def find_page_column(columns):
    for name in PAGE_DIMENSIONS:
        if name in columns:
            return name

    return None

This makes the program less dependent on one report configuration.

Remove Blank Rows and Columns Top ↑

Remove columns that contain no data:

cleaned_df=cleaned_df.dropna(
    axis=1,
    how='all'
)

Remove completely empty rows:

cleaned_df=cleaned_df.dropna(
    how='all'
)

Rows without a page dimension are also removed:

cleaned_df=cleaned_df.dropna(
    subset=[page_column]
)

The page values are stripped of surrounding spaces:

cleaned_df[page_column]=cleaned_df[
    page_column
].astype('string').str.strip()

Remove Query Strings from Page Paths Top ↑

A URL such as:

/python/list.php?utm_source=newsletter

can represent the same underlying page as:

/python/list.php

The old tutorial removed every row containing ?. That can discard valid traffic.

The revised application can instead remove only the query-string part:

cleaned_df[page_column]=cleaned_df[
    page_column
].str.replace(
    r'\?.*$',
    '',
    regex=True
)

Fragments can be removed in the same way:

.str.replace(
    r'#.*$',
    '',
    regex=True
)
Important: after removing query strings, several Analytics rows can have the same normalized page path. Do not automatically sum every GA4 metric. Metrics such as users and averages require metric-aware aggregation.

Create Python-Friendly Column Names Top ↑

The older tutorial forced the DataFrame into exactly seven names:

df.columns=[
    'Page',
    'p_view',
    'u_view',
    'avg',
    'entrance',
    'b_rate',
    'b_exit'
]

This no longer fits GA4 reports.

The revised application converts whatever columns were actually exported into lower-case underscore names.

Examples:

Views
    -> views

Active users
    -> active_users

Average engagement time per active user
    -> average_engagement_time_per_active_user

The detected page dimension is given the simpler name:

page

Preview the Source and Cleaned DataFrame Top ↑

The application displays:

  • detected header row;
  • detected page dimension;
  • number of rows;
  • number of columns;
  • column names;
  • first ten records.

For example:

Rows: 1250
Columns: 8
Page field: page

COLUMNS

page
views
active_users
views_per_active_user
average_engagement_time_per_active_user
event_count
key_events
total_revenue

Save the Cleaned Analytics DataFrame Top ↑

Use the Tkinter Save As dialog:

file_path=filedialog.asksaveasfilename(
    defaultextension='.csv',
    filetypes=[
        ('CSV file','*.csv')
    ]
)

Save the cleaned DataFrame using to_csv():

cleaned_df.to_csv(
    file_path,
    index=False
)

Complete Tkinter GA4 CSV Cleaning Application Top ↑

Why We Keep Source and Cleaned Data Separately Top ↑

The application stores:

source_df
cleaned_df

The original imported report remains in source_df. Cleaning creates another DataFrame.

GA4 CSV
   |
   v
source_df
   |
   +----------------+
   |                |
   v                |
clean_data()        |
   |                |
   v                |
cleaned_df          |
                    |
Show Source --------+

This makes it easy to compare the report before and after cleaning.

Why We Do Not Automatically Merge Duplicate Page Paths Top ↑

Removing query strings can turn:

/python/list.php?a=1
/python/list.php?a=2
/python/list.php?a=3

into:

/python/list.php
/python/list.php
/python/list.php

It may be tempting to immediately group these rows and sum every metric.

However, Analytics metrics do not all behave the same way. A metric such as page views can often be summed across rows, while user-based or average metrics should not automatically be added together.

This page therefore prepares the DataFrame but leaves aggregation to a later analysis step where the correct metric-specific calculation can be chosen.

Analyze the Cleaned GA4 Data Top ↑

After saving the cleaned CSV, continue with the practical analysis project:

Pandas GroupBy and Pivot Analysis

You can also choose specific DataFrame columns:

Select DataFrame Columns

Or search the DataFrame through a Tkinter interface:

Search DataFrame Records

Frequently Asked Questions Top ↑

Q1: Which Google Analytics report is this tutorial designed for?

It is designed primarily for CSV exports containing a GA4 page dimension such as Page path and screen class or Page path + query string and screen class.

Q2: Why does the program detect the CSV header instead of using skiprows=6?

A fixed skip value depends on one export format. Detecting a known GA4 page dimension makes the import less dependent on a specific number of leading rows.

Q3: Why not delete every page containing a question mark?

A question mark normally introduces a query string. Removing the complete row can discard useful Analytics data, so the revised example removes only the query-string portion of a page path.

Q4: Why are the Analytics column names changed?

Spaces and punctuation are converted to lower-case underscore names so the columns are easier to reference in later Python and Pandas operations.

Q5: Why is the page dimension renamed to page?

GA4 provides several possible page dimensions. Renaming the detected dimension to a consistent page field makes later processing simpler.

Q6: Does removing query strings automatically combine duplicate page rows?

No. The rows remain separate because different GA4 metrics require different aggregation rules.

Q7: Does cleaning modify the downloaded GA4 file?

No. The downloaded report remains unchanged. A separate cleaned DataFrame is created and can be saved as a new CSV file.


Pandas Data Analysis Select DataFrame Columns Search DataFrame

Tkinter Projects Tkinter Pandas Projects


Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer