Project-Management
編輯逗號分隔值 (CSV) 文件的策略
當我從事數據分析項目時,我經常將數據存儲在逗號或製表符分隔(CSV、TSV)數據文件中。而數據通常屬於專用的數據庫管理系統。對於我的許多應用程序,這將是過度的事情。
我可以在 Excel(或者可能是另一個電子表格程序)中編輯 CSV 和 TSV 文件。這有以下好處:
- 電子表格使輸入數據變得容易
還有幾個問題:
- 使用 CSV 和 TSV 文件會導致各種警告消息,包括各種功能丟失以及如何僅保存活動工作表等。因此,如果您只想打開文件並進行一些更改,那就很煩人了。
- 它進行了許多“所謂的智能”轉換。例如,如果你輸入 12/3,它會認為你要輸入一個日期。 **更新:**我應該提到日期示例只是眾多示例之一;大多數問題似乎與不適當的轉換有關。特別是,看起來像數字或日期的文本字段會導致問題。
或者,我可以直接在標准文本編輯器中處理文本文件。這樣可以確保我輸入的是記錄的內容。然而,這是一種非常尷尬的數據輸入方式(列不對齊;很難將數據簡單地輸入到多個單元格中;等等)。
問題
- 處理 CSV 或 TSV 數據文件的好策略是什麼?即,什麼策略使輸入和操作數據變得容易,同時確保您輸入的內容實際上被正確解釋?
- 如果您對 R 感到滿意,您可以創建基本的 data.frame,然後在其上使用 fix() 函數來輸入數據。與 #5 一樣,一旦設置了 data.frame,您就可以使用一系列 readLines(n=1) (或其他)來獲取數據、驗證數據,並提供添加下一個的機會排。然後將修復留給 fix()。請參閱下面使用 scan() 實現的示例。
- excel 中的另一個選項會很混亂,但您可以輸入 12/9,然後讓另一列評估 =IFERROR(MONTH(DateEntryCell)/DAY(DataEntryCell),DataEntryCell)。但是,您必須維護 excel 表和 csv 表,並且在編寫 csv 時所有的抱怨都會持續存在。
- 或者,只要您的字段相對較短並且具有一致的長度,常規文本編輯器應該可以很好地為您提供 TSV。完成後,您始終可以將其加載到 Excel 中,並確保每行的列數符合您的預期。
- Emacs 可以在許多平台上使用,並且可能有一些專門用於此的東西,例如http://www.emacswiki.org/emacs/CsvMode。
- 如果你是一個熱心的人,用編程語言快速編寫一些東西來進行數據輸入是微不足道的,數據編輯會困難得多。
- 一個快速的谷歌搜索顯示軟件就是為了這個目的,但似乎沒有免費軟件有什麼好處。
- 這聽起來很瘋狂,但超級用戶的某個人建議在訪問中編輯表格,然後將它們導出為 CSV……這簡直太瘋狂了。
- 當您保存為 .csv 時,它並不會阻止 excel 抱怨,但是您可以在數據輸入字段之前鍵入一個撇號,這使得它在自動格式化方面保持獨立。很好,這(至少在 Office 2007 中)不會在 csv 文件中留下撇號。
更新: 我一直在討論這個問題,因為我也有這個問題。到目前為止,我見過的最好/最簡單的數據輸入解決方案是KillinkCSV。它不是“免費”軟件,它是具有 30 天試用期和合理價格(約 27 美元)的共享軟件。不過,我不確定我在編輯現有 CSV 時有多信任它 - 我給了它一個非常大(並且可能格式正確)的 CSV,但它無法讀取所有行。然而,對於一個相當大(20 MB)的文件來說,它似乎工作得很好,而且較大文件的問題可能是我的用戶錯誤。
示例:
#This function takes a what argument like in scan, #a list with the types to be used, see usage example #at the end of this code block #dataEntry will keep reading in values until #the values it reads in matches what is in #"terminateon". #limitations: Many dataEntry <- function(what,terminateon) { CONTINUE <- TRUE #Make sure we start the loop data <- NULL #Create empty data so that the data.frame can define itself ti <- NULL while(CONTINUE) { ti <- NULL ti <- tryCatch( {as.data.frame(scan(what=what, nlines=1, multi.line=FALSE, comment.char="",quiet=TRUE))}, error=function (e) {print("Error in data entry! Line not stored.") return(NULL)}, warning=function(w) {print("Error in data entry! Line not stored.") return(NULL)}, finally={ti <- NULL} ) #Try getting the data according to the parameters in 'what' one row at a time. if (!is.null(ti)) { if ((ncol(ti)==length(what)) & (nrow(ti)==1)) { data <- rbind(data,ti) #If there wasn't an error, add ti to the previous value } else { print("Too many or not enough values on previous entry.") print("Tail of current data:") print(tail(data)) } } if (!is.null(ti) & all(ti == terminateon)) { CONTINUE <- FALSE data <- data[-c(nrow(data)),] } #if we've recieved the final value we won't continue and the last row is invalid so we remove it } return(data) } dataEntry(list(x=integer(), y=numeric(), z=character()),terminateon=c(999,999,"Z"))