{"id":19303,"date":"2015-04-21T13:01:47","date_gmt":"2015-04-21T13:01:47","guid":{"rendered":"https:\/\/blog.kaywa.com\/?p=19303"},"modified":"2015-04-21T14:57:02","modified_gmt":"2015-04-21T14:57:02","slug":"working-with-stripe-data-and-r","status":"publish","type":"post","link":"https:\/\/blog.kaywa.com\/?p=19303","title":{"rendered":"Working with Stripe Data and R"},"content":{"rendered":"<p>I recently started working with <a href=\"http:\/\/www.r-project.org\/\" title=\"The R Project for Statistical Computing\">R<\/a>. As we use <a href=\"https:\/\/stripe.com\">Stripe<\/a> as one of our payment providers, I thought it might be interesting to use Stripe&#39;s CSV files and R to answer some of the questions every SaaS startup needs to know.<\/p>\n<h2>Question: How many new, recurring and lost customers do we have per week?<\/h2>\n<p>As we have a weekly meeting where we discuss our numbers, it would be best to get the numbers per week. So I decided to start with the question of how many new and recurring customer we have, as well as how many customers we lose per week.<\/p>\n<p>To start I only wanted to have a table that shows the week and below the numbers for lost, new and recurring customers [1].<\/p>\n<p>In future posts, I plan to show different aspects like financial data or add visual features like plots. <\/p>\n<h2>The CSV file from Stripe<\/h2>\n<p>If you are a Stripe user you can download the payments.csv file directly from <a href=\"https:\/\/dashboard.stripe.com\/payments\">https:\/\/dashboard.stripe.com\/payments<\/a>.<br \/>\nBelow are the columns, or variables in R speak, available in the CSV file:<\/p>\n<pre><code>&gt; names(raw_data)\r\n [1] &quot;id&quot;                         &quot;Description&quot;               \r\n [3] &quot;Created..UTC.&quot;              &quot;Amount&quot;                    \r\n [5] &quot;Amount.Refunded&quot;            &quot;Currency&quot;                  \r\n [7] &quot;Converted.Amount&quot;           &quot;Converted.Amount.Refunded&quot; \r\n [9] &quot;Fee&quot;                        &quot;Tax&quot;                       \r\n[11] &quot;Converted.Currency&quot;         &quot;Mode&quot;                      \r\n[13] &quot;Status&quot;                     &quot;Statement.Descriptor&quot;      \r\n[15] &quot;Customer.ID&quot;                &quot;Customer.Description&quot;      \r\n[17] &quot;Customer.Email&quot;             &quot;Captured&quot;                  \r\n[19] &quot;Card.ID&quot;                    &quot;Card.Last4&quot;                \r\n[21] &quot;Card.Brand&quot;                 &quot;Card.Funding&quot;              \r\n[23] &quot;Card.Exp.Month&quot;             &quot;Card.Exp.Year&quot;             \r\n[25] &quot;Card.Name&quot;                  &quot;Card.Address.Line1&quot;        \r\n[27] &quot;Card.Address.Line2&quot;         &quot;Card.Address.City&quot;         \r\n[29] &quot;Card.Address.State&quot;         &quot;Card.Address.Country&quot;      \r\n[31] &quot;Card.Address.Zip&quot;           &quot;Card.Issue.Country&quot;        \r\n[33] &quot;Card.Fingerprint&quot;           &quot;Card.CVC.Status&quot;           \r\n[35] &quot;Card.AVS.Zip.Status&quot;        &quot;Card.AVS.Line1.Status&quot;     \r\n[37] &quot;Disputed.Amount&quot;            &quot;Dispute.Status&quot;            \r\n[39] &quot;Dispute.Reason&quot;             &quot;Dispute.Date..UTC.&quot;        \r\n[41] &quot;Dispute.Evidence.Due..UTC.&quot; &quot;Invoice.ID&quot;                \r\n[43] &quot;Payment.Source.Type&quot;  \r\n<\/code><\/pre>\n<p>We will only use a few of them for our purpose, but it&#39;s good to see what is available.<br \/>\nBesides the payments.csv, there are also CSV files available for customers and for transfers. We may discuss their respective merit in future posts.<\/p>\n<h2>Installing R and R Studio<\/h2>\n<p>To play with the <a href=\"https:\/\/github.com\/rogerfischer\/stripe_with_R\/blob\/master\/stripe_weekly_new_recurring_lost.R\">R script<\/a> and your CSV file, you need, however, first to <a href=\"http:\/\/stat.ethz.ch\/CRAN\/\">download<\/a> and install R.<br \/>\nI also highly recommend to use <a href=\"http:\/\/www.rstudio.com\/products\/rstudio\/download\/\">R Studio<\/a>.<\/p>\n<h2>Setting your Working Directory<\/h2>\n<p>Once you have R Studio (or R on your Terminal\/Shell) up and running, choose your working directory and set it in R Studio like this:<\/p>\n<pre><code class=\"r\">## &quot;your_working_directory&quot; is a path like &quot;\/Users\/rogerfischer\/R\/kaywa\/stripe_with_R&quot;\r\nsetwd(&quot;your_working_directory&quot;)\r\n<\/code><\/pre>\n<p>If you want to check that you are where you want to be, you can use<\/p>\n<pre><code class=\"r\">getwd()\r\n<\/code><\/pre>\n<p>The <a href=\"https:\/\/github.com\/rogerfischer\/stripe_with_R\/blob\/master\/stripe_weekly_new_recurring_lost.R\">R script<\/a> and your payments.csv file should be in the same working directory if you want to run this &ldquo;as is&rdquo;. <\/p>\n<p>If you don&#39;t use Github yet, you can always copy the <a href=\"https:\/\/github.com\/rogerfischer\/stripe_with_R\/blob\/master\/stripe_weekly_new_recurring_lost.R\">script&#39;s<\/a> content and import it in a file named &ldquo;stripe_weekly_new_recurring_lost.R&rdquo;. Any text editor (Sublime, R Studio) will be fine for this task. <\/p>\n<h2>Running the whole R script or run by code chunk<\/h2>\n<p>You can run the script entirely and you will then find a cvs file in your working directory called &ldquo;new_recurring_lost.csv&rdquo; (which you can import into Excel). This CSV file is bare bones. It starts with the first week of your payments.csv data and ends with the last.<br \/>\nHere is an example how it will look:<\/p>\n<pre><code>14-52 | 15-00 | 15-01 | Year and Week, 14-52 = Week 52, 2014 (Weeks start Sunday, US notation)      \r\n1     | 4     | 4     | Lost Customers      \r\n5     | 11    | 23    | New Customers       \r\n25    | 53    | 88    | Recurring Customers \r\n<\/code><\/pre>\n<p>In the CVS file itself the names &ldquo;year and week&rdquo;, &ldquo;lost&rdquo;, &ldquo;new&rdquo;, &ldquo;recurring\u201c are missing. This is by convention, as further manipulation of your data could screw up because you named the rows.<\/p>\n<p>So don&#39;t write row.names = TRUE as in the documentation about <a href=\"https:\/\/stat.ethz.ch\/R-manual\/R-devel\/library\/utils\/html\/write.table.html\">write.table<\/a>. This will only create a blank column.<br \/>\nIf you absolutely want names, you better transform the table into a wide format data frame (see &quot;wide_df&rdquo; at the end of the article) and then export this to csv. In the R script, I added the option to create a CSV file with column names, based on &ldquo;wide_df&rdquo; too.<\/p>\n<p>I recommend that you run the code file by code chunks as this is more interesting. And will also give you a better understanding of the data. In R Studio you will see in the top right corner above your R script an icon named Run. If you select your code and click on Run, it will only execute the selected part. So let&#39;s start!<\/p>\n<h2>Loading the Data with read.cvs<\/h2>\n<p>At first we load the data into an R object, called a data frame, with this command:<\/p>\n<pre><code class=\"r\">raw_data &lt;- read.csv(&quot;payments.csv&quot;, stringsAsFactors=FALSE)\r\n<\/code><\/pre>\n<p>You can now explore your data. I recommend the following:<\/p>\n<pre><code class=\"r\">dim(raw_data)     # Dimension of the data frame, first the rows, then the columns\r\nstr(raw_data)     # More information about the structure of your data frame\r\nsummary(raw_data) # Gives you a summary by column \r\nhead(raw_data)    # Gives you the first 6 rows\r\ntail(raw_data)    # Gives you the last 6 rows\r\n\r\nView(raw_data)    # See all your data in one big table\r\n<\/code><\/pre>\n<p>Every function gives you some information about your data frame respectively about your payments.csv data. With View(raw_data), you see all your data in tabular form. As there is too much data, we first need to do some cleanup. <\/p>\n<p>In our <a href=\"https:\/\/github.com\/rogerfischer\/stripe_with_R\/blob\/master\/stripe_weekly_new_recurring_lost.R\">R script<\/a> the cleaning, subsetting, renaming, changing the type of variables, so we can count, the sorting continues till line 72. If you are interested to see what kind of value is behind a variable name you can always run it.<br \/>\nHere is an example:<\/p>\n<pre><code class=\"r\">data$period &lt;- format(data$time,&quot;%y-%U&quot;)\r\n## Now you want to know what the value of data$period is. Just use:\r\n&gt; head(data$period, 10)\r\n [1] &quot;15-10&quot; &quot;15-10&quot; &quot;15-10&quot; &quot;15-10&quot; &quot;15-10&quot; &quot;15-10&quot; &quot;15-10&quot; &quot;15-10&quot; &quot;15-10&quot; &quot;15-10&quot;\r\n\r\n## or if you know you don&#39;t have tons of data, try to run it directly\r\ndata$period\r\n<\/code><\/pre>\n<p>This is a good way to check what the code does. The comments in the R script should give you enough guidance.<\/p>\n<h2>Creating the weekly table of lost, new and recurring customers<\/h2>\n<p>If you have selected and run all the code till line 72, all your objects are now loaded in memory.<br \/>\nNow we will build our table which you can directly check up in R Studio&#39;s console<\/p>\n<pre><code class=\"r\">cust_types_by_period &lt;- table(data_f$cust_type, data_f$period)\r\n\r\n## Show table in the console\r\ncust_types_by_period \r\n<\/code><\/pre>\n<h2>Create a new CVS file, called new_recurring_lost.csv<\/h2>\n<p>Create now the CSV file called &ldquo;new_recurring_lost.csv&rdquo; with the function write.table. The CSV file  will be available in your working directory. It contains the table with the weekly data we discussed earlier.<\/p>\n<p>If you don&#39;t want to create this CSV file, you can just outcomment the following:<\/p>\n<pre><code class=\"r\">write.table(user_types_by_period, file = &quot;new_recurring_lost.csv&quot;, row.names = FALSE, sep = &quot;\\t&quot;)\r\n<\/code><\/pre>\n<h2>Addendum: Months instead of weeks as data period<\/h2>\n<p>If you want to look at months rather than weeks, you can just change the format of the data$period.<br \/>\nInstead of<\/p>\n<pre><code class=\"r\">## By weeks\r\n## data$period &lt;- format(data$time,&quot;%y-%U&quot;) \r\n<\/code><\/pre>\n<p>use<\/p>\n<pre><code class=\"r\">## Month as decimal number, otherwise use %b or %B\r\ndata$period &lt;- format(data$time,&quot;%y-%m&quot;) \r\n<\/code><\/pre>\n<h2>Addendum: Creating data frames in the long and wide format<\/h2>\n<p>If you want to store your table data in a new data frame, you can do this by running <\/p>\n<pre><code class=\"r\">long_df &lt;- data.frame(cust_types_by_period)\r\ncolnames(long_df) &lt;- c(&quot;customer_type&quot;, &quot;period&quot;, &quot;count&quot;)\r\n<\/code><\/pre>\n<p>You can see the long format below:<\/p>\n<pre><code class=\"r\">&gt; head(long_df)\r\n  customer_type period count\r\n1          lost  12-43     1\r\n2           new  12-43     4\r\n3     recurring  12-43     1\r\n4          lost  12-44     6\r\n5           new  12-44    17\r\n6     recurring  12-44     1\r\n<\/code><\/pre>\n<p>If you need the wide format looking like this:<\/p>\n<pre><code class=\"r\">&gt; head(wide_df)\r\n  period lost new recurring\r\n1  12-43    1   4         1\r\n2  12-44    6  17         1\r\n3  12-45    0  24         1\r\n4  12-46    2  23         1\r\n5  12-47    0  13         0\r\n6  12-48    0  12        14\r\n<\/code><\/pre>\n<p>you can install the <a href=\"http:\/\/cran.r-project.org\/web\/packages\/reshape2\/index.html\">reshape2 package<\/a> and then load it or just run this code chunk:<\/p>\n<pre><code class=\"r\">install.packages(&quot;reshape2&quot;) # needs only to be installed once\r\nlibrary(reshape2)\r\nwide_df &lt;- dcast(long_df, period ~ customer_type, value.var = &quot;count&quot;)\r\n<\/code><\/pre>\n<p>If you want the column names in your CSV file, as mentioned above, you could now run the following:<\/p>\n<pre><code class=\"r\">write.table(wide_df, file = &quot;wide_df.csv&quot;, row.names = FALSE , col.names = TRUE, sep = &quot;\\t&quot;)\r\n<\/code><\/pre>\n<p>Now the first line uses your columns names: period, lost, new, recurring.<\/p>\n<h2>Addendum: Mean and Media for all weeks<\/h2>\n<p>With the wide format, you are now able to calculate the mean (or average) and median (or middle value) over all your weeks for lost, new and recurring customers. If you have a lot of outliers in your data, I recommend using the median over the mean.<\/p>\n<pre><code class=\"r\">&gt; weekly_medians &lt;- apply(wide_df[, 2:4], 2 , median)\r\n&gt; weekly_medians\r\n     lost       new   recurring \r\n      3.0      76.0        91.0 \r\n<\/code><\/pre>\n<h2>Addendum: Looking at one week only<\/h2>\n<p>If you want to look at a single week, use:<\/p>\n<pre><code class=\"r\">## Subset Week 9, 2015\r\np1509 &lt;- subset(data_f, data_f$period == &quot;15-09&quot;)\r\nView(p1509)\r\n## Subset all lost users in Week 9, 2015\r\np1509_lost &lt;- subset(data_f, data_f$period == &quot;15-09&quot; &amp; data_f$cust_type == &quot;lost&quot;)\r\nView(p1509_lost)\r\n<\/code><\/pre>\n<h2>Conclusion<\/h2>\n<p>Now you can always download your current payments.cvs and rerun the R script and you have your updated numbers. You will always know how many new, lost or recurring customers you have. And you can compare that to your average or median week. <\/p>\n<p>Obviously this is just a first attempt to work with the Stripe data and I plan to improve in any direction. Next up will be the week, seen from its financial side. And then the first visualization of this data.<\/p>\n<p>Many thanks also to <a href=\"http:\/\/blog.kovanovic.info\/\">Vitomir Kovanovic<\/a> who helped me with the R script.<\/p>\n<p>If you have any questions, ideas, improvements, let me know in the comments.<\/p>\n<\/p>\n<hr\/>\n<p>[1]<br \/>\n<strong>Definitions<\/strong>   <\/p>\n<ul>\n<li>new: customer appears for the first time<\/li>\n<li>lost: last appearance of the customer with a failed payment. Lost doesn&#39;t mean the customer has cancelled.<\/li>\n<li>recurring: customers that are neither new nor lost<\/li>\n<\/ul>\n<p><strong>Note<\/strong>: Customers who cancel simply dispappear.<br \/>\nCustomers that are lost can reappear as recurring in the following week when their payment goes through. It makes a lot of sense to check these lost customers in detail.<\/p>\n<p>So far I didn&#39;t find cancelled customers in the CSV files from Stripe. Although there is a column called &ldquo;Cancel.At.Period.End&rdquo; in the customer.cvs with the values &ldquo;false&rdquo; or &ldquo;true&rdquo;, it wasn&#39;t always consistent with our own internal data.<br \/>\nSo I will contact Stripe and keep you updated about this.<\/p>\n<\/p>\n<p>You can also find the same on Github:<br \/>\n<a href=\"https:\/\/github.com\/rogerfischer\/stripe_with_R\/blob\/master\/working_with_stripe_and_R.md\">Article on Github<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I recently started working with R. As we use Stripe as one of our payment providers, I thought it might be interesting to use Stripe&#39;s CSV files and R to answer some of the&#46;&#46;&#46;<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,4],"tags":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=\/wp\/v2\/posts\/19303"}],"collection":[{"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=19303"}],"version-history":[{"count":5,"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=\/wp\/v2\/posts\/19303\/revisions"}],"predecessor-version":[{"id":19308,"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=\/wp\/v2\/posts\/19303\/revisions\/19308"}],"wp:attachment":[{"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=19303"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=19303"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.kaywa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=19303"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}