iphone-prices

piotrek / iphone-prices

hi there

Hi! What would you like to analyze?

hard to say

We can start with whatever you have: a dataset, a question, or even a rough idea such as finding trends, making a chart, cleaning data, or building a prediction model.

hmm

No problem. Describe the goal in a sentence, or choose one:

  1. Explore and summarize a dataset
  2. Create a chart
  3. Clean data
  4. Build a prediction model
  5. Create a dashboard

/home/piotr/sandbox/mercury-examples/iphone-dashboard load a csv from this path

✓ File activity · Completed · 1 action

Access: Full filesystem

✓ List folder iphone-dashboard completed

/home/piotr/sandbox/mercury-examples/iphone-dashboard

› /home/piotr/sandbox/mercury-examples/iphone-dashboard/iphone_prices.csv
Technical details

list_directory

{"path":"/home/piotr/sandbox/mercury-examples/iphone-dashboard","limit":100}
{"ok": true, "entries": [{"path": "/home/piotr/sandbox/mercury-examples/iphone-dashboard/iphone_prices.csv", "kind": "file"}], "truncated": false, "skipped": 0, "next_offset": null}

I’ll load the selected CSV and show a quick preview.

</>Load iphone prices data
import pandas as pd

df = pd.read_csv("/home/piotr/sandbox/mercury-examples/iphone-dashboard/iphone_prices.csv")

print(f"Shape: {df.shape}")
columns = df.columns.tolist()
print(f"Columns ({len(columns)}):", columns if len(columns) <= 100 else columns[:100] + ["..."])
df.head()
Shape: (55, 27)
Columns (27): ['model', 'release_year', 'screen_size_inches', 'price_usd', 'base_storage_gb', 'advertised_launch_price_usd', 'price_basis', 'notes', 'specification_source_url', 'price_source_url', 'inner_screen_size_inches', 'outer_screen_size_inches', 'form_factor', 'announcement_date', 'scheduled_release_date', 'release_status', 'status_as_of', 'price_source_publisher', 'price_source_type', 'price_source_note', 'price_verification_status', 'price_source_checked_on', 'advertised_launch_price_source_url', 'advertised_price_source_publisher', 'advertised_price_verification_status', 'pricing_conditions_source_url', 'pricing_conditions_source_note']
model release_year screen_size_inches price_usd base_storage_gb advertised_launch_price_usd price_basis notes specification_source_url price_source_url ... price_source_publisher price_source_type price_source_note price_verification_status price_source_checked_on advertised_launch_price_source_url advertised_price_source_publisher advertised_price_verification_status pricing_conditions_source_url pricing_conditions_source_note
0 iPhone (1st generation) 2007 3.5 499.0 4 499 full_device_contract_required Original 4GB model. $499 device price; AT&T/Ci... https://www.apple.com/newsroom/2007/01/09Apple... https://www.apple.com/newsroom/2007/01/09Apple... ... Apple primary_manufacturer 4GB device price; service activation and a con... verified_primary_source 2026-09-14 https://www.apple.com/newsroom/2007/01/09Apple... Apple verified_primary_source NaN NaN
1 iPhone 3G 2008 3.5 NaN 8 199 full_device_price_unverified $199 was the subsidized two-year-contract pric... https://www.apple.com/newsroom/2008/06/09Apple... NaN ... NaN NaN No verified full-device price. The $199 contra... missing_full_device_price 2026-09-14 https://www.apple.com/newsroom/2008/06/09Apple... Apple verified_primary_source NaN NaN
2 iPhone 3GS 2009 3.5 599.0 16 199 launch_era_full_device Full-device launch-era retail price; advertise... https://www.apple.com/newsroom/2009/06/08Apple... https://arstechnica.com/gadgets/2009/06/att-on... ... Ars Technica contemporaneous_reporting 16GB no-commitment price; AT&T activation was ... verified_contemporaneous_reporting 2026-09-14 https://www.apple.com/newsroom/2009/06/08Apple... Apple verified_primary_source NaN NaN
3 iPhone 4 2010 3.5 599.0 16 199 launch_era_full_device Full-device launch-era retail price; advertise... https://www.apple.com/newsroom/2010/06/07Apple... https://osxdaily.com/2010/07/08/iphone-4-price... ... OS X Daily contemporaneous_reporting 16GB no-commitment price, carrier-locked; not ... verified_contemporaneous_reporting 2026-09-14 https://www.apple.com/newsroom/2010/06/07Apple... Apple verified_primary_source NaN NaN
4 iPhone 4S 2011 3.5 649.0 16 199 launch_era_full_device Full-device launch-era retail price; advertise... https://www.apple.com/newsroom/2011/10/04Apple... https://osxdaily.com/2011/10/07/unlocked-iphon... ... OS X Daily contemporaneous_reporting 16GB unlocked price; unlocked sales began in N... verified_contemporaneous_reporting 2026-09-14 https://www.apple.com/newsroom/2011/10/04Apple... Apple verified_primary_source NaN NaN

5 rows × 27 columns

The CSV contains 55 iPhone records with 27 columns. It includes model names, release years, screen sizes, prices in USD, storage capacity, launch pricing, and supporting source/verification fields. The preview shows early models beginning with the 2007 first-generation iPhone.

please do plot prive vs screen size and lpease add labels for each point

This plots entries with both a recorded price and screen size, labeling every point by iPhone model.

</>Plot price vs screen size
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

plot_df = df.dropna(subset=["screen_size_inches", "price_usd"]).copy()

fig, ax = plt.subplots(figsize=(14, 9))
ax.scatter(
    plot_df["screen_size_inches"],
    plot_df["price_usd"],
    s=70,
    color="steelblue",
    edgecolor="white",
    linewidth=0.8,
    alpha=0.9,
)

for _, row in plot_df.iterrows():
    ax.annotate(
        row["model"],
        (row["screen_size_inches"], row["price_usd"]),
        xytext=(5, 5),
        textcoords="offset points",
        fontsize=8,
        alpha=0.9,
    )

ax.set_title("iPhone Price vs. Screen Size")
ax.set_xlabel("Screen size (inches)")
ax.set_ylabel("Price (USD)")
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x:,.0f} USD"))
ax.grid(True, alpha=0.25)

plt.tight_layout()
plt.show()
Notebook output

The chart uses the available screen_size_inches and price_usd values, with each marker labeled by model. Some records may be absent where either value is missing. The table below provides the exact values represented by the labeled points.

</>Display sorted price table
display(
    plot_df[["model", "screen_size_inches", "price_usd"]]
    .sort_values(["screen_size_inches", "price_usd", "model"])
    .rename(columns={
        "model": "Model",
        "screen_size_inches": "Screen size (inches)",
        "price_usd": "Price (USD)",
    })
)
Model Screen size (inches) Price (USD)
0 iPhone (1st generation) 3.5 499.0
2 iPhone 3GS 3.5 599.0
3 iPhone 4 3.5 599.0
4 iPhone 4S 3.5 649.0
12 iPhone SE (1st generation) 4.0 399.0
6 iPhone 5c 4.0 549.0
5 iPhone 5 4.0 649.0
7 iPhone 5s 4.0 649.0
24 iPhone SE (2nd generation) 4.7 399.0
33 iPhone SE (3rd generation) 4.7 429.0
8 iPhone 6 4.7 649.0
10 iPhone 6s 4.7 649.0
13 iPhone 7 4.7 649.0
15 iPhone 8 4.7 699.0
25 iPhone 12 mini 5.4 699.0
29 iPhone 13 mini 5.4 699.0
9 iPhone 6 Plus 5.5 749.0
11 iPhone 6s Plus 5.5 749.0
14 iPhone 7 Plus 5.5 769.0
16 iPhone 8 Plus 5.5 799.0
22 iPhone 11 Pro 5.8 999.0
17 iPhone X 5.8 999.0
18 iPhone XS 5.8 999.0
46 iPhone 16e 6.1 599.0
51 iPhone 17e 6.1 599.0
21 iPhone 11 6.1 699.0
20 iPhone XR 6.1 749.0
26 iPhone 12 6.1 799.0
30 iPhone 13 6.1 799.0
34 iPhone 14 6.1 799.0
38 iPhone 15 6.1 799.0
42 iPhone 16 6.1 799.0
27 iPhone 12 Pro 6.1 999.0
31 iPhone 13 Pro 6.1 999.0
36 iPhone 14 Pro 6.1 999.0
40 iPhone 15 Pro 6.1 999.0
47 iPhone 17 6.3 799.0
44 iPhone 16 Pro 6.3 999.0
49 iPhone 17 Pro 6.3 1099.0
52 iPhone 18 Pro 6.3 1199.0
48 iPhone Air 6.5 999.0
23 iPhone 11 Pro Max 6.5 1099.0
19 iPhone XS Max 6.5 1099.0
35 iPhone 14 Plus 6.7 899.0
39 iPhone 15 Plus 6.7 899.0
43 iPhone 16 Plus 6.7 899.0
28 iPhone 12 Pro Max 6.7 1099.0
32 iPhone 13 Pro Max 6.7 1099.0
37 iPhone 14 Pro Max 6.7 1099.0
41 iPhone 15 Pro Max 6.7 1199.0
45 iPhone 16 Pro Max 6.9 1199.0
50 iPhone 17 Pro Max 6.9 1199.0
53 iPhone 18 Pro Max 6.9 1299.0
54 iPhone Duo 7.6 1999.0

The scatter plot maps iPhone price against screen size and labels each point by model. The plotted values span early 3.5-inch models priced from 499 USD to 649 USD, while later 4.0-inch and 4.7-inch models show several overlapping prices—so labels are especially useful for distinguishing models at the same coordinates. Rows missing either price or screen size were excluded.

nice! but please add larger forn, and please make sure that there is no overlap between labels

This redraws the chart with larger label text and automatically repels labels to avoid overlap.

</>Plot price vs screen size
%pip install -q adjustText

import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
from adjustText import adjust_text

plot_df = df.dropna(subset=["screen_size_inches", "price_usd"]).copy()

fig, ax = plt.subplots(figsize=(16, 10))
ax.scatter(
    plot_df["screen_size_inches"],
    plot_df["price_usd"],
    s=85,
    color="steelblue",
    edgecolor="white",
    linewidth=0.9,
    alpha=0.9,
    zorder=2,
)

texts = [
    ax.text(
        row["screen_size_inches"],
        row["price_usd"],
        row["model"],
        fontsize=10,
        fontweight="medium",
        zorder=3,
    )
    for _, row in plot_df.iterrows()
]

adjust_text(
    texts,
    ax=ax,
    arrowprops=dict(arrowstyle="-", color="gray", alpha=0.65, lw=0.7),
    expand=(1.3, 1.5),
)

ax.set_title("iPhone Price vs. Screen Size", fontsize=18, pad=14)
ax.set_xlabel("Screen size (inches)", fontsize=14)
ax.set_ylabel("Price (USD)", fontsize=14)
ax.tick_params(axis="both", labelsize=12)
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x:,.0f} USD"))
ax.grid(True, alpha=0.25, zorder=1)

plt.tight_layout()
plt.show()
Notebook output

The updated plot uses larger model labels (font size 10 versus 8 previously) and adjustText to reposition labels automatically. Connector lines link displaced labels to their corresponding points, preserving readability where multiple iPhones share the same screen size and price.

larger fonts please

Increase the label and axis fonts further while retaining the non-overlapping label placement.

</>Plot price vs screen size
fig, ax = plt.subplots(figsize=(18, 12))

ax.scatter(
    plot_df["screen_size_inches"],
    plot_df["price_usd"],
    s=115,
    color="steelblue",
    edgecolor="white",
    linewidth=1.0,
    alpha=0.9,
    zorder=2,
)

texts = [
    ax.text(
        row["screen_size_inches"],
        row["price_usd"],
        row["model"],
        fontsize=13,
        fontweight="semibold",
        zorder=3,
    )
    for _, row in plot_df.iterrows()
]

adjust_text(
    texts,
    ax=ax,
    arrowprops=dict(arrowstyle="-", color="gray", alpha=0.65, lw=0.9),
    expand=(1.5, 1.7),
    force_text=(0.8, 1.2),
    force_points=(0.5, 0.8),
)

ax.set_title("iPhone Price vs. Screen Size", fontsize=24, fontweight="bold", pad=18)
ax.set_xlabel("Screen size (inches)", fontsize=18, labelpad=10)
ax.set_ylabel("Price (USD)", fontsize=18, labelpad=10)
ax.tick_params(axis="both", labelsize=15)
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x:,.0f} USD"))
ax.grid(True, alpha=0.25, zorder=1)

plt.tight_layout()
plt.show()
Notebook output

The latest chart uses substantially larger text: model labels are 13 pt, axis labels 18 pt, tick labels 15 pt, and the title 24 pt. The 18 × 12 inch figure also gives adjustText more room to separate the dense labels. The warning only notes that some connector arrows may pass through labels; it does not prevent the larger fonts from rendering.

larger points please, more color, so points are more visuble, and we can make labels as little transparent

Make the points larger and more colorful, while giving labels a light white background so they remain readable without being fully opaque.

</>Plot price vs screen size
import seaborn as sns

fig, ax = plt.subplots(figsize=(18, 12))

palette = sns.color_palette("husl", n_colors=plot_df["screen_size_inches"].nunique())
color_map = dict(zip(sorted(plot_df["screen_size_inches"].unique()), palette))
point_colors = plot_df["screen_size_inches"].map(color_map)

ax.scatter(
    plot_df["screen_size_inches"],
    plot_df["price_usd"],
    s=220,
    c=point_colors,
    edgecolor="white",
    linewidth=1.4,
    alpha=0.95,
    zorder=2,
)

texts = [
    ax.text(
        row["screen_size_inches"],
        row["price_usd"],
        row["model"],
        fontsize=13,
        fontweight="semibold",
        alpha=0.82,
        bbox=dict(facecolor="white", edgecolor="none", alpha=0.55, pad=0.25),
        zorder=3,
    )
    for _, row in plot_df.iterrows()
]

adjust_text(
    texts,
    ax=ax,
    arrowprops=dict(arrowstyle="-", color="gray", alpha=0.5, lw=0.8, shrinkA=8),
    expand=(1.5, 1.7),
    force_text=(0.8, 1.2),
    force_points=(0.5, 0.8),
)

ax.set_title("iPhone Price vs. Screen Size", fontsize=24, fontweight="bold", pad=18)
ax.set_xlabel("Screen size (inches)", fontsize=18, labelpad=10)
ax.set_ylabel("Price (USD)", fontsize=18, labelpad=10)
ax.tick_params(axis="both", labelsize=15)
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x:,.0f} USD"))
ax.grid(True, alpha=0.25, zorder=1)

plt.tight_layout()
plt.show()
Notebook output

The latest figure uses much more visible points: marker size is 220 with white outlines and high opacity. Colors now vary by screen size, so models at different sizes are easier to distinguish. Labels use 82% text opacity with a 55%-opaque white backing, which keeps dense annotations legible while preserving a lighter visual feel. The figure rendered without the earlier arrow-placement warning.

labels more transparent, points a little larger

Increase marker size slightly and reduce both label and label-background opacity.

</>Plot price vs screen size
fig, ax = plt.subplots(figsize=(18, 12))

ax.scatter(
    plot_df["screen_size_inches"],
    plot_df["price_usd"],
    s=260,
    c=point_colors,
    edgecolor="white",
    linewidth=1.4,
    alpha=0.95,
    zorder=2,
)

texts = [
    ax.text(
        row["screen_size_inches"],
        row["price_usd"],
        row["model"],
        fontsize=13,
        fontweight="semibold",
        alpha=0.68,
        bbox=dict(facecolor="white", edgecolor="none", alpha=0.30, pad=0.25),
        zorder=3,
    )
    for _, row in plot_df.iterrows()
]

adjust_text(
    texts,
    ax=ax,
    arrowprops=dict(arrowstyle="-", color="gray", alpha=0.45, lw=0.8, shrinkA=8),
    expand=(1.5, 1.7),
    force_text=(0.8, 1.2),
    force_points=(0.5, 0.8),
)

ax.set_title("iPhone Price vs. Screen Size", fontsize=24, fontweight="bold", pad=18)
ax.set_xlabel("Screen size (inches)", fontsize=18, labelpad=10)
ax.set_ylabel("Price (USD)", fontsize=18, labelpad=10)
ax.tick_params(axis="both", labelsize=15)
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x:,.0f} USD"))
ax.grid(True, alpha=0.25, zorder=1)

plt.tight_layout()
plt.show()
Notebook output

The latest chart increases point area from 220 to 260, making each colored marker slightly more prominent. Label text opacity is reduced to 0.68 and the white label background to 0.30, so labels are noticeably more transparent while remaining readable against the plot.

what is color meaning in the plot?

Colors represent screen size. Each distinct screen_size_inches value is assigned a different color from the Seaborn "husl" palette:

</>Map colors to screen size
palette = sns.color_palette("husl", n_colors=plot_df["screen_size_inches"].nunique())
color_map = dict(zip(sorted(plot_df["screen_size_inches"].unique()), palette))
point_colors = plot_df["screen_size_inches"].map(color_map)

So, models with the same screen size use the same color. However, because screen size is already on the x-axis, the color encoding is redundant. There is currently no legend identifying each color.

Color is assigned from screen_size_inches: each distinct iPhone screen size gets a different hue from Seaborn’s "husl" palette. The same screen-size value therefore uses the same point color.

Because screen size is already encoded by the x-axis, the colors do not add a separate meaning such as model generation or price range; they are redundant and are not explained by a legend.

larger fonts on axes, and pease make markers to the front layer, because sometimes they are behind

This increases axis fonts and draws markers above labels and connector lines.

</>Plot price vs screen size
fig, ax = plt.subplots(figsize=(18, 12))

ax.scatter(
    plot_df["screen_size_inches"],
    plot_df["price_usd"],
    s=260,
    c=point_colors,
    edgecolor="white",
    linewidth=1.4,
    alpha=0.95,
    zorder=5,  # keep markers in the foreground
)

texts = [
    ax.text(
        row["screen_size_inches"],
        row["price_usd"],
        row["model"],
        fontsize=13,
        fontweight="semibold",
        alpha=0.68,
        bbox=dict(facecolor="white", edgecolor="none", alpha=0.30, pad=0.25),
        zorder=3,
    )
    for _, row in plot_df.iterrows()
]

adjust_text(
    texts,
    ax=ax,
    arrowprops=dict(arrowstyle="-", color="gray", alpha=0.45, lw=0.8, shrinkA=8),
    expand=(1.5, 1.7),
    force_text=(0.8, 1.2),
    force_points=(0.5, 0.8),
)

ax.set_title("iPhone Price vs. Screen Size", fontsize=26, fontweight="bold", pad=18)
ax.set_xlabel("Screen size (inches)", fontsize=22, labelpad=12)
ax.set_ylabel("Price (USD)", fontsize=22, labelpad=12)
ax.tick_params(axis="both", labelsize=18)
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x:,.0f} USD"))
ax.grid(True, alpha=0.25, zorder=1)

plt.tight_layout()
plt.show()
Notebook output

The latest figure applies the requested presentation changes:

  • Axis titles use fontsize=22 and tick labels use labelsize=18, making both substantially more readable.
  • Markers use zorder=5, while model labels use zorder=3, connector lines are below that, and the grid uses zorder=1. This keeps the colored markers visibly in front when labels or arrows overlap them.
Made with MLJAR
Explore more conversationsMore from piotrek