lassence.net

Teaching pandas to MBA students

--- date: theme: data words: 2,952 ---

About the data

The dataset is Online Retail, from the UCI Machine Learning Repository: all the orders of a UK-based online shop selling gifts, mostly to wholesalers, from December 2010 to December 2011. One row is one product in one invoice, with its quantity, unit price, customer and country. Invoice numbers starting with a C are cancellations, with negative quantities.

It works well for teaching: real data, with its flaws (missing customers, returns, a few odd prices), about a business that everyone understands, and big enough (half a million rows) that a spreadsheet would struggle.

The course ended with a lifetime value model on the same data, which has its own post.

Setup

### Import standard libraries
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

### Setup
sns.set(rc={'figure.figsize':(12,6)})
### Read source file
df = pd.read_excel("Online Retail.xlsx")

### Change column types
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])
df['Quantity'] = df['Quantity'].astype('int')
df['UnitPrice'] = df['UnitPrice'].astype('float')
df['CustomerID'] = df['CustomerID'].astype('str')

### Filter on 1 year of data
df = df.loc[lambda x: x['InvoiceDate'] < '2011-12-01']

Step 1: summary of data

### Look at first rows
df.head()
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 08:26:002.5517850.0United Kingdom
153636571053WHITE METAL LANTERN62010-12-01 08:26:003.3917850.0United Kingdom
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-01 08:26:002.7517850.0United Kingdom
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 08:26:003.3917850.0United Kingdom
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-01 08:26:003.3917850.0United Kingdom
### We can see more rows
df.head(10)
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 08:26:002.5517850.0United Kingdom
153636571053WHITE METAL LANTERN62010-12-01 08:26:003.3917850.0United Kingdom
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-01 08:26:002.7517850.0United Kingdom
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 08:26:003.3917850.0United Kingdom
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-01 08:26:003.3917850.0United Kingdom
553636522752SET 7 BABUSHKA NESTING BOXES22010-12-01 08:26:007.6517850.0United Kingdom
653636521730GLASS STAR FROSTED T-LIGHT HOLDER62010-12-01 08:26:004.2517850.0United Kingdom
753636622633HAND WARMER UNION JACK62010-12-01 08:28:001.8517850.0United Kingdom
853636622632HAND WARMER RED POLKA DOT62010-12-01 08:28:001.8517850.0United Kingdom
953636784879ASSORTED COLOUR BIRD ORNAMENT322010-12-01 08:34:001.6913047.0United Kingdom
### ✅ 1.1. Look at *last* rows
### Hint: use `tail()`
df.tail()
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
516379C57988622197POPCORN HOLDER-12011-11-30 17:39:000.8515676.0United Kingdom
516380C57988623146TRIPLE HOOK ANTIQUE IVORY ROSE-12011-11-30 17:39:003.2915676.0United Kingdom
516381C57988784946ANTIQUE SILVER T-LIGHT GLASS-12011-11-30 17:42:001.2516717.0United Kingdom
516382C5798878504815CM CHRISTMAS GLASS BALL 20 LIGHTS-12011-11-30 17:42:007.9516717.0United Kingdom
516383C57988723490T-LIGHT HOLDER HANGING LOVE BIRD-32011-11-30 17:42:003.7516717.0United Kingdom
### ✅ 1.2. How many rows and columns do we have ?
### Hint: use `shape`
df.shape
(516384, 8)
### ✅ 1.3. Show some useful info about our dataframe
### Hint: use `info()`
df.info()
<class 'pandas.DataFrame'>
RangeIndex: 516384 entries, 0 to 516383
Data columns (total 8 columns):
 #   Column       Non-Null Count   Dtype         
---  ------       --------------   -----         
 0   InvoiceNo    516384 non-null  object        
 1   StockCode    516384 non-null  object        
 2   Description  514945 non-null  object        
 3   Quantity     516384 non-null  int64         
 4   InvoiceDate  516384 non-null  datetime64[us]
 5   UnitPrice    516384 non-null  float64       
 6   CustomerID   389168 non-null  str           
 7   Country      516384 non-null  str           
dtypes: datetime64[us](1), float64(1), int64(1), object(3), str(2)
memory usage: 31.5+ MB
### ✅ 1.4. Show summary statistics for numeric columns
### Hint: use `describe()`
df.describe()
QuantityInvoiceDateUnitPrice
count516384.00516384516384.00
mean9.592011-06-26 22:32:58.1641564.58
min-74215.002010-12-01 08:26:00-11062.06
25%1.002011-03-22 13:54:001.25
50%3.002011-07-11 10:14:002.08
75%10.002011-10-10 10:57:004.13
max74215.002011-11-30 17:42:0038970.00
std156.33nan94.29
### ✅ 1.5. Check for missing (null) data
### Hint: use `isnull()` followed by `sum()`
df.isnull().sum()
0
InvoiceNo0
StockCode0
Description1439
Quantity0
InvoiceDate0
UnitPrice0
CustomerID127216
Country0

Step 2: select data

### Select a single column
df['InvoiceDate']
InvoiceDate
02010-12-01 08:26:00
12010-12-01 08:26:00
22010-12-01 08:26:00
32010-12-01 08:26:00
42010-12-01 08:26:00
......
5163792011-11-30 17:39:00
5163802011-11-30 17:39:00
5163812011-11-30 17:42:00
5163822011-11-30 17:42:00
5163832011-11-30 17:42:00
### ✅ 2.1. Select column `Description`
df['Description']
Description
0WHITE HANGING HEART T-LIGHT HOLDER
1WHITE METAL LANTERN
2CREAM CUPID HEARTS COAT HANGER
3KNITTED UNION FLAG HOT WATER BOTTLE
4RED WOOLLY HOTTIE WHITE HEART.
......
516379POPCORN HOLDER
516380TRIPLE HOOK ANTIQUE IVORY ROSE
516381ANTIQUE SILVER T-LIGHT GLASS
51638215CM CHRISTMAS GLASS BALL 20 LIGHTS
516383T-LIGHT HOLDER HANGING LOVE BIRD
### More verbose way with `.loc[]`
df.loc[:, 'CustomerID']
CustomerID
017850.0
117850.0
217850.0
317850.0
417850.0
......
51637915676.0
51638015676.0
51638116717.0
51638216717.0
51638316717.0
### ✅ 2.2. Select column `Description` with `.loc[]`
df.loc[:, 'Description']
Description
0WHITE HANGING HEART T-LIGHT HOLDER
1WHITE METAL LANTERN
2CREAM CUPID HEARTS COAT HANGER
3KNITTED UNION FLAG HOT WATER BOTTLE
4RED WOOLLY HOTTIE WHITE HEART.
......
516379POPCORN HOLDER
516380TRIPLE HOOK ANTIQUE IVORY ROSE
516381ANTIQUE SILVER T-LIGHT GLASS
51638215CM CHRISTMAS GLASS BALL 20 LIGHTS
516383T-LIGHT HOLDER HANGING LOVE BIRD
### Select multiple columns
df.loc[:, ['CustomerID','Country']]
CustomerIDCountry
017850.0United Kingdom
117850.0United Kingdom
217850.0United Kingdom
317850.0United Kingdom
417850.0United Kingdom
.........
51637915676.0United Kingdom
51638015676.0United Kingdom
51638116717.0United Kingdom
51638216717.0United Kingdom
51638316717.0United Kingdom
### ✅ 2.3. Select columns `StockCode` and `Description`
df.loc[:, ['StockCode', 'Description']]
StockCodeDescription
085123AWHITE HANGING HEART T-LIGHT HOLDER
171053WHITE METAL LANTERN
284406BCREAM CUPID HEARTS COAT HANGER
384029GKNITTED UNION FLAG HOT WATER BOTTLE
484029ERED WOOLLY HOTTIE WHITE HEART.
.........
51637922197POPCORN HOLDER
51638023146TRIPLE HOOK ANTIQUE IVORY ROSE
51638184946ANTIQUE SILVER T-LIGHT GLASS
5163828504815CM CHRISTMAS GLASS BALL 20 LIGHTS
51638323490T-LIGHT HOLDER HANGING LOVE BIRD
### Now let's select also subset of rows
df.loc[:5, ['CustomerID','Country']]
CustomerIDCountry
017850.0United Kingdom
117850.0United Kingdom
217850.0United Kingdom
317850.0United Kingdom
417850.0United Kingdom
517850.0United Kingdom
### ✅ 2.4. Select 10 first rows for columns `StockCode` and `Description`
df.loc[:10, ['StockCode', 'Description']]
StockCodeDescription
085123AWHITE HANGING HEART T-LIGHT HOLDER
171053WHITE METAL LANTERN
284406BCREAM CUPID HEARTS COAT HANGER
384029GKNITTED UNION FLAG HOT WATER BOTTLE
484029ERED WOOLLY HOTTIE WHITE HEART.
.........
621730GLASS STAR FROSTED T-LIGHT HOLDER
722633HAND WARMER UNION JACK
822632HAND WARMER RED POLKA DOT
984879ASSORTED COLOUR BIRD ORNAMENT
1022745POPPY'S PLAYHOUSE BEDROOM
### Filtering is an essential aspect
df.loc[df['StockCode'] == '85123A']
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 08:26:002.5517850.0United Kingdom
4953637385123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 09:02:002.5517850.0United Kingdom
6653637585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 09:32:002.5517850.0United Kingdom
22053639085123AWHITE HANGING HEART T-LIGHT HOLDER642010-12-01 10:19:002.5517511.0United Kingdom
26253639485123AWHITE HANGING HEART T-LIGHT HOLDER322010-12-01 10:39:002.5513408.0United Kingdom
...........................
51365057955885123AWHITE HANGING HEART T-LIGHT HOLDER122011-11-30 11:24:002.9514755.0United Kingdom
51371757955985123AWHITE HANGING HEART T-LIGHT HOLDER32011-11-30 11:30:002.9516764.0United Kingdom
51420357968785123AWHITE HANGING HEART T-LIGHT HOLDER122011-11-30 13:50:002.9514448.0United Kingdom
514940C57975285123AWHITE HANGING HEART T-LIGHT HOLDER-12011-11-30 14:46:002.5513777.0United Kingdom
51635157988585123AWHITE HANGING HEART T-LIGHT HOLDER12011-11-30 17:37:002.9515444.0United Kingdom
### How many rows did we get back ?
df.loc[df['StockCode'] == '85123A'].count()
0
InvoiceNo2245
StockCode2245
Description2245
Quantity2245
InvoiceDate2245
UnitPrice2245
CustomerID2017
Country2245
### ✅ 2.5. Get rows where `StockCode` equals `84029G`
df.loc[df['StockCode'] == '84029G']
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 08:26:003.3917850.0United Kingdom
6153637384029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 09:02:003.3917850.0United Kingdom
7853637584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 09:32:003.3917850.0United Kingdom
23053639084029GKNITTED UNION FLAG HOT WATER BOTTLE242010-12-01 10:19:003.3917511.0United Kingdom
29153639684029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 10:51:003.3917850.0United Kingdom
...........................
51294657952384029GKNITTED UNION FLAG HOT WATER BOTTLE242011-11-30 08:08:003.7515903.0United Kingdom
51361557955784029GKNITTED UNION FLAG HOT WATER BOTTLE82011-11-30 11:18:008.29nanUnited Kingdom
51484657975084029GKNITTED UNION FLAG HOT WATER BOTTLE22011-11-30 14:42:004.2513536.0United Kingdom
51520757977784029GKNITTED UNION FLAG HOT WATER BOTTLE12011-11-30 15:13:008.29nanUnited Kingdom
51610557986784029GKNITTED UNION FLAG HOT WATER BOTTLE42011-11-30 16:41:004.2516265.0United Kingdom
### ✅ 2.6. And count them
df.loc[df['StockCode'] == '84029G'].count()
0
InvoiceNo444
StockCode444
Description443
Quantity444
InvoiceDate444
UnitPrice444
CustomerID307
Country444
### ✅ 2.7. What is the average unit price ?
### Hint: use mean()
df['UnitPrice'].mean()
4.579708732261263
### ✅ 2.8. What are the first and last dates in our table ?
### Hint: use min() and max()
print(df['InvoiceDate'].min())
print(df['InvoiceDate'].max())
2010-12-01 08:26:00
2011-11-30 17:42:00

Step 3: calculate new columns

### Day of the order, without time
df['InvoiceDay'] = df['InvoiceDate'].dt.date
df['InvoiceDay'] = pd.to_datetime(df['InvoiceDay'])
### Week of the order
df['Week'] = df['InvoiceDate'].dt.isocalendar().week
### ✅ 3.1. Add a column named `WeekDay` that gives the day of week
### Hint: use `dt.weekday()`
df['WeekDay'] = df['InvoiceDate'].dt.weekday
### ✅ 3.2. Add a column named `Revenue` that multiplies quantity by unit price
df['Revenue'] = df['UnitPrice'] * df['Quantity']
### ✅ 3.3. Check that it looks OK with `head()`
df.head()
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountryInvoiceDayWeekWeekDayRevenue
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 08:26:002.5517850.0United Kingdom2010-12-0148215.30
153636571053WHITE METAL LANTERN62010-12-01 08:26:003.3917850.0United Kingdom2010-12-0148220.34
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-01 08:26:002.7517850.0United Kingdom2010-12-0148222.00
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 08:26:003.3917850.0United Kingdom2010-12-0148220.34
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-01 08:26:003.3917850.0United Kingdom2010-12-0148220.34

Step 4: grouping by a dimension

### First try to group by date
df_date = df.groupby('InvoiceDay').sum(numeric_only=True)
df_date.head()
InvoiceDayQuantityUnitPriceWeekWeekDayRevenue
2010-12-012681412904.25149184621658635.56
2010-12-02210236815.68101232632746207.28
2010-12-031483011114.09105696880845620.46
2010-12-05163957893.171308001635031383.95
2010-12-062141917678.78190022053860.18
### Let's try again with more appropriate aggregates
df_date = (
    df
    .groupby('InvoiceDay')
    .agg({'InvoiceNo': 'nunique', 'CustomerID': 'nunique', 'Quantity': 'sum', 'Revenue': 'sum'})
    .reset_index()
)
df_date.head()
InvoiceDayInvoiceNoCustomerIDQuantityRevenue
02010-12-01143982681458635.56
12010-12-021671172102346207.28
22010-12-03108551483045620.46
32010-12-0595761639531383.95
42010-12-06133902141953860.18
### ✅ 4.1. Add back the UnitPrice
df_date['UnitPrice'] = df_date['Revenue'] / df_date['Quantity']
df_date.head()
InvoiceDayInvoiceNoCustomerIDQuantityRevenueUnitPrice
02010-12-01143982681458635.562.19
12010-12-021671172102346207.282.20
22010-12-03108551483045620.463.08
32010-12-0595761639531383.951.91
42010-12-06133902141953860.182.51
### ✅ 4.2. Starting with `df`, get the revenue per country
df.groupby('Country').agg({'Revenue': 'sum'})
Revenue
Australia137077.27
Austria9471.12
Bahrain548.40
Belgium39501.53
Brazil1143.60
......
Switzerland56385.35
USA1115.64
United Arab Emirates1902.28
United Kingdom7799070.93
Unspecified4749.79

Step 5: explore data distributions

By date

### ✅ 5.1. What is the day with the greatest revenue ?
### Hint: use sort_values()
df_date.sort_values('Revenue').tail(1)
InvoiceDayInvoiceNoCustomerIDQuantityRevenueUnitPrice
2822011-11-1415210945959112141.112.44
### ✅ 5.2. And the day with the smallest revenue ?
df_date.sort_values('Revenue').head(1)
InvoiceDayInvoiceNoCustomerIDQuantityRevenueUnitPrice
212011-01-05765618435-1566.23-0.08
### ✅ 5.3. How many days have a revenue greather than 75k$ ?
df_date.loc[df_date['Revenue'] > 75000]
InvoiceDayInvoiceNoCustomerIDQuantityRevenueUnitPrice
2352011-09-20775643702109286.212.50
2482011-10-05104824616175244.431.63
2822011-11-1415210945959112141.112.44
2902011-11-231541143735078480.702.10
### Let's plot the revenue by day
df_date.plot.line(x='InvoiceDay', y='Revenue')
Output of the cell above
### And plot the distribution with a histogram
df_date.plot.hist(x='InvoiceDay', y='Revenue', bins=20)
Output of the cell above
### Add the mean
fig, ax = plt.subplots()
df_date.plot.hist(x='InvoiceDay', y='Revenue', bins=20, ax=ax)
ax.axvline(df_date['Revenue'].mean(), color='red')
Output of the cell above

By week

### ✅ 5.4. Group data by week in a dataframe named `df_week`
### Don't forget to recompute the UnitPrice
df_week = (
    df
    .groupby('Week')
    .agg({'InvoiceNo': 'nunique', 'CustomerID': 'nunique', 'Quantity': 'sum', 'Revenue': 'sum'})
    .reset_index()
)
df_week['UnitPrice'] = df_week['Revenue'] / df_week['Quantity']
df_week.head()
WeekInvoiceNoCustomerIDQuantityRevenueUnitPrice
013202367349193720.931.28
1234323585626190994.962.23
2329721467969133782.911.97
3443127469312119136.811.72
4538427067613123642.181.83
### ✅ 5.5. What is the average revenue by week ?
df_week['Revenue'].mean()
182629.01811764707
### ✅ 5.6. Plot the weekly Revenue evolution
df_week.plot.line(x='Week', y='Revenue')
Output of the cell above
### ✅ 5.7. Now plot the weekly number of orders, along with the number of customers
### and the average for each metric
### Hint: use `ax.axhline()`
fig, ax = plt.subplots()
df_week.plot.line(x='Week', y='InvoiceNo', ax=ax)
df_week.plot.line(x='Week', y='CustomerID', ax=ax)
ax.axhline(df_week['InvoiceNo'].mean())
ax.axhline(df_week['CustomerID'].mean(), color='orange')
Output of the cell above
### ✅ 5.8. Add the cumulated revenue in a column named RevenueCtd
### Hint: use `cumsum()`
df_week['RevenueCtd'] = df_week['Revenue'].cumsum()
df_week.head()
WeekInvoiceNoCustomerIDQuantityRevenueUnitPriceRevenueCtd
013202367349193720.931.2893720.93
1234323585626190994.962.23284715.89
2329721467969133782.911.97418498.80
3443127469312119136.811.72537635.61
4538427067613123642.181.83661277.79
### ✅ 5.9. Plot the weekly cumulated revenue in an area plot
### Hint: use `plot.area()`
df_week.plot.area(x='Week', y='RevenueCtd')
Output of the cell above

By day of week and hour of day

### ✅ 5.10. Create a column named `Hour` with the hour of the day
df['Hour'] = df['InvoiceDate'].dt.hour
### ✅ 5.11. Group Revenue by day of week *and* hour of day
df_week_hour = (
    df
    .groupby(['WeekDay', 'Hour'])
    .agg({'Revenue': 'sum'})
)
df_week_hour.head()
Revenue
(0, 7)1494.58
(0, 8)43099.04
(0, 9)155633.37
(0, 10)208740.66
(0, 11)158759.48
### Let's pivot the dataframe
df_week_hour = (
    df_week_hour
    .reset_index()
    .pivot(index='Hour', columns='WeekDay', values='Revenue')
)
df_week_hour
012346
6-497.35
71494.582052.583343.466883.7117234.99
843099.0470126.6741119.7865622.7748765.24
9155633.37182366.7098465.40143908.90151264.35749.65
10208740.66249800.68250020.51286959.61188072.7077987.21
.....................
16111226.87168261.66121594.08171797.1264647.7438798.88
17153024.7949203.7336091.17111876.9352135.69
182039.3917469.980.00106522.25-1577.30
193053.4040388.29373.46
2013031.502905.1822.14
### ✅ 5.12. Plot a heatmap
### Hint: use `sns.heatmap()`
sns.heatmap(df_week_hour)
Output of the cell above

There is no column 5: the shop sends no invoices on Saturdays.

By customers

### ✅ 5.13. Let's group by customers now: number of Orders, Revenue and first date of Order
### Hint: use `min()` to get the first date 
df_clients = (
    df
    .groupby('CustomerID')
    .agg({'InvoiceNo': 'nunique', 'Revenue': 'sum', 'InvoiceDay': 'min'})
)
df_clients.head()
CustomerIDInvoiceNoRevenueInvoiceDay
12346.020.002011-01-18
12347.064085.182010-12-07
12348.041797.242010-12-16
12349.011757.552011-11-21
12350.01334.402011-02-02
### ✅ 5.14. Who are the top 5 biggest spenders ?
df_clients.sort_values('Revenue', ascending=False).head()
CustomerIDInvoiceNoRevenueInvoiceDay
14646.074267761.002010-12-20
18102.059244952.952010-12-07
17450.054185919.772010-12-07
14911.0238125596.252010-12-01
12415.026123725.452011-01-06
### ✅ 5.15. Plot the Revenue distribution of clients in a histogram
df_clients.plot.hist(y='Revenue')
Output of the cell above
### ✅ 5.16. Look at the quartiles of Revenue and save it to `df_clients_quantiles`
### Hint: use `quantiles([0, .25, .5, .75, .9, .95])`
df_clients_quantiles = df_clients['Revenue'].quantile([0, .25, .5, .75, .9, .95])
df_clients_quantiles
Revenue
0.0-4287.63
0.25289.87
0.5634.95
0.751549.50
0.93375.23
0.955456.90
### ✅ 5.17. Now filter client Revenue to be >0 and less than the 95% percentile
### And plot the histogram again
(
    df_clients
    .loc[df_clients['Revenue'] > 0]
    .loc[df_clients['Revenue'] <= df_clients_quantiles[0.95]]
    .plot.hist(y='Revenue')
)
Output of the cell above

Revenue by weekday

### ✅ 5.18. Add a `WeekDay` column to the df_date dataframe
df_date['WeekDay'] = df_date['InvoiceDay'].dt.dayofweek
### Plot revenue by WeekDay with a boxplot
sns.boxplot(data=df_date, x='WeekDay', y='Revenue')
Output of the cell above
### ✅ 5.19. Try with a `sns.boxenplot()`
sns.boxenplot(data=df_date, x='WeekDay', y='Revenue')
Output of the cell above
### ✅ 5.20. And with a `sns.violinplot()`
sns.violinplot(data=df_date, x='WeekDay', y='Revenue')
Output of the cell above

Step 6: models

Estimate trend

### ✅ 6.1. Fit a linear regression on weekly revenue with `sns.regplot()`
sns.regplot(x=list(range(df_week.shape[0])),
            y=df_week['Revenue']
)
Output of the cell above
### ✅ 6.2. Fit a linear regression on weekly *cumulated* revenue
sns.regplot(x=list(range(df_week.shape[0])),
            y=df_week['RevenueCtd']
)
Output of the cell above