Como atualizar três arquivos CSV com base em um quarto arquivo

0

Eu tenho quatro arquivos csv e desejo atualizar um conjunto de três, com base nos valores do quarto arquivo.

  1. file_1 contém nomes.
  2. arquivo_2 seus IDs numéricos.
  3. arquivo_3 contém IDs de arquivo_1 e arquivo_2 , e valores correspondentes a cada par de combinações de ID.
  4. file_4 contém valores de algumas novas combinações de nomes.

O que eu preciso fazer é acrescentar os novos nomes do file_4 a file_1 e file_2 , e criar automaticamente ids para eles. Em seguida, insira os valores em arquivo_3 de acordo com as novas combinações de ID. O problema é bastante simples, como explica o exemplo abaixo, o que complica um pouco é a presença de sub-campos delimitados por vírgulas como "1,2,3" em alguns dos csv s.

Eu preciso usar um script para conseguir isso, embora eu saiba que pode ser mais fácil usar o sql .

file_1

nid,vname
1,name1
2,name2
3,name3

arquivo_2

did,dname
1,"s1,s2,s3"
2,s4
3,"s5,s6"

file_3

nid,did,value
1,1,aa
1,2,gg
1,3,tt
2,1,aa
2,2,ag
2,3,at
3,1,aa
3,2,tt

file_4

new_name,new_dataset,value
name1,"s7,s8",aa
name2,"s9,s10",gg
name8,"s1,s2,s3",aa

Assim, os três arquivos atualizados devem se parecer com:

file_1_updated

nid,vname
1,name1
2,name2
3,name3
4,name8

file_2_updated

did,dname
1,"s1,s2,s3"
2,s4
3,"s5,s6"
4,"s7,s8"
5,"s9,s10"

file_3_updated

nid,did,value
1,1,aa
1,2,gg
1,3,tt
1,4,aa
2,1,aa
2,2,ag
2,3,at
2,5,gg
3,1,aa
3,2,tt
4,1,aa
    
por Hia Sen 26.10.2014 / 21:35

2 respostas

3

Veja o que você poderia fazer no bash simples, assumindo que os dados são exatamente como você postou. ( Warning : modifica os arquivos no local. Tenha o cuidado de fazer backups antes do teste.)

Algumas funções para gerenciar os dois primeiros arquivos:

next_id() {
  file="$1"
  # assumes file is sorted by id
  echo $(( $(tail -n 1 $file|cut -d, -f1) + 1 ))
}

Assumindo que arquivo1 e arquivo2 estejam classificados na coluna id, isso pega a primeira parte da última linha e incrementa-a em um, gerando o próximo id.

find_or_create_id() {
  file="$1"
  item="$2"
  # check if we already have that item
  id=$(grep -m 1 ",$item$" "$file" 2> /dev/null)
  if [[ $? -ne 0 ]] ; then
    # generate the next id, append
    id=$(next_id "$file")
    echo "$id,$item" >> "$file"
  else
    # got it already
    id=${id/,*}
  fi
  echo "$id"
}

Isso procura um item (vname ou dname) em um dos dois primeiros arquivos. Se for encontrado, retorne o ID existente. Caso contrário, gere o próximo id e armazene-o de volta no arquivo.

A parte principal é bem simples quando você tem as substrings certas:

while read line ; do
  col1=${line/,*}  # everything up to first ,
  col3=${line//*,} # everything after last ,
  col2=${line%,*}  # everything after first ,
  col2=${col2#*,}  # everything before last ,
  id1=$(find_or_create_id file1 "$col1")
  id2=$(find_or_create_id file2 "$col2")
  # don't insert duplicates
  if ! grep -m 1 -q "^$id1,$id2," file3 ; then
    echo "$id1,$id2,$col3" >> file3
  fi
done < <(tail -n +2 file4)

Isso não será inserido no último arquivo na ordem, e as novas linhas serão adicionadas no final.

Dito isto, se algum desses arquivos não for de tamanho trivial, um banco de dados seria apropriado. Olhe para o SQLite se você não quiser um servidor de banco de dados.

Supondo que você não se importa com ids sequenciais (apenas que eles são distintos), e você colocou integer primary key autoincrement ids para a tabela 1 e a tabela 2 (além de chaves exclusivas em vname e dname), a atualização seria como (provavelmente existem maneiras mais sutis do que a abordagem insert or ignore ):

insert or ignore into tab1(vname) select distinct vname from tab4;
insert or ignore into tab2(dname) select distinct dname from tab4;

insert or ignore into tab3(id1,id2,value)
  select tab1.id, tab2.id, tab4.value
  from tab4
  left join tab1 on tab1.vname = tab4.vname
  left join tab2 on tab2.dname = tab4.dname;

SQLite pode lidar com o " em seu arquivo muito bem.

.separator ,
.import fileX tabX

faz o Right Thing ™, pelo menos com as amostras que você tem lá.

Esquema simples:

create table tab1 (id integer primary key autoincrement, vname text);
create unique index tab1_vname on tab1(vname);

create table tab2 (id integer primary key autoincrement, dname text);
create unique index tab2_dname on tab2(dname);

create table tab3 (id1 int, id2 int, value text,
                   constraint tab3_pk primary key(id1, id2));

create table tab4 (vname text, dname text, value text);
    
por 27.10.2014 / 07:24
0

Aqui estão 2/3 da resposta, usando as ferramentas de software * nix . file_1_updated :

head -n 1 file_1 ; \
{ tail -n +2 file_1 | cut -d ',' -f 2 ; \
  tail -n +2 file_4 | cut -d ',' -f 1 ; } | \
sort -n | uniq | nl -s ',' | tr -d ' '

Saída:

nid,vname
1,name1
2,name2
3,name3
4,name8

file_2_updated :

head -n 1 file_2 ; \
{ tail -n +2 file_2 | cut -d ',' -f 2- ; \
  tail -n +2 file_4 | cut -d ',' -f 2- | \
  rev | cut -d ',' -f 2- | rev ; } | \
sort -n | uniq | nl -s ',' | tr -d ' '

Saída:

did,dname
1,"s1,s2,s3"
2,s4
3,"s5,s6"
4,"s7,s8"
5,"s9,s10"
    
por 28.06.2016 / 05:32