кот из двух структур: не одинаковые поля

У меня есть несколько файлов csv

а.csv

field_a, field_b
111,     121
112,     122

b.csv

field_a, field_c
211,     231
212,     232

c.csv

field_a, field_b, field_c
311,     321,     331
312,     322,     332

И я хотел бы объединить их

вывод.csv

field_a,field_b,field_c
111,    121,    NA
112,    122,    NA
211,    NA,     231
212,    NA,     232
311,    321,    331
312,    322,    332

Я хотел бы сделать это с октавой.

Что я сделал до сих пор:

a=csv2cell(a.csv)
A=cell2struct(a(2:end,:),a(1,:),1)

и теперь я ищу что-то вроде

слияние (A, B, C) или vertcat (A, B, C)

но я не понял, что все поля в выводе.

С R я сделал это так:

 filelist<-list.files() 
 for (i in 1:length(filelist)) {
  datas[[i]]<-list(as.data.frame(read.csv(filelist[i])))
  merged <- merge(merged,datas[[i]], all=TRUE)}

но цикл for ужасно медленный. Поэтому я ищу возможность объединить их все сразу.


person telemachos    schedule 12.02.2013    source источник
comment
Неэффективный код R часто работает медленно. На самом деле это не операция слияния. Это операция укладки.   -  person IRTFM    schedule 13.02.2013
comment
да, я не знал лучшего способа. У @Arun была идея получше.   -  person telemachos    schedule 14.02.2013


Ответы (3)


rbind.fill из пакета plyr отлично справляется с этим:

require(plyr)
rbind.fill(a,b,c)

#   field_a field_b field_c
# 1     111     121      NA
# 2     112     122      NA
# 3     211      NA     231
# 4     212      NA     232
# 5     311     321     331
# 6     312     322     332
person Arun    schedule 12.02.2013
comment
я думаю, что это работает в целом, но у меня проблемы с объемом памяти, и он выдает ошибку. Придется искать машину получше... - person telemachos; 14.02.2013
comment
Каков размер ваших данных? А какая у тебя память? И вы используете 32-битную версию R? (используйте sessionInfo(), чтобы узнать последнюю часть) - person Arun; 14.02.2013
comment
Если проблемы с памятью являются проблемой, вам следует подумать об использовании базы данных и доступе к данным из нее с помощью пакета sqldf. - person IRTFM; 14.02.2013
comment
@Arun У меня есть 13 файлов *.netcdf, каждый по 44 МБ. Я хотел бы объединить один csv и загрузить этот csv на сервер базы данных (gsn). Мне не нужно объединять их все вместе, но было бы неплохо, если бы они имели одинаковую структуру. В противном случае мне придется каждый раз редактировать файл xml для загрузки. - person telemachos; 14.02.2013
comment
@DWin: у меня есть x86_64-pc-linux-gnu (64-разрядная версия) с 2 ГБ оперативной памяти, WinXP (32-разрядная версия) с 3,49 ГБ. - person telemachos; 14.02.2013

Я не уверен насчет октавы, но в Matlab я бы использовал функции fieldnames и set.

В псевдокоде примерно так:

all_fields = union of fieldnames(a), fieldnames(b) and fieldnames(c)
for each variable:
   missing_fields = setdiff(all_fields,fieldnames)
   add the missing fields
then join
person bdecaf    schedule 12.02.2013
comment
Хорошо, я думаю, что это также может быть хорошим способом получить один и тот же структурщик, но не объединять их (из-за размера памяти). - person telemachos; 14.02.2013
comment
Но это приводит к некоторым другим вопросам: установка нескольких полей одновременно - person telemachos; 14.02.2013

Как я сделал, наконец:

С октавой (MATLAB)

% FileNames=readdir(pwd);
d=dir(pwd);

isDirIdx = [d.isdir];
names = {d.name};
FileNames = names(~isDirIdx);

for ii = 1:numel(FileNames)
  % Load csv to cell
  datas{ii}=csv2cell(FileNames{ii});
  %   Then I convert them to a struct
  Datas{ii}=cell2struct((datas{ii}(2:end,:)),[datas{ii}(1,:)],2);
  try fields=[fields, fieldnames(Datas{ii})'];% fails for the first loop, becauce 'fields' doesn't exist yet
  catch
   fields=[fieldnames(Datas{ii})'];  % create 'fields' in the first loop
  end
  Datalenght(ii)=numel(Datas{ii}(1));
end

cd(startdir)

for jj=1:numel(Datas)
  missing_fields{jj} = setdiff(fields,fieldnames(Datas{jj}));
  for kk=1:numel(missing_fields{jj})
    [Datas{jj}.(missing_fields{jj}{kk})]=deal(NaN);%*zeros(numel(datas{jj}(2:end,1)),1);)
  end
end

Проблема была в том, что я не видел простого способа экспортировать структуру в csv. Поэтому я снова переключаюсь на R. Поскольку у меня недостаточно памяти, я не мог загрузить все файлы в r и экспортировать их как один CSV. Итак, сначала я экспортировал каждый файл netcdf в csv с точно такими же значениями. Затем я объединил их все с помощью команды unix/gnu cat.

R:

# Converts all NetCDF (*.nc) in a folder to ASCII (csv)
# when there are more then one, all csv will have the same fields
# when there is a field missing in one NetCDF file, this scripts adds 'NA' Values

# it saves memory, because there is always only one NetCDF-File in the memory.

# Needs package RNetCDF:
# http://cran.r-project.org/web/packages/RNetCDF/index.html
# load package
library('RNetCDF')

# get list of all files to merge
filelist<-list.files() 

# initialise variable names
varnames_all<-{}
varnames_file<-list(filelist)

n_files<-length(filelist)
n_vars<-rep(NA,n_files) # initialise

# get variables-names of each NetCDF file
for (i in 1:n_files) {
  ncfile<-open.nc(filelist[i]) # open nc file
  print(paste(filelist[i],"opend!"))

  # get number of variable in the NetCDF
  n_vars[i]<-file.inq.nc(ncfile)$nvars 
  varnames="" # initialise and clear

  # read every variable name
  for (j in 0:(n_vars[i]-1)) {
    varnames[j]<-var.inq.nc(ncfile,j)$name
  }
  close.nc(ncfile)
  varnames_file[[i]]<-varnames # add to the list of all files
  varnames_all<-(c(varnames_all,varnames)) # concat to one array
}

varnames_all<-unique(varnames_all)  # take every varname only once
print("Existing variable names:")
print(varnames_all)

#initialise a data.frame for load the NetCDF
datas<-data.frame() 

for (i in 1:length(filelist)) {
  print(filelist[i])
  ncfile<-open.nc(filelist[i]) # open nc file
  print(paste("reading ", filelist[i], "...")) 
  datas<-as.data.frame(read.nc(ncfile))  #import data from ncfile as data frame
  close.nc(ncfile)

  #check witch variables are missing
  missing_vars<-setdiff(varnames_all,colnames(datas))

  # Add missing variables a colums with NA
  datas[missing_vars]<-NA
  print(paste("writing ", filelist[i], " to ", filelist[i],".csv ...", sep=""))

  #reorder colum in the same way as in the array varname_all
  datas<-datas[varnames_all] 

  # Write File
  write.csv(datas,file=paste(filelist[i],".csv", sep=""))

  # clear Memory
  rm(datas)
}

Тогда кошка прямо вперед

#!/bin/bash
# Concatenate csv files, whitch have exactly the same fields  

## Change to the directory, from where the files is executed
path=$PWD
cd $path

if [ $# -gt 0 ]; then
  cd $1
fi

# get a list of all data files
datafile_list=$( ls )
read -a datafile_array <<< $datafile_list
echo "copying files ..."
echo "copying file:" ${datafile_array[0]}

cat < ./${datafile_array[0]} > ../outputCat.csv
for  (( i=1; i<${#datafile_array[@]}; i++))
  do
  echo "copying file" ${datafile_array[$i]}
  cat < ./${datafile_array[$i]} | tail -n+2 >> ../outputCat.csv
done
person telemachos    schedule 15.02.2013