1. Lighthouse

Level 9 · Boss: is it the channel or the customer?

The request

From Catherine Hale, Chief Executive Officer

“The board keeps asking whether we should push customers toward one sales channel because its baskets are bigger. Is basket size really about the channel, or about who the customer is? Numbers, a picture, and one clear sentence.”

Your goal

Step 1 of 4. Set result to a DataFrame with one row per channel and columns channel, business and consumer: the median order value for that channel and segment, rounded to 2, sorted by channel A to Z. Draw it as an annotated heatmap.

  • Use the customers DataFramenot done yet
  • Use the orders DataFramenot done yet
  • Show exactly channel business consumernot done yet

Step 1 · your code

Python

Loading the editor…

    ResultPreview of customers · press Run to see yours
    Preview of customers
    customer_idfirst_namelast_nameemailcity
    1OwenHudsonowen.hudson@icloud.comSan Francisco
    2DouglasMendozadmendoza69@gmail.comAtlanta
    3HenryBrookshbrooks@lakesidecafe.comLos Angeles
    4BrianGrahambgraham@bluecedarhomes.comSeattle

    Mote checks your result

    Press Run. Each row that matches the goal becomes a firefly in your lantern.

    Step 2 · your code

    Python

    Loading the editor…

      ResultPreview of customers · press Run to see yours
      Preview of customers
      customer_idfirst_namelast_nameemailcity
      1OwenHudsonowen.hudson@icloud.comSan Francisco
      2DouglasMendozadmendoza69@gmail.comAtlanta
      3HenryBrookshbrooks@lakesidecafe.comLos Angeles
      4BrianGrahambgraham@bluecedarhomes.comSeattle

      Mote checks your result

      Press Run. Each row that matches the goal becomes a firefly in your lantern.

      Step 3 · your code

      Python

      Loading the editor…

        ResultPreview of customers · press Run to see yours
        Preview of customers
        customer_idfirst_namelast_nameemailcity
        1OwenHudsonowen.hudson@icloud.comSan Francisco
        2DouglasMendozadmendoza69@gmail.comAtlanta
        3HenryBrookshbrooks@lakesidecafe.comLos Angeles
        4BrianGrahambgraham@bluecedarhomes.comSeattle

        Mote checks your result

        Press Run. Each row that matches the goal becomes a firefly in your lantern.

        Step 4 · your code

        Python

        Loading the editor…

          ResultPreview of customers · press Run to see yours
          Preview of customers
          customer_idfirst_namelast_nameemailcity
          1OwenHudsonowen.hudson@icloud.comSan Francisco
          2DouglasMendozadmendoza69@gmail.comAtlanta
          3HenryBrookshbrooks@lakesidecafe.comLos Angeles
          4BrianGrahambgraham@bluecedarhomes.comSeattle

          Mote checks your result

          Press Run. Each row that matches the goal becomes a firefly in your lantern.

          DataFrames

          Loaded for you as pandas DataFrames, plus pd and np.

          customers2,018 rows · 9 columns
          • customer_idinteger
          • first_namevarchar
          • last_namevarchar
          • emailvarchar
          • cityvarchar
          • statevarchar
          • signup_datedate
          • segmentvarchar
          • referral_sourcevarchar
          First 5 rows of customers
          customer_idfirst_namelast_nameemailcitystatesignup_datesegmentreferral_source
          1OwenHudsonowen.hudson@icloud.comSan FranciscoCA2022-06-02consumersearch
          2DouglasMendozadmendoza69@gmail.comAtlantaGA2022-06-02consumerNaN
          3HenryBrookshbrooks@lakesidecafe.comLos AngelesCA2022-06-04businessNaN
          4BrianGrahambgraham@bluecedarhomes.comSeattleWA2022-06-06businesspinterest
          5KyleJimenezkjimenez27@yahoo.comIndianapolisIN2022-06-07consumerNaN
          orders12,000 rows · 7 columns
          • order_idinteger
          • customer_idinteger
          • order_datetimestamp
          • statusvarchar
          • channelvarchar
          • shipping_feedecimal
          • coupon_codevarchar
          First 5 rows of orders
          order_idcustomer_idorder_datestatuschannelshipping_feecoupon_code
          10011252023-01-01 20:51:19completedweb0.00NaN
          10021562023-01-02 07:58:46completedweb0.00NaN
          1003222023-01-02 10:25:27completedmarketplace4.99WELCOME10
          1004482023-01-02 11:21:51completedmarketplace4.99WELCOME10
          1005822023-01-02 11:43:36completedweb0.00WELCOME10
          order_items30,338 rows · 6 columns
          • order_item_idinteger
          • order_idinteger
          • product_idinteger
          • quantityinteger
          • unit_pricedecimal
          • discount_pctdecimal
          First 5 rows of order_items
          order_item_idorder_idproduct_idquantityunit_pricediscount_pct
          11001121931.990.00
          21001154311.990.00
          31002154411.990.00
          410021941149.000.00
          51002193219.990.00
          products120 rows · 8 columns
          • product_idinteger
          • namevarchar
          • categoryvarchar
          • subcategoryvarchar
          • unit_pricedecimal
          • unit_costdecimal
          • launched_ondate
          • is_activeboolean
          First 5 rows of products
          product_idnamecategorysubcategoryunit_priceunit_costlaunched_onis_active
          101Vale Velvet LoveseatFurnitureSofas1659.00NaN2023-09-11true
          102Alder Oak Coffee TableFurnitureTables919.00503.502024-05-16true
          103Orchard Rattan Dining ChairFurnitureChairs789.00429.872023-05-27true
          104Upland Oak BookcaseFurnitureStorage429.00214.702022-12-20true
          105Avery Leather Bed FrameFurnitureBeds449.00232.702021-08-31true
          returns1,673 rows · 5 columns
          • return_idinteger
          • order_item_idinteger
          • returned_ondate
          • reasonvarchar
          • refund_amountdecimal
          First 5 rows of returns
          return_idorder_item_idreturned_onreasonrefund_amount
          1142023-01-10damaged83.69
          2422023-01-10wrong_item19.99
          31052023-01-14wrong_item10.99
          4242023-01-16changed_mind26.99
          51262023-01-16late20.99
          employees60 rows · 8 columns
          • employee_idinteger
          • first_namevarchar
          • last_namevarchar
          • departmentvarchar
          • titlevarchar
          • manager_idinteger
          • hire_datedate
          • salaryinteger
          First 5 rows of employees
          employee_idfirst_namelast_namedepartmenttitlemanager_idhire_datesalary
          1CatherineHaleExecutiveChief Executive OfficerNaN2016-03-01285000
          2RobertKimExecutiveChief Operating Officer12017-01-09235000
          3MeganCollinsSalesHead of Sales12018-04-16172000
          4ChloeGutierrezSalesAccount Executive32020-01-2172000
          5FrankBellSalesAccount Executive32023-05-2173000
          web_sessions40,000 rows · 7 columns
          • session_idinteger
          • customer_idinteger
          • started_attimestamp
          • devicevarchar
          • sourcevarchar
          • pages_viewedinteger
          • convertedboolean
          First 5 rows of web_sessions
          session_idcustomer_idstarted_atdevicesourcepages_viewedconverted
          1NaN2023-01-01 23:02:24mobilepaid1false
          2182023-01-01 15:07:49desktoppaid1false
          3NaN2023-01-01 16:55:46desktopdirect7false
          4NaN2023-01-01 12:43:28mobileorganic3false
          5NaN2023-01-01 13:06:21mobilesocial2false
          marketing_spend3,276 rows · 3 columns
          • spend_datedate
          • channelvarchar
          • amountdecimal
          First 5 rows of marketing_spend
          spend_datechannelamount
          2023-01-01paid492.99
          2023-01-01email37.38
          2023-01-01social200.53
          2023-01-02paid709.13
          2023-01-02email64.13

          The lesson

          Splitting by the second factor

          The board compares channels. But a channel’s average basket depends on who shops there. If business customers preferred one channel, that channel would look better for a reason that has nothing to do with the channel itself. The check is to split by the second factor and see where the differences really are.

          A pivot with channels down and segments across does exactly that, and a heatmap makes it readable:

          grid = per_order.pivot_table(index="channel", columns="segment", values="value", aggfunc="median")
          sns.heatmap(grid, annot=True, fmt=",.0f", cmap="Blues", cbar_kws={"label": "Median order (USD)"})

          Use a sequential palette such as Blues for values that are all positive, light for low and dark for high. Keep the diverging coolwarm for numbers with a sign, like correlations.

          Spread, not only the middle

          A median per cell can hide very different spreads, so draw the boxes too. hue="segment" splits each channel’s box in two:

          sns.boxplot(data=per_order, x="channel", y="value", hue="segment", order=order, showfliers=False)

          showfliers=False hides the outlier dots so the boxes aren’t squashed by a few huge orders. Say so on the axis label, since you’re hiding data from the picture (not from the numbers).

          A dashboard in seaborn

          seaborn functions take an ax= argument, so a grid from plt.subplots holds seaborn charts as easily as matplotlib ones:

          fig, axs = plt.subplots(1, 2, figsize=(13, 4.5))
          sns.heatmap(grid, annot=True, fmt=",.0f", cmap="Blues", ax=axs[0])
          sns.boxplot(data=per_order, x="channel", y="value", hue="segment", showfliers=False, ax=axs[1])
          plt.tight_layout()

          Exporting and writing it up

          Colleagues get the table as a file: to_csv(path, index=False) writes CSV, to_excel an Excel sheet (it needs the openpyxl package), to_parquet a compact typed file for large data. Without a path, to_csv returns the text, which is handy for checking what you’d write.

          Then the sentence. Medians by channel run from $249 on the marketplace to $263 on the web, a difference of about 5%. A business order’s median is about 3.8 times a consumer order’s. What: the channel hardly matters. How much: $249 to $263, against 3.8 times. So what: target by segment.

          Common mistakes

          • Comparing channels without splitting by segment, and crediting the channel for its customers.
          • A diverging palette for all-positive values, which suggests a meaningful midpoint that isn’t there.
          • Writing numbers into the takeaway by hand instead of formatting them from the data.

          Pick what may run on this site. You can change this any time from "Cookie settings" at the bottom of every page.