google 表單即時回饋

 

google 表單大幅降低蒐集問卷資料的難度;此外,表單將回應自動彙整成試算表更使分析資料變得非常容易。然而,google 表單缺乏一項重要的功能:即時將結果回饋給填寫者1

讓問卷填寫者能馬上知道結果,可以增加其填寫意願,同時也是負責的態度(在回饋不會造成負面影響的前提下)。當然,這在 google 表單本身的限制下無法達成。以下將介紹如何結合 google 試算表 以及 DataCamp Light,讓任何人都能製作出一個在靜態網頁上運行的平台,使填寫者能在此填寫問卷、查詢結果

實際操作
繼續閱讀下去前,可先至回饋功能示範平台操作看看,比較容易理解下文內容。文章中的說明即是依據此回饋功能示範平台

概觀: 運作邏輯

graph TD; user("使用者") ==>|"1. 填寫"| Form("表單"); Form -.->|"2. 原始資料"|sheet("試算表"); sheet -.->|"3. 問卷回饋(多筆)"|dc; dc("DataCamp") ==>|"4. 問卷回饋(1筆)"| user; style Form fill:#8b64ce; style dc fill:#08A8D0; style user fill:#FFFFFF; style sheet fill:#1FA463, stroke:#898989,stroke-width:3.5px,stroke-dasharray: 5, 5; Form2("表單") -->|"1. 自動產生"|sheet1("表單回應"); sheet1 -->|"2. 連結"|sheet2("運算分析"); sheet2 -->|"3. 連結"|sheet3("結果查找"); dc2("DataCamp") -.->|"4. 讀取資料"|sheet3; style sheet1 fill:#1FA463; style sheet2 fill:#1FA463; style sheet3 fill:#1FA463; style Form2 fill:#8b64ce; style dc2 fill:#08A8D0;

上圖的每個方塊(除了左圖的試算表)名稱,皆對應到回饋功能示範平台背後運作的檔案。圖需要分來看:

  • 左側以使用者觀點為中心,顯示使用者填寫問卷(送出資料)到獲得回饋之間,資料流動的路程。

  • 右側的流程圖,實際上是左圖試算表(表單、DataCamp之間)那格的完整路程,意即資料在 google 試算表間的流動及運算。使用者獲得的回饋即是由這些試算表的運算產生。

回饋功能運作的邏輯其實非常簡單:在問卷送出後,透過 google 試算表處理資料(運算、整理);接著透過 DataCamp light 執行預先寫入的 R 語言,讀取經 google 試算表處理後的資料;最後使用者輸入查詢金鑰(於問卷中填寫),篩選出那筆自己填寫的資料。

以下,說明如何設置問卷回饋系統的各個成分,並使用此資料夾中的檔案說明。檔案間的關係完全對應至上文概觀中的概念圖,亦即問卷回饋平台背後蒐集及運算資料所使用到的檔案。下方的說明,單純閱讀文字會難以理解。若有打算實作,可實際打開資料夾中的檔案以配合閱讀,或甚至完全自己重複文中的步驟。

表單、試算表 設置

這節將設置問卷回饋平台資料蒐集與運算功能,包含 1 個 google 表單(表單)及 3 個 google 試算表(表單回應, 運算分析, 結果查找)。

連結表單至試算表

這項功能使用過 google 表單的人都知道,可參考 google 說明,以下簡單說明:

從雲端硬碟進入到表單後,即會顯示下圖的頁面(需具編輯權限)。注意需於中間白色方塊點選「回覆」,畫面才會如下圖(預設畫面是「問題」)。

接著點選白色方塊右上方的綠色 icon,即會出現 2 個選項:

  • 建立新試算表,並命名。 (預設名稱為「無標題表單 (回應)」)
  • 選取現有的試算表

選擇建立新的試算表。
點選建立後,即會在與表單相同的資料夾中建立試算表,我將其命名為表單回應(即概觀右圖表單回應)。
此後,每當有人填完問卷,表單回應即會自動新增一列(row)資料。

試算表間的連結: IMPORTRANGE

千萬不能編輯表單回應,這可能會破壞收集到的問卷資料。google 試算表有一個很實用的函數IMPORTRANGE,能夠選取一試算表中特定的範圍,將其連結至另一獨立的試算表中(獨立檔案)。因此,每當原先的試算表更新,透過IMPORTRANGE連結的新試算表也會跟著更新。如此,即可在不更動表單回應下,對表單回應的內容進行運算。

若此文關於IMPORTRANGE有描述不清的地方,可參考這篇寫得相當清楚的文章。

IMPORTRANGE("<URL>", "<工作表名稱>!<儲存格範圍>")
  • <URL>: 所欲匯入資料之試算表的網址,在此為表單回應之URL
  • <工作表名稱>: 表單回應只有一個工作表,將其名稱填入這裡。
  • <儲存格範圍>: 儲存格範圍視問卷的題數與筆數而定,其格式為:A1:F9999。大寫字母代表欄位,一個欄位即為問卷上的一題;字母後面的數字是列數,一筆資料(一份問卷)佔有一列(row)。

運算分析試算表

匯入

運算分析中的儲存格A1,我輸入了以下公式:

=IMPORTRANGE("https://docs.google.com/spreadsheets/d/1-eOAbpOZ1aeuNUHo3b0olLTrheq-T-pe2BsRXK-P-mM/edit#gid=579070166", "表單回應 1!A1:E9999")

以匯入表單回應的 A 至 E 欄2

運算公式

我在 G 欄設定公式計算 Q1, Q2, Q3 的分數總合,其中 Q3 是反向計分

時間戳記

由於之後會透過 DataCamp Light 讀取 google 試算表,但其並不支援英文以外的文字,因此需將試算表的格式改為英文

選擇試算表 檔案 > 試算表設定 > 一般:

  • 語言代碼: 美國
  • 時區: (GMT+08:00) Taipei3

更改完試算表語言後,需更改時間戳記的格式4

  1. 選擇時間戳記那欄(在此為 A 欄)
  2. 格式 > 數值 > 日期時間

結果查找試算表

運算分析設置完成之後,需要選擇希望使用者查詢時,能看到的項目:

  1. 時間戳記: A 欄
  2. Token: E 欄
  3. score: G 欄

因此,需將結果查找中的 A、B、C 欄分別對應到運算分析中的 A、E、G 欄。在結果查找的儲存格A1B1C1,分別使用IMPORTRANGE

  1. 儲存格A1
=IMPORTRANGE("https://docs.google.com/spreadsheets/d/1znFpdD_Kt1Jk274l0yD1dGZZyhsh7m1Xji9IYZUigEU/edit#gid=0", "工作表1!A1:A9999")
  1. 儲存格B1
=IMPORTRANGE("https://docs.google.com/spreadsheets/d/1znFpdD_Kt1Jk274l0yD1dGZZyhsh7m1Xji9IYZUigEU/edit#gid=0", "工作表1!E1:E9999")
  1. 儲存格C1
=IMPORTRANGE("https://docs.google.com/spreadsheets/d/1znFpdD_Kt1Jk274l0yD1dGZZyhsh7m1Xji9IYZUigEU/edit#gid=0", "工作表1!G1:G9999")

DataCamp Light 設置

DataCamp 是一個學習資料科學程式語言的線上教學網站,有 R 和 Python 的教學。DataCamp Light 是一個互動式的程式語言輔助教學工具。其能夠鑲嵌在網頁上,讓使用者直接透過網頁學習 R 或 Python。

這裡即透過 DataCamp 執行預先寫入的 R Script,讀取儲存在雲端的結果查找。使用者在 DataCamp Light 輸入的Token是用來篩選資料,以回傳使用者填寫的那筆問卷。

取得試算表權限

DataCamp Light 讀取的是結果查找的內容,因此需將結果查找需將結果查找透過連結分享5至網路:

開啟結果查找,選取右上角藍色按鍵共用即會開啟下圖中的小視窗:

接著,

  1. 選取「知道連結的人均可以檢視」(注意不要選到可以編輯)
  2. 複製連結
  3. 按下方「完成」

完整程式碼

以下是回饋功能示範平台的 DataCamp Light 程式碼(html):

<script src="https://cdn.datacamp.com/datacamp-light-latest.min.js"></script>
<div data-datacamp-exercise data-lang="r">
  <code data-type="pre-exercise-code">
    library(googlesheets)
    data <- gs_read(gs_url("https://docs.google.com/spreadsheets/d/1ufuzTL9VCxdvX1QeFQcMGxYbEMq1ZEWVht3CEDpXBmc/edit?usp=sharing"))
              
    data <- as.data.frame(data)
    colnames(data) <- c("DateTime", "Token", "Score")
    data <- data[which(!is.na(data$DateTime)),]
    score <- function(token) {
      i <- which(data$Token == token)
      data[i,]
    }
  </code>
  <code data-type="sample-code">
    # Put Token in "". Ex: score("abcde123")
    score("Enter_your_Token")
  </code>
</div>

下面將分別說明這些程式碼的意義。

注意
DataCamp Light 僅能正常顯示英文,因此需確定 R Script 以及使用者填入的 Token 皆沒有多位元組字(例如,中文)。

預先執行程式碼

下面為<code data-type="pre-exercise-code">...</code>之間的程式碼
此段程式碼是使用者看不到,但會預先執行的 R Script,其中可分成 3 個部分:讀取資料資料刪減查找函數

library(googlesheets)
data <- gs_read(gs_url("https://docs.google.com/spreadsheets/d/1ufuzTL9VCxdvX1QeFQcMGxYbEMq1ZEWVht3CEDpXBmc/edit?usp=sharing"))

data <- as.data.frame(data)
colnames(data) <- c("DateTime", "Token", "Score")
data <- data[which(!is.na(data$DateTime)),]

score <- function(token) {
  i <- which(data$Token == token)
  data[i,]
}

讀取資料

library(googlesheets)
data <- gs_read(gs_url("https://docs.google.com/spreadsheets/..."))

上面這段程式碼透過雲端讀取結果查找,並將其儲存於變項data。函數內的連結即是上面在設置結果查找的讀取權限時的共用分享連結。

資料刪減

data <- as.data.frame(data)
colnames(data) <- c("DateTime", "Token", "Score")
data <- data[which(!is.na(data$DateTime)),]

上面的程式碼做了 3 件事(1 行 1 件事):

  1. data轉變成 base R 的data.frame。由readr::read_csv讀進來的data.frame會是tibble,而tibble在 DataCamp Light 的 console 印出時,會顯示出幾項對使用者沒用的訊息(這是 R 給資料分析者看的),但傳統的 base R data.frame不會。
  2. 第二行則是將資料中每一個變項的名稱,更改為 DateTime(時間戳記)、Token(Token)、Score(Score)。括號內的名稱是結果查找內的變項名稱。
  3. 第三行是用以刪減多餘的資料。由於在運算結果套用公式時,勢必要為未來的儲存格著想:預先套入公式,才能計算未來產生的資料,因此,讀入的資料大多數都是空白的,僅有Score那欄為 66

查找函數

score <- function(token) {
  i <- which(data$Token == token)
  data[i,]
}

這是 DataCamp Light 在此最關鍵的功能。score()是一函數,讓填寫者透過當初於問卷填寫的 Token ,查詢自己的問卷回饋7。該函數的功能,即是在data中的Token變項,尋找符合使用者輸入的值,並將符合的資料印在 console 上。

顯示程式碼

下面為<code data-type="sample-code">...</code>之間的程式碼,是使用者看的到的 R Script:

# Put Token in "". Ex: score("abcde123")
score("Enter_your_Token")

第一行是註解(同一行中,#之後的內容不會執行),可用來說明。
第二行預先印出score()函數,讓使用者僅需輸入 Token 而不需自行打出函數。

靜態網頁 設置

對於完全沒有概念的人,設置靜態網頁可能會是比較困難的部分,因為多數人對此相當陌生。靜態網頁在此的目的是為了讓 DataCamp Light 的程式碼(即一段 HTML)能夠運行,因此若讀者使用的部落格平台允許自由變更網頁的 html 並且能自由匯入 JS8,則可以忽略此節內容。

GitHub Pages

架設靜態網頁9並非難事,難的是做出漂亮的靜態網頁。然而,網頁越漂亮,其結構通常也更加複雜。如何(短時間)打造美觀的靜態網頁以及基礎 HTML, CSS 的概念並非此文的目的。對於有這些需求的讀者,我推薦 Yihui Xieblogdown

以下提供一個最精簡的例子,由註冊 GitHub 帳號到架設網頁,過程中僅需使用到瀏覽器(GUI),不需用到 Git。

註冊與建立 Repo

  1. 至 https://github.com/ ,填寫註冊資訊(一個 email 僅能註冊一次),並記得去信箱認證。Username 即為之後網站的網址,以下圖為例,minimalghpage.github.io。

  2. 信箱認證後,將自動跳回 GitHub 頁面。之後,基本上不需更動出現之畫面的設定,只要按下一步。最後應會出現下圖,按右上角圖示並選取 Your Profile

    account info

  3. 按下網頁中上方的 Repositories 後應會出現下圖,接著再按下右上方的綠色按鈕 New

  4. 出現下圖後,在 Repository name 輸入<username>.github.io(<username>一定要與當初註冊時填入的 Username 一模一樣),並勾選下方 Initialize this repository with a README。最後按 Create repository

上傳網頁

  1. 下載 Minimal Web Page (下載後需解壓縮。)

  2. 至剛剛建立的 Repository (<username>.github.io),點擊 Upload files (圖中黃色螢光處)。

  3. 進入新畫面後,將index.html, search.html, .nojekyll拖曳上傳,並按下畫面最下方 Commit changes.

  4. 上傳完成後,即可看到下圖。.nojekyll不會顯示出來。

  5. 完成!過 1, 2 分鐘後,即可至<username>.github.io檢視網頁。
  6. 之後若要修改檔案,將修改過後的檔案依相同步驟上傳即可。

Minimal Web Page

Minimal Web Page 裡面有三個檔案:index.html, search.html, .nojekyll

  • index.html: 這是網站的首頁,亦即瀏覽器進入https://<username>.github.io/時所讀取的檔案。此檔案內含 HTML 必要結構,並且匯入 bootstrap 的 CSS 和 JS 以快速製作漂亮的 Button 和 Modal。
  • search.html:這份檔案即為上文 DataCamp light 的完整程式碼,加上一些 HTML 的必要結構以及重新整理頁面的按鈕(Reload)。若需修改其中的 R Script,需用文字編輯器開啟此檔案修改<code>...</code>裡面的內容。
  • .nojekyll: Jekyll 是 GitHub Pages 預設的靜態網頁產生器,能自動將 Markdown 生成.html,對於常寫文章的使用者很方便:不需每次發文都要自己將文章轉為 html 檔。.nojekyll在此的作用是告訴 GitHub Pages 不要使用 Jekyll 產生網頁,因為使用 Jekyll 產生網頁,repository 需符合特定的檔案格式與架構10

R 使用者

會用 Rmarkdown 的人,可直接下載回饋功能示範平台製作網頁(需額外安裝一些 package),不須使用上述資料夾內的檔案。這能省下許多製作網頁(index.html)的時間。

R markdown 是 Markdown 的擴充,其輸出的 HTML 格式已經過簡單的排版,同時也支援 Bootstrap (Minimal Web Page 裡的 HTML 也有匯入 Bootstrap),因此能夠輕易地製作出美觀的網頁。Rmarkdown 可輸出許多格式,其中 html_document 最為簡單。Rmarkdown 的語法(Cheat Sheet)即為 Markdown 語法加上許多額外的功能(透過 R 實現)。

隱私問題

在此需特別提醒問卷填寫者隱私的問題。由於查詢個人的問卷回饋需透過 DataCamp Light,其由雲端讀取之試算表(結果查找)是公開的。縱使網頁表面看不見結果查找的網址,但只要檢視網頁的原始碼(透過瀏覽器的開發人員工具,或至 GitHub 直接下載search.html),即可取得結果查找的網址,並下載整份資料11

以這裡的例子說明,結果查找僅含有 3 欄:時間戳記、Token、分數。這 3 欄是任何人都能看見的內容,其中 Token 是由問卷填寫者直接填寫,因此

在設計問卷時,需於 Token 那題特別提醒填寫者:不能填寫能關聯到個人身份的內容,如學號、e-mail 等

Last updated: Apr 27, 2018
  1. 其實 google 表單確實能即時回饋分數,但僅限測驗模式,有諸多限制,例如,題目僅能為「對」或「錯」,無法處理反向計分的問題,無法使用線性刻度 (linear scale) 計分等。 

  2. 若擔心填答人數超過 9998 人,可設個更大的數字,如E99999。 

  3. 你也可以設置時區,通常依據的是多數問卷填寫者所在位置的時區。這邊設為台北時間。 

  4. 這邊是為了方便之後 R parse 日期格式。 

  5. 這關係到隱私問題,詳見最後一節隱私問題 

  6. 總分(Score) = 空白(Q1) + 空白(Q2) + 6(6 - Q3)。Q3 是反向計分的五點量尺。 

  7. 若有兩筆以上的資料有相同的 Token,score()就會篩選出相同筆數的資料,並將這些資料印在 console 上。此時,可以透過 DateTime 那行來確定填寫時間,以找到自己填寫的那筆資料。 

  8. 我對目前的部落格平台功能相當不熟悉,但就我所知提供此功能的應該不多。DataCamp Light 有提供 WordPress(不是 WordPress.com) 外掛,詳見 DataCamp Light Wordpress Plugin。 

  9. 這裡的靜態網頁是架設在 GitHub 上,代表(1)可以任意修改網頁;(2)網頁的內容(檔案)是完全公開的。因此,相對於其他部落格平台,如 Blogger, 網站管理人的彈性相當大,而且網頁上不會出現廣告。然而,由於檔案是完全公開的,需注意隱私以及版權問題。 

  10. 這是自行在 GitHub Pages 上架設部落格最困難的地方:使用者需對 Jekyll 有一定程度的理解。這同時也是我推薦 blogdown 的原因,其讓使用者略過理解複雜的靜態網頁產生器,而能專心在網頁的內容上。 

  11. 結果查找透過IMPORTRANGE匯入的試算表只要未開放共用連結,仍是安全的。這也是為何即使僅需 2 個(或甚至 1 個)試算表和 DataCamp Light 即可做到問卷回饋,但我仍使用了 3 個試算表。
    (另一原因是考量 google 及 DataCamp Light 的運算資源及時間。縱使我較喜歡,也應該要用 R 語言處理資料,考量到 google 擁有較強大及穩定的運算資源,多數的運算因此交給 google 試算表,而 DataCamp Light 僅用來讀取資料。)