2015-09-20 11 views
6

È possibile contare il numero di campi per riga in una virgola/scheda/qualsiasi file di testo delimitato utilizzando utils::count.fields.Come contare il numero di campi non vuoti in un file delimitato?

Ecco un esempio riproducibile:

d <- data.frame(
    x = c(1, NA, 3, NA, 5), 
    y = c(NA, "b", "c", NA, NA), 
    z = c(NA, "beta", "gamma", NA, "epsilon") 
) 

fname <- "test.csv" 
write.csv(d, fname, na = "", row.names = FALSE) 
count.fields(fname, sep = ",") 
## [1] 3 3 3 3 3 3 

voglio calcolare il numero di campi non vuoti per linea. Posso farlo in un modo goffo leggendo tutto e contando il numero di valori che non sono NA.

d2 <- read.csv(fname, na.strings = "") 
rowSums(!is.na(d2)) 
## [1] 1 2 3 0 2 

Mi piacerebbe davvero come un modo di scansione del file (come count.fields) in modo da poter indirizzare specifiche sezioni da leggere in.

C'è un modo migliore di contare il numero di campi non vuoti in un file delimitato?

risposta

6

Questo dovrebbe essere completamente portatile a patto di avere i Rcpp & BH pacchetti installati:

library(Rcpp) 
library(inline) 

csvblanks <- ' 
string data = as<string>(filename); 
ifstream fil(data.c_str()); 
if (!fil.is_open()) return(R_NilValue); 

typedef tokenizer< escaped_list_separator<char> > Tokenizer; 

vector<string> fields; 
vector<int> retval; 
string line; 

while (getline(fil, line)) { 
    int numblanks = 0; 
    Tokenizer tok(line); 
    for(Tokenizer::iterator beg=tok.begin(); beg!=tok.end(); ++beg){ 
    numblanks += (beg->length() == 0) ? 1 : 0 ; 
    }; 
    retval.push_back(numblanks); 
} 
return(wrap(retval)); 
' 

count_blanks <- rcpp(
    signature(filename="character"), 
    body=csvblanks, 
    includes=c("#include <iostream>", 
      "#include <fstream>", 
      "#include <vector>", 
      "#include <string>", 
      "#include <algorithm>", 
      "#include <iterator>", 
      "#include <boost/tokenizer.hpp>", 
      "using namespace Rcpp;", 
      "using namespace std;", 
      "using namespace boost;") 
) 

Una volta che è di provenienza si può chiamare count_blanks(FULLPATH) e restituirà un vettore numerico di conti di campi vuoti per riga.

ho corse contro questo file:

"DATE","APIKEY","FILENAME","LANGUAGE","JOBID","TRANSCRIPT" 
1,2,3,4,5 
1,,3,4,5 
1,2,3,4,5 
1,2,,4,5 
1,2,3,4,5 
1,2,3,,5 
1,2,3,4,5 
1,2,3,4, 
1,2,3,4,5 
1,,3,,5 
1,2,3,4,5 
,2,,4, 
1,2,3,4,5 

via:

count_blanks("/tmp/a.csv") 
## [1] 0 0 1 0 1 0 1 0 1 0 2 0 3 0 

CAVEATS

  • E 'abbastanza ovvio che non è ignorare l'intestazione, in modo che potesse usare un parametro logico header con codice C/C++ associato (che sarà b e piuttosto semplice).
  • Se contate "spazi" (ad esempio [:space:]+) come "vuoti", avrete bisogno di qualcosa di un po 'più complesso della chiamata a length. This è un modo potenziale per affrontarlo se necessario.
  • Sta utilizzando la configurazione predefinita per la funzione Boost escaped_list_separator che è definita here. Che può anche essere personalizzato con citando & caratteri separatori (rendendo possibile simulare ulteriore read.csv/read.table.

Ciò più strettamente avvicinarsi count.fields/C_countfields performance e può eliminare la necessità di utilizzare la memoria leggendo in ogni riga solo per trovare le linee che alla fine vuoi raggiungere in modo più ottimale. Non penso che preallocare lo spazio per il vettore numerico restituito aggiungerà molto alla velocità, ma puoi vedere la discussione here che mostra come farlo se necessario

+0

ho appena realizzato che volevi contare _non-vuoto_, spero che l'inverso di questo sia abbastanza banale non garantire una modifica ;-) – hrbrmstr