Overview

This notebook runs through the process of updating the data in sheet in automated way, such that pivot tables and charts are preserved. The basic steps are:

If this process seems a little more complex than you’d expect, here’s why: Although there are API’s available to update the original sheet directly using a cell aware function, eliminating the need for a second sheet, the API’s are very slow and not practical for many rows (> 1000). The alternative API’s that replace the data are much faster, but do not preserve charts, pivots, or secondary sheets that have been added to the Spreadsheet.

library(googlesheets)
library(dplyr)
src_name <- 'gs_upload_src'
clone_name <- 'gs_upload_clone'

Setup

Authenticate using a cached token if it exists, or skip this chunk below and run this command to authenticate via the browser:

gs_auth(new_user = TRUE, cache = FALSE )

# authenticate and create a simple spread sheet
gs_auth()  #(new_user = TRUE, cache = FALSE ) to force reauthentication
Auto-refreshing stale OAuth token.
gd_user()
          displayName: Dave Hurst
         emailAddress: dhurst79@gmail.com
                 date: 2017-05-26 20:40:12 GMT
         permissionId: 13748610123221591404
         rootFolderId: 0ANLqBVMoMgPvUk9PVA

Create some arbitrary data in a sheet titled gs_upload_src. We’ll also create gs_upload_clone that we’ll use to clone the data using the steps below.

data1 <- data.frame( a = 1:10, b = (1:10)**2) 
ss_src <- gs_new(title = src_name, input = data1)
Sheet "gs_upload_src" created in Google Drive.
Range affected by the update: "R1C1:R11C2"
Worksheet "Sheet1" successfully updated with 22 new value(s).
Worksheet dimensions: 1000 x 26.
# create a spread sheet to import this data 
import_formula <- sprintf('=IMPORTRANGE("%s", "A1:Z1000")', ss_src$sheet_key)
ss_clone <- gs_new(title = clone_name, input = data.frame(import_formula))
Sheet "gs_upload_clone" created in Google Drive.
Range affected by the update: "R1C1:R2C1"
Worksheet "Sheet1" successfully updated with 2 new value(s).
Worksheet dimensions: 1000 x 26.
gs_browse(ss_clone)

Link to original spreadsheet Link to cloned spreadsheet

MANUAL STEPS (give it a few secs to load completely)

1 - Click on the #REF! cell – this should raise dialogue: “You need to connect these sheets.”
2 - Click on “Allow access” – this should result in data1 appearing starting in cell A2
3 - Create a pivot or chart on this data

Now we’ll update the original spread sheet and open it to observe the change.

# write out a .csv file of data to replace original with
data2 <- data.frame( a = 1:15, b = -1 * (1:15)**2)  #any visual change will do
tmp_file <- 'ssdata_tmp.csv'
write.csv(data2, file=tmp_file, row.names = F)
ss <- gs_upload(tmp_file, sheet_title = 'gs_upload_src', overwrite = T)
File uploaded to Google Drive:
ssdata_tmp.csv
As the Google Sheet named:
gs_upload_src
gs_browse(ss)

Note that it can take several minutes for the import to update in the cloned sheet.

Here’s a look at the cloned sheet after the update:

LS0tDQp0aXRsZTogIkF1dG9tYXRlZCBHb29nbGUgU2hlZXRzIHVwZGF0ZXMgZm9yIENoYXJ0cyBhbmQgUGl2b3QgVGFibGVzIg0KYXV0aG9yOiAiRGF2ZSBIdXJzdCINCmRhdGU6ICJNYXkgMjYsIDIwMTciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBPdmVydmlldw0KDQpUaGlzIG5vdGVib29rIHJ1bnMgdGhyb3VnaCB0aGUgcHJvY2VzcyBvZiB1cGRhdGluZyB0aGUgZGF0YSBpbiBzaGVldCBpbiBhdXRvbWF0ZWQgd2F5LCBzdWNoIHRoYXQgcGl2b3QgdGFibGVzIGFuZCBjaGFydHMgYXJlIHByZXNlcnZlZC4gIFRoZSBiYXNpYyBzdGVwcyBhcmU6DQoNCisgQ3JlYXRpbmcgYW4gZXhhbXBsZSBnU2hlZXQNCisgQ3JlYXRlIGEgY2xvbmUgb2YgdGhlIGZpcnN0IHNoZWV0IHRoYXQgdXBkYXRlcyB3aXRoIGNoYW5nZXMNCisgQWRkIGFueSBjaGFydHMgb3IgcGl2b3RzIHRvIHRoZSBjbG9uZQ0KKyBVcGRhdGUgdGhlIG9yaWdpbmFsIHNoZWV0DQoNCklmIHRoaXMgcHJvY2VzcyBzZWVtcyBhIGxpdHRsZSBtb3JlIGNvbXBsZXggdGhhbiB5b3UnZCBleHBlY3QsIGhlcmUncyB3aHk6DQpBbHRob3VnaCB0aGVyZSBhcmUgQVBJJ3MgYXZhaWxhYmxlIHRvIHVwZGF0ZSB0aGUgb3JpZ2luYWwgc2hlZXQgZGlyZWN0bHkgdXNpbmcgYSBjZWxsIGF3YXJlIGZ1bmN0aW9uLCBlbGltaW5hdGluZyB0aGUgbmVlZCBmb3IgYSBzZWNvbmQgc2hlZXQsIHRoZSBBUEkncyBhcmUgdmVyeSBzbG93IGFuZCBub3QgcHJhY3RpY2FsIGZvciBtYW55IHJvd3MgKD4gMTAwMCkuICBUaGUgYWx0ZXJuYXRpdmUgQVBJJ3MgdGhhdCByZXBsYWNlIHRoZSBkYXRhIGFyZSBtdWNoIGZhc3RlciwgYnV0IGRvIG5vdCBwcmVzZXJ2ZSBjaGFydHMsIHBpdm90cywgb3Igc2Vjb25kYXJ5IHNoZWV0cyB0aGF0IGhhdmUgYmVlbiBhZGRlZCB0byB0aGUgU3ByZWFkc2hlZXQuDQoNCmBgYHtyfQ0KbGlicmFyeShnb29nbGVzaGVldHMpDQpsaWJyYXJ5KGRwbHlyKQ0KDQpzcmNfbmFtZSA8LSAnZ3NfdXBsb2FkX3NyYycNCmNsb25lX25hbWUgPC0gJ2dzX3VwbG9hZF9jbG9uZScNCmBgYA0KDQojIyBTZXR1cA0KDQpBdXRoZW50aWNhdGUgdXNpbmcgYSBjYWNoZWQgdG9rZW4gaWYgaXQgZXhpc3RzLCBvciBza2lwIHRoaXMgY2h1bmsgYmVsb3cgYW5kIHJ1biB0aGlzIGNvbW1hbmQgdG8gYXV0aGVudGljYXRlIHZpYSB0aGUgYnJvd3NlcjoNCg0KYGdzX2F1dGgobmV3X3VzZXIgPSBUUlVFLCBjYWNoZSA9IEZBTFNFIClgDQoNCmBgYHtyfQ0KIyBhdXRoZW50aWNhdGUgYW5kIGNyZWF0ZSBhIHNpbXBsZSBzcHJlYWQgc2hlZXQNCmdzX2F1dGgoKSAgIyhuZXdfdXNlciA9IFRSVUUsIGNhY2hlID0gRkFMU0UgKSB0byBmb3JjZSByZWF1dGhlbnRpY2F0aW9uDQpgYGANCmBgYHtyfQ0KZ2RfdXNlcigpDQpgYGANCg0KQ3JlYXRlIHNvbWUgYXJiaXRyYXJ5IGRhdGEgaW4gYSBzaGVldCB0aXRsZWQgYGByIHNyY19uYW1lYGAuICBXZSdsbCBhbHNvIGNyZWF0ZSBgYHIgY2xvbmVfbmFtZWBgIHRoYXQgd2UnbGwgdXNlIHRvIGNsb25lIHRoZSBkYXRhIHVzaW5nIHRoZSBzdGVwcyBiZWxvdy4NCg0KYGBge3J9DQoNCmRhdGExIDwtIGRhdGEuZnJhbWUoIGEgPSAxOjEwLCBiID0gKDE6MTApKioyKSANCnNzX3NyYyA8LSBnc19uZXcodGl0bGUgPSBzcmNfbmFtZSwgaW5wdXQgPSBkYXRhMSkNCg0KIyBjcmVhdGUgYSBzcHJlYWQgc2hlZXQgdG8gaW1wb3J0IHRoaXMgZGF0YSANCmltcG9ydF9mb3JtdWxhIDwtIHNwcmludGYoJz1JTVBPUlRSQU5HRSgiJXMiLCAiQTE6WjEwMDAiKScsIHNzX3NyYyRzaGVldF9rZXkpDQpzc19jbG9uZSA8LSBnc19uZXcodGl0bGUgPSBjbG9uZV9uYW1lLCBpbnB1dCA9IGRhdGEuZnJhbWUoaW1wb3J0X2Zvcm11bGEpKQ0KDQpnc19icm93c2Uoc3NfY2xvbmUpDQpgYGANCg0KW0xpbmsgdG8gb3JpZ2luYWwgc3ByZWFkc2hlZXRdKGByIHNzX3NyYyRicm93c2VyX3VybGApDQpbTGluayB0byBjbG9uZWQgc3ByZWFkc2hlZXRdKGByIHNzX2Nsb25lJGJyb3dzZXJfdXJsYCkNCg0KKipNQU5VQUwgU1RFUFMqKiAoZ2l2ZSBpdCBhIGZldyBzZWNzIHRvIGxvYWQgY29tcGxldGVseSkgIA0KDQoxIC0gIENsaWNrIG9uIHRoZSAjUkVGISBjZWxsIC0tIHRoaXMgc2hvdWxkIHJhaXNlIGRpYWxvZ3VlOiAiWW91IG5lZWQgdG8gY29ubmVjdCB0aGVzZSBzaGVldHMuIiAgDQoyIC0gIENsaWNrIG9uICJBbGxvdyBhY2Nlc3MiIC0tIHRoaXMgc2hvdWxkIHJlc3VsdCBpbiBkYXRhMSBhcHBlYXJpbmcgc3RhcnRpbmcgaW4gY2VsbCBBMiAgDQozIC0gIENyZWF0ZSBhIHBpdm90IG9yIGNoYXJ0IG9uIHRoaXMgZGF0YQ0KDQohW10oZ3NfdXBkYXRlX2NoYXJ0X2V4YW1wbGVfbGluay5QTkcpDQoNCiFbXShnc191cGRhdGVfY2hhcnRfZXhhbXBsZS5QTkcpICANCg0KDQpOb3cgd2UnbGwgdXBkYXRlIHRoZSBvcmlnaW5hbCBzcHJlYWQgc2hlZXQgYW5kIG9wZW4gaXQgdG8gb2JzZXJ2ZSB0aGUgY2hhbmdlLg0KDQpgYGB7cn0NCg0KIyB3cml0ZSBvdXQgYSAuY3N2IGZpbGUgb2YgZGF0YSB0byByZXBsYWNlIG9yaWdpbmFsIHdpdGgNCmRhdGEyIDwtIGRhdGEuZnJhbWUoIGEgPSAxOjE1LCBiID0gLTEgKiAoMToxNSkqKjIpICAjYW55IHZpc3VhbCBjaGFuZ2Ugd2lsbCBkbw0KdG1wX2ZpbGUgPC0gJ3NzZGF0YV90bXAuY3N2Jw0Kd3JpdGUuY3N2KGRhdGEyLCBmaWxlPXRtcF9maWxlLCByb3cubmFtZXMgPSBGKQ0KDQpzcyA8LSBnc191cGxvYWQodG1wX2ZpbGUsIHNoZWV0X3RpdGxlID0gJ2dzX3VwbG9hZF9zcmMnLCBvdmVyd3JpdGUgPSBUKQ0KZ3NfYnJvd3NlKHNzKQ0KYGBgDQoNCk5vdGUgdGhhdCBpdCBjYW4gdGFrZSBzZXZlcmFsIG1pbnV0ZXMgZm9yIHRoZSBpbXBvcnQgdG8gdXBkYXRlIGluIHRoZSBjbG9uZWQgc2hlZXQuDQoNCkhlcmUncyBhIGxvb2sgYXQgdGhlIGNsb25lZCBzaGVldCBhZnRlciB0aGUgdXBkYXRlOiAgDQoNCiFbXShnc191cGRhdGVfY2hhcnRfZXhhbXBsZTIuUE5HKQ0K