Close Menu
    Trending
    • What If Your Portfolio Could Speak for You? | by Lusha Wang | Jun, 2025
    • High Paying, Six Figure Jobs For Recent Graduates: Report
    • What If I had AI in 2018: Rent the Runway Fulfillment Center Optimization
    • YouBot: Understanding YouTube Comments and Chatting Intelligently — An Engineer’s Perspective | by Sercan Teyhani | Jun, 2025
    • Inspiring Quotes From Brian Wilson of The Beach Boys
    • AI Is Not a Black Box (Relatively Speaking)
    • From Accidents to Actuarial Accuracy: The Role of Assumption Validation in Insurance Claim Amount Prediction Using Linear Regression | by Ved Prakash | Jun, 2025
    • I Wish Every Entrepreneur Had a Dad Like Mine — Here’s Why
    Finance StarGate
    • Home
    • Artificial Intelligence
    • AI Technology
    • Data Science
    • Machine Learning
    • Finance
    • Passive Income
    Finance StarGate
    Home»Artificial Intelligence»4-Dimensional Data Visualization: Time in Bubble Charts
    Artificial Intelligence

    4-Dimensional Data Visualization: Time in Bubble Charts

    FinanceStarGateBy FinanceStarGateFebruary 12, 2025No Comments8 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    Share
    Facebook Twitter LinkedIn Pinterest Email

    Bubble Charts elegantly compress massive quantities of knowledge right into a single visualization, with bubble measurement including a 3rd dimension. Nonetheless, evaluating “earlier than” and “after” states is commonly essential. To deal with this, we suggest including a transition between these states, creating an intuitive consumer expertise.

    Since we couldn’t discover a ready-made answer, we developed our personal. The problem turned out to be fascinating and required refreshing some mathematical ideas.

    Surely, essentially the most difficult a part of the visualization is the transition between two circles — earlier than and after states. To simplify, we give attention to fixing a single case, which might then be prolonged in a loop to generate the mandatory variety of transitions.

    To construct such a determine, let’s first decompose it into three elements: two circles and a polygon that connects them (in grey).

    Base factor decomposition, picture by Writer

    Constructing two circles is sort of easy — we all know their facilities and radii. The remaining activity is to assemble a quadrilateral polygon, which has the next kind:

    Polygon, picture by Writer

    The development of this polygon reduces to discovering the coordinates of its vertices. That is essentially the most fascinating activity, and we’ll remedy it additional.

    From polygon to tangent traces, picture by Writer

    To calculate the space from a degree (x1, y1) to the road ax+y+b=0, the components is:

    Distance from level to a line, picture by Writer

    In our case, distance (d) is the same as circle radius (r). Therefore,

    Distance to radius, picture by Writer

    After multiplying either side of the equation by a**2+1, we get:

    Base math, picture by Writer

    After shifting every thing to at least one facet and setting the equation equal to zero, we get:

    Base math, picture by Writer

    Since we’ve got two circles and must discover a tangent to each, we’ve got the next system of equations:

    System of equations, picture by Writer

    This works nice, however the issue is that we’ve got 4 doable tangent traces in actuality:

    All doable tangent traces, picture by Writer

    And we have to select simply 2 of them — exterior ones.

    To do that we have to verify every tangent and every circle middle and decide if the road is above or beneath the purpose:

    Examine if line is above or beneath the purpose, picture by Writer

    We’d like the 2 traces that each cross above or each cross beneath the facilities of the circles.

    Now, let’s translate all these steps into code:

    import matplotlib.pyplot as plt
    import numpy as np
    import pandas as pd
    import sympy as sp
    from scipy.spatial import ConvexHull
    import math
    from matplotlib import rcParams
    import matplotlib.patches as patches
    
    def check_position_relative_to_line(a, b, x0, y0):
        y_line = a * x0 + b
        
        if y0 > y_line:
            return 1 # line is above the purpose
        elif y0 

    Now, we simply want to seek out the intersections of the tangents with the circles. These 4 factors would be the vertices of the specified polygon.

    Circle equation:

    Circle equation, picture by Writer

    Substitute the road equation y=ax+b into the circle equation:

    Base math, picture by Writer

    Resolution of the equation is the x of the intersection.

    Then, calculate y from the road equation:

    Calculating y, picture by Writer

    The way it interprets to the code:

    def find_circle_line_intersection(circle_x, circle_y, circle_r, line_a, line_b):
        x, y = sp.symbols('x y')
        circle_eq = (x - circle_x)**2 + (y - circle_y)**2 - circle_r**2
        intersection_eq = circle_eq.subs(y, line_a * x + line_b)
    
        sol_x_raw = sp.remedy(intersection_eq, x)[0]
        attempt:
            sol_x = float(sol_x_raw)
        besides:
            sol_x = sol_x_raw.as_real_imag()[0]
        sol_y = line_a * sol_x + line_b
        return sol_x, sol_y

    Now we wish to generate pattern information to reveal the entire chart compositions.

    Think about we’ve got 4 customers on our platform. We all know what number of purchases they made, generated income and exercise on the platform. All these metrics are calculated for two durations (let’s name them pre and publish interval).

    # information technology
    df = pd.DataFrame({'consumer': ['Emily', 'Emily', 'James', 'James', 'Tony', 'Tony', 'Olivia', 'Olivia'],
                       'interval': ['pre', 'post', 'pre', 'post', 'pre', 'post', 'pre', 'post'],
                       'num_purchases': [10, 9, 3, 5, 2, 4, 8, 7],
                       'income': [70, 60, 80, 90, 20, 15, 80, 76],
                       'exercise': [100, 80, 50, 90, 210, 170, 60, 55]})
    Knowledge pattern, picture by Writer

    Let’s assume that “exercise” is the realm of the bubble. Now, let’s convert it into the radius of the bubble. We will even scale the y-axis.

    def area_to_radius(space):
        radius = math.sqrt(space / math.pi)
        return radius
    
    x_alias, y_alias, a_alias="num_purchases", 'income', 'exercise'
    
    # scaling metrics
    radius_scaler = 0.1
    df['radius'] = df[a_alias].apply(area_to_radius) * radius_scaler
    df['y_scaled'] = df[y_alias] / df[x_alias].max()

    Now let’s construct the chart — 2 circles and the polygon.

    def draw_polygon(plt, factors):
        hull = ConvexHull(factors)
        convex_points = [points[i] for i in hull.vertices]
    
        x, y = zip(*convex_points)
        x += (x[0],)
        y += (y[0],)
    
        plt.fill(x, y, shade="#99d8e1", alpha=1, zorder=1)
    
    # bubble pre
    for _, row in df[df.period=='pre'].iterrows():
        x = row[x_alias]
        y = row.y_scaled
        r = row.radius
        circle = patches.Circle((x, y), r, facecolor="#99d8e1", edgecolor="none", linewidth=0, zorder=2)
        plt.gca().add_patch(circle)
    
    # transition space
    for consumer in df.consumer.distinctive():
        user_pre = df[(df.user==user) & (df.period=='pre')]
        x1, y1, r1 = user_pre[x_alias].values[0], user_pre.y_scaled.values[0], user_pre.radius.values[0]
        user_post = df[(df.user==user) & (df.period=='post')]
        x2, y2, r2 = user_post[x_alias].values[0], user_post.y_scaled.values[0], user_post.radius.values[0]
    
        tangent_equations = find_tangent_equations(x1, y1, r1, x2, y2, r2)
        circle_1_line_intersections = [find_circle_line_intersection(x1, y1, r1, eq[0], eq[1]) for eq in tangent_equations]
        circle_2_line_intersections = [find_circle_line_intersection(x2, y2, r2, eq[0], eq[1]) for eq in tangent_equations]
    
        polygon_points = circle_1_line_intersections + circle_2_line_intersections
        draw_polygon(plt, polygon_points)
    
    # bubble publish
    for _, row in df[df.period=='post'].iterrows():
        x = row[x_alias]
        y = row.y_scaled
        r = row.radius
        label = row.consumer
        circle = patches.Circle((x, y), r, facecolor="#2d699f", edgecolor="none", linewidth=0, zorder=2)
        plt.gca().add_patch(circle)
    
        plt.textual content(x, y - r - 0.3, label, fontsize=12, ha="middle")

    The output appears to be like as anticipated:

    Output, picture by Writer

    Now we wish to add some styling:

    # plot parameters
    plt.subplots(figsize=(10, 10))
    rcParams['font.family'] = 'DejaVu Sans'
    rcParams['font.size'] = 14
    plt.grid(shade="grey", linestyle=(0, (10, 10)), linewidth=0.5, alpha=0.6, zorder=1)
    plt.axvline(x=0, shade="white", linewidth=2)
    plt.gca().set_facecolor('white')
    plt.gcf().set_facecolor('white')
    
    # spines formatting
    plt.gca().spines["top"].set_visible(False)
    plt.gca().spines["right"].set_visible(False)
    plt.gca().spines["bottom"].set_visible(False)
    plt.gca().spines["left"].set_visible(False)
    plt.gca().tick_params(axis="each", which="each", size=0)
    
    # plot labels
    plt.xlabel("Quantity purchases") 
    plt.ylabel("Income, $")
    plt.title("Product customers efficiency", fontsize=18, shade="black")
    
    # axis limits
    axis_lim = df[x_alias].max() * 1.2
    plt.xlim(0, axis_lim)
    plt.ylim(0, axis_lim)

    Pre-post legend in the fitting backside nook to provide viewer a touch, tips on how to learn the chart:

    ## pre-post legend 
    # circle 1
    legend_position, r1 = (11, 2.2), 0.3
    x1, y1 = legend_position[0], legend_position[1]
    circle = patches.Circle((x1, y1), r1, facecolor="#99d8e1", edgecolor="none", linewidth=0, zorder=2)
    plt.gca().add_patch(circle)
    plt.textual content(x1, y1 + r1 + 0.15, 'Pre', fontsize=12, ha="middle", va="middle")
    # circle 2
    x2, y2 = legend_position[0], legend_position[1] - r1*3
    r2 = r1*0.7
    circle = patches.Circle((x2, y2), r2, facecolor="#2d699f", edgecolor="none", linewidth=0, zorder=2)
    plt.gca().add_patch(circle)
    plt.textual content(x2, y2 - r2 - 0.15, 'Put up', fontsize=12, ha="middle", va="middle")
    # tangents
    tangent_equations = find_tangent_equations(x1, y1, r1, x2, y2, r2)
    circle_1_line_intersections = [find_circle_line_intersection(x1, y1, r1, eq[0], eq[1]) for eq in tangent_equations]
    circle_2_line_intersections = [find_circle_line_intersection(x2, y2, r2, eq[0], eq[1]) for eq in tangent_equations]
    polygon_points = circle_1_line_intersections + circle_2_line_intersections
    draw_polygon(plt, polygon_points)
    # small arrow
    plt.annotate('', xytext=(x1, y1), xy=(x2, y1 - r1*2), arrowprops=dict(edgecolor="black", arrowstyle="->", lw=1))
    Including styling and legend, picture by Writer

    And at last bubble-size legend:

    # bubble measurement legend
    legend_areas_original = [150, 50]
    legend_position = (11, 10.2)
    for i in legend_areas_original:
        i_r = area_to_radius(i) * radius_scaler
        circle = plt.Circle((legend_position[0], legend_position[1] + i_r), i_r, shade="black", fill=False, linewidth=0.6, facecolor="none")
        plt.gca().add_patch(circle)
        plt.textual content(legend_position[0], legend_position[1] + 2*i_r, str(i), fontsize=12, ha="middle", va="middle",
                  bbox=dict(facecolor="white", edgecolor="none", boxstyle="spherical,pad=0.1"))
    legend_label_r = area_to_radius(np.max(legend_areas_original)) * radius_scaler
    plt.textual content(legend_position[0], legend_position[1] + 2*legend_label_r + 0.3, 'Exercise, hours', fontsize=12, ha="middle", va="middle")

    Our remaining chart appears to be like like this:

    Including second legend, picture by Writer

    The visualization appears to be like very fashionable and concentrates various data in a compact kind.

    Right here is the total code for the graph:

    import matplotlib.pyplot as plt
    import numpy as np
    import pandas as pd
    import sympy as sp
    from scipy.spatial import ConvexHull
    import math
    from matplotlib import rcParams
    import matplotlib.patches as patches
    
    def check_position_relative_to_line(a, b, x0, y0):
        y_line = a * x0 + b
        
        if y0 > y_line:
            return 1 # line is above the purpose
        elif y0 ", lw=1))
    
    # y axis formatting
    max_y = df[y_alias].max()
    nearest_power_of_10 = 10 ** math.ceil(math.log10(max_y))
    ticks = [round(nearest_power_of_10/5 * i, 2) for i in range(0, 6)]
    yticks_scaled = ticks / df[x_alias].max()
    yticklabels = [str(i) for i in ticks]
    yticklabels[0] = ''
    plt.yticks(yticks_scaled, yticklabels)
    
    plt.savefig("plot_with_white_background.png", bbox_inches="tight", dpi=300)

    Including a time dimension to bubble charts enhances their capability to convey dynamic information adjustments intuitively. By implementing clean transitions between “earlier than” and “after” states, customers can higher perceive developments and comparisons over time.

    Whereas no ready-made options have been accessible, growing a customized method proved each difficult and rewarding, requiring mathematical insights and cautious animation methods. The proposed technique may be simply prolonged to varied datasets, making it a worthwhile software for Data Visualization in enterprise, science, and analytics.



    Source link
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleHacked by Design: Why AI Models Cheat Their Own Teachers & How to Stop It | by Oliver Matthews | Feb, 2025
    Next Article Using AI, Like ChatGPT, Damages Critical Thinking: Study
    FinanceStarGate

    Related Posts

    Artificial Intelligence

    What If I had AI in 2018: Rent the Runway Fulfillment Center Optimization

    June 14, 2025
    Artificial Intelligence

    AI Is Not a Black Box (Relatively Speaking)

    June 13, 2025
    Artificial Intelligence

    Boost Your LLM Output and Design Smarter Prompts: Real Tricks from an AI Engineer’s Toolbox

    June 13, 2025
    Add A Comment

    Comments are closed.

    Top Posts

    Keysource and ADCC are now officially part of the Salute brand following completed acquisition

    April 11, 2025

    The Future of Filmmaking: How Generative AI is Transforming Video Production | by Felix Nguyen | Feb, 2025

    February 17, 2025

    25 ways bloated governments hurt your pocketbook

    February 25, 2025

    Running MLflow Projects on Azure Databricks | by Invisible Guru Jii | Mar, 2025

    March 19, 2025

    Before You Invest, Take These Steps to Build a Strategy That Works

    June 4, 2025
    Categories
    • AI Technology
    • Artificial Intelligence
    • Data Science
    • Finance
    • Machine Learning
    • Passive Income
    Most Popular

    When each human is a line of the dataset | by 侧成峰 | Mar, 2025

    March 24, 2025

    These documents are influencing the DOGE-sphere’s agenda

    February 7, 2025

    Real ID Deadline Brings Long Lines, Scalpers to the DMV

    April 1, 2025
    Our Picks

    Your Business Needs AI—But Not 50 Different Apps

    March 22, 2025

    Entrepreneur Ranked Baya Bar the #1 Açai Bowl Franchise

    March 12, 2025

    DeepSeek: Architettura, Ottimizzazione e Benchmark

    February 5, 2025
    Categories
    • AI Technology
    • Artificial Intelligence
    • Data Science
    • Finance
    • Machine Learning
    • Passive Income
    • Privacy Policy
    • Disclaimer
    • Terms and Conditions
    • About us
    • Contact us
    Copyright © 2025 Financestargate.com All Rights Reserved.

    Type above and press Enter to search. Press Esc to cancel.