Se você tem acompanhado as postagens anteriores no blog do Couchbase, já viu conteúdo sobre o Couchbase Shell última versão. Estou experimentando coisas diferentes e hoje quis ver o que poderia fazer com conjuntos de dados de séries temporais.
Ingerir dados de séries temporais
Navegando no Kaggle, encontrei dados de temperatura por cidade. Eu fiz o download. Ele tem cerca de 500 Mb e isso o torna difícil de manipular como um todo. Mas, é claro, é um arquivo de texto, portanto, podemos facilmente dar uma olhada na estrutura de dados da seguinte forma:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
> aberto GlobalLandTemperaturesByCity (temperaturas globais da terra por cidade).csv | primeiro 10 ╭───┬────────────┬────────────────────┬───────────────────────────────┬───────┬─────────┬──────────┬───────────╮ │ # │ dt │ Temperatura média │ Incerteza da temperatura média │ Cidade │ País │ Latitude │ Longitude │ ├───┼────────────┼────────────────────┼───────────────────────────────┼───────┼─────────┼──────────┼───────────┤ │ 0 │ 1743-11-01 │ 6.07 │ 1.74 │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 1 │ 1743-12-01 │ │ │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 2 │ 1744-01-01 │ │ │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 3 │ 1744-02-01 │ │ │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 4 │ 1744-03-01 │ │ │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 5 │ 1744-04-01 │ 5.79 │ 3.62 │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 6 │ 1744-05-01 │ 10.64 │ 1.28 │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 7 │ 1744-06-01 │ 14.05 │ 1.35 │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 8 │ 1744-07-01 │ 16.08 │ 1.40 │ Århus │ Dinamarca │ 57.05N │ 10.33E │ │ 9 │ 1744-08-01 │ │ │ Århus │ Dinamarca │ 57.05N │ 10.33E │ ╰───┴────────────┴────────────────────┴───────────────────────────────┴───────┴─────────┴──────────┴───────────╯ |
1 2 3 |
> aberto GlobalLandTemperaturesByCity (temperaturas globais da terra por cidade).csv | comprimento 8599212 |
8599212 linhas, serão 8599211 documentos. Meu objetivo final é ver um gráfico de temperatura de várias cidades ao longo dos anos. Para fazer isso, primeiro vou importar tudo para um bucket de importação e, em seguida, transformarei os dados em séries temporais.
Se eu importar isso de forma ingênua, terei um documento por cidade/grupo de meses. Portanto, a chave do meu documento será como Århus:1743-11-01. Digamos que eu queira fazer o upload apenas da primeira linha, ela deverá ter a seguinte aparência:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
> aberto GlobalLandTemperaturesByCity (temperaturas globais da terra por cidade).csv | primeiro 1 | envoltório conteúdo | inserir id { |x| $"($x.content.City):($x.content.dt)" } ╭───┬───────────────────┬──────────────────╮ │ # │ conteúdo │ id │ ├───┼───────────────────┼──────────────────┤ │ 0 │ {registro 7 campos} │ Århus:1743-11-01 │ ╰───┴───────────────────┴──────────────────╯ > aberto GlobalLandTemperaturesByCity (temperaturas globais da terra por cidade).csv | primeiro 1 | envoltório conteúdo | inserir id { |x| $"($x.content.City):($x.content.dt)" } |doc upsert ╭───┬───────────┬─────────┬────────┬──────────┬─────────╮ │ # │ processado │ sucesso │ falha │ falhas │ cluster │ ├───┼───────────┼─────────┼────────┼──────────┼─────────┤ │ 0 │ 1 │ 1 │ 0 │ │ capela │ ╰───┴───────────┴─────────┴────────┴──────────┴─────────╯ |
E agora que eu sei que funciona, vamos pegar tudo e fazer um lote:
1 |
aberto GlobalLandTemperaturesByCity (temperaturas globais da terra por cidade).csv | par-cada -t 5 {|x| envoltório conteúdo | inserir id {$"($x.City):($x.dt)" } } | doc upsert |
Eu criei um série na qual importo o resultado de uma coleção SELECIONAR agregação, obtendo todas as datas como registros de data e hora e todas as Temperatura média como valor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
consulta ' INSERT INTO series(KEY _k, VALUE _v) SELECT a.City _k, {"City": a.City , "ts_start" : MIN(STR_TO_MILLIS(a.dt)), "ts_end" : MAX(STR_TO_MILLIS(a.dt)), "ts_data" : ARRAY_AGG([STR_TO_MILLIS(a.dt), a.AverageTemperature]) } _v FROM import a WHERE a.dt BETWEEN "1700-01-01" E "2020-12-31" GROUP BY a.City; ' INSERIR PARA série(CHAVE _k, VALOR _v) SELECIONAR a.Cidade _k, {"Cidade": a.Cidade , "ts_start" : MIN(STR_TO_MILLIS(a.dt)), "ts_end" : MAX(STR_TO_MILLIS(a.dt)), "ts_data" : ARRAY_AGG([STR_TO_MILLIS(a.dt), a.Temperatura média]) } _v DE importação a ONDE SUBSTR(a.Cidade,0,1) = "Z" E a.dt ENTRE "2010-01-01" E "2020-12-31" GRUPO BY a.Cidade; |
E agora todos os dados estão disponíveis como séries temporais. Digamos que eu queira os dados de Paris, posso usar o comando _timeseries função como esta:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
> deixar ts_ranges = [946684800000,1375315200000]; consulta $"SELECT t._t time, t._v0 `value` FROM series AS d UNNEST _timeseries\(d, {\"ts_ranges\":($ts_ranges)}\) AS t WHERE d.city = \"Paris\" AND \(d.ts_start <= ($ts_ranges.1) AND d.ts_end >= ($ts_ranges.0) \);" | rejeitar agrupamento | para csv tempo,valor 946684800000,3.845 949363200000,6.587000000000001 951868800000,7.872000000000001 954547200000,10.067 957139200000,15.451 959817600000,17.666 962409600000,16.954 965088000000,19.512 967766400000,16.548000000000002 970358400000,11.675999999999998 .... |
Para acelerar o processo, você pode criar o seguinte índice: CREATE INDEX ix1 ON series(City, ts_end, ts_start);
Observe o uso da palavra ts_ranges no início. Você pode reutilizar facilmente esses valores em um modelo Cordas. Eles começam com um $ e as variáveis devem estar entre parênteses, como ($my_variable). Isso também significa que agora você precisa escapar do caractere de parêntese, bem como das aspas duplas.
Plotar séries temporais
Há uma variedade de bibliotecas de plotagem de terminal, aqui estou usando youplot:
1 |
> deixar ts_ranges = [946684800000,1375315200000]; consulta $"SELECT t._t date, t._v0 `value` FROM series AS d UNNEST _timeseries\(d, {\"ts_ranges\":($ts_ranges)}\) AS t WHERE d.city = \"Paris\" AND \(d.ts_start <= ($ts_ranges.1) AND d.ts_end >= ($ts_ranges.0) \);" | rejeitar agrupamento | para csv | uplot linha -d, --xlim 946684800000,1375315200000 --ylim -20,35 --título "temperatura global em Paris" --xlabel data --ylabel temperatura` |
Tudo isso é ótimo, mas o ideal é que eu queira ter várias cidades para compará-las. Todas as colunas CSV adicionais são coletadas automaticamente, desde que você execute linhas do youplot. Fazendo as coisas gradualmente, vamos começar com o suporte de várias cidades na consulta. Portanto, algumas alterações aqui:
-
- agrupamento dos dados por tempo
- adicionando d.city IN ($city) na cláusula where. Isso funciona porque a matriz de cidades é um literal String de uma matriz JSON. Vamos dar uma olhada na resposta como um documento JSON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
> deixar ts_ranges = [946684800000,1375315200000]; deixar cidade = ['"Aba"','"Berlim"','"Londres"','"Paris"']; consulta $"SELECT t._t date, \( ARRAY_AGG\({\"cidade\": d.cidade, \"temporário\":t._v0}\) \) FROM series AS d UNNEST _timeseries\(d, {\"ts_ranges\":($ts_ranges)}\) AS t WHERE d.city IN ($city) AND \(d.ts_start <= ($ts_ranges.1) AND d.ts_end >= ($ts_ranges.0) \) GROUP BY t._t ORDER BY t._t;" | rejeitar agrupamento | primeiro |para json [ { "data": "2000-01-01", "$1": [ { "cidade": "Aba", "temp": 26.985000000000007 }, { "cidade": "Berlim", "temp": 1.3239999999999998 }, { "cidade": "Londres", "temp": 4.6930000000000005 }, { "cidade": "Paris", "temp": 3.845 } ] } ] |
Mas isso não pode se transformar em um CSV, mesmo que você achate tudo dessa forma:
1 2 3 4 5 6 7 |
> deixar ts_ranges = [946684800000,1375315200000]; deixar cidade = ['"Aba"','"Berlim"','"Londres"','"Paris"']; consulta $"SELECT t._t date, \( ARRAY_AGG\({\"cidade\": d.cidade, \"temporário\":t._v0}\) \) FROM series AS d UNNEST _timeseries\(d, {\"ts_ranges\":($ts_ranges)}\) AS t WHERE d.city IN ($city) AND \(d.ts_start <= ($ts_ranges.1) AND d.ts_end >= ($ts_ranges.0) \) GROUP BY t._t ORDER BY t._t;" |rejeitar agrupamento | primeiro |achatar|achatar|para csv data,cidade,temporário 2000-01-01,Aba,26.985000000000007 2000-01-01,Berlim,1.3239999999999998 2000-01-01,Londres,4.6930000000000005 2000-01-01,Paris,3.845 |
Portanto, podemos transformá-lo em um objeto da seguinte forma: Objeto v.city : v.temp FOR v IN ARRAY_AGG({"city": d.city, "temp":t._v0}) when v IS NOT MISSING END
1 2 3 4 5 6 7 8 9 10 11 12 |
> deixar ts_ranges = [946684800000,1375315200000]; deixar cidade = ['"Aba"','"Berlim"','"Londres"','"Paris"']; consulta $"SELECT t._t date, \( Objeto v.cidade : v.temporário PARA v IN ARRAY_AGG\({\"cidade\": d.cidade, \"temporário\":t._v0}\) when v IS NOT MISSING END\) FROM series AS d UNNEST _timeseries\(d, {\"ts_ranges\":($ts_ranges)}\) AS t WHERE d.city IN ($city) AND \(d.ts_start <= ($ts_ranges.1) AND d.ts_end >= ($ts_ranges.0) \) GROUP BY t._t ORDER BY t._t;" |rejeitar agrupamento | primeiro | para json { "data": "2000-01-01", "$1": { "Aba": 26.985000000000007, "Berlim": 1.3239999999999998, "Londres": 4.6930000000000005, "Paris": 3.845 } } |
Que agora pode ser transformado em um CSV:
1 2 3 4 5 |
> deixar ts_ranges = [946684800000,1375315200000]; deixar cidade = ['"Aba"','"Berlim"','"Londres"','"Paris"']; consulta $"SELECT t._t date, \( Objeto v.cidade : v.temporário PARA v IN ARRAY_AGG\({\"cidade\": d.cidade, \"temporário\":t._v0}\) when v IS NOT MISSING END\) FROM series AS d UNNEST _timeseries\(d, {\"ts_ranges\":($ts_ranges)}\) AS t WHERE d.city IN ($city) AND \(d.ts_start <= ($ts_ranges.1) AND d.ts_end >= ($ts_ranges.0) \) GROUP BY t._t ORDER BY t._t;" |rejeitar agrupamento | primeiro | achatar| para csv data,Aba,Berlim,Londres,Paris 2000-01-01,26.985000000000007,1.3239999999999998,4.6930000000000005,3.845 |
E, com isso, estamos prontos para traçar várias linhas:
1 |
> deixar ts_ranges = [946684800000,1375315200000]; deixar cidade = ['"Aba"','"Berlim"','"Londres"','"Paris"']; consulta $"SELECT t._t date, \( Objeto v.cidade : v.temporário PARA v IN ARRAY_AGG\({\"cidade\": d.cidade, \"temporário\":t._v0}\) when v IS NOT MISSING END\) FROM series AS d UNNEST _timeseries\(d, {\"ts_ranges\":($ts_ranges)}\) AS t WHERE d.city IN ($city) AND \(d.ts_start <= ($ts_ranges.1) AND d.ts_end >= ($ts_ranges.0) \) GROUP BY t._t ORDER BY t._t;" |rejeitar agrupamento | achatar | para csv | uplot linhas -H -d, --xlim 946684800000,1375315200000 --ylim -10,50 -w 50 --título "temperatura por mês" --xlabel data --ylabel temperatura |
Para facilitar isso, você pode usar funções. Criar uma .nu arquivo, como temp.nucom o seguinte conteúdo:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def tempGraph [$ts_ranges, $cidades] { cb-env balde cbsh cb-env escopo globaltemp cb-env coleção série deixar consulta = ($"SELECT t._t date, \( Objeto v.cidade : v.temporário PARA v IN ARRAY_AGG\({\"cidade\": d.cidade, \"temporário\":t._v0}\) quando v NÃO ESTIVER FALTANDO FIM\)" + $" FROM series AS d UNNEST _timeseries\(d, {\"ts_ranges\":($ts_ranges)}\) AS t " + $" WHERE d.city IN ($city) " + $" E \(d.ts_start <= ($ts_ranges.1) AND d.ts_end >= ($ts_ranges.0) \)" + $" GROUP BY t._t ORDER BY t._t;" ) deixar csv = consulta $consulta |rejeitar agrupamento | achatar | para csv $csv | uplot linhas -H -d, --xlim 946684800000,1375315200000 --ylim -10,50 -w 50 --título "temperatura por mês" --xlabel data --ylabel temperatura } |
Então, se você o adquirir, ele se tornará muito mais fácil de usar:
1 2 3 |
> fonte ./testingcbshell/temporário.nu > tempGraph [946684800000,1375315200000] ['"Aba"','"Berlim"','"Londres"','"Paris"'] |
Espero que isso tenha lhe dado uma rápida visão geral do suporte a séries temporais do Couchbase, da manipulação de dados do Couchbase Shell e de como você pode usar outros comandos do shell, como youplot para tornar as coisas mais integradas e interessantes. Você poderia facilmente gerar um relatório completo em vários formatos a partir de um Ações do GitHub por exemplo, infinitas possibilidades!
-
- Leia mais postagens sobre Shell do Couchbase.