Gráfico de série temporal empilhada

Oct 21 2020

Tenho várias séries temporais medidas em unidades diferentes. Quero ter um gráfico empilhado de meus dados com um eixo x comum. Um exemplo estaria aqui . Para ilustração, aqui estão meus dados.

nobs = 100;
TS1= RandomVariate[NormalDistribution[0, 1], nobs];
TS2= Accumulate[RandomVariate[NormalDistribution[0, 5], nobs]];
TS3= RandomInteger[{-10, 100}, nobs];
dates = DateRange[DatePlus[Today, Quantity[-nobs + 1, "Days"]], Today];
mytsdata = TimeSeries[#, {dates}] & /@ {TS1, TS2, TS3};
mergeddata = TimeSeriesThread[# &, mytsdata];

Posso usar DateListPlotou StackedDateListPlotpara traçar meu conjunto de dados mytsdataou, mergeddatamas nenhum desses gráficos serve aos meus interesses. Eu criei um gráfico usando o MATLAB e é assim.

É possível criar um gráfico semelhante usando códigos Ma? A flexibilidade para alterar as propriedades do gráfico agregaria muito valor. O principal requisito é que deve haver um e apenas um eixo x. As séries temporais podem ter durações diferentes.

Atualização: Aqui está uma lista de várias séries temporais com durações diferentes.

nobs = 100;
dates = DateRange[DatePlus[Today, Quantity[-nobs + 1, "Days"]], Today];
s1 = RandomVariate[NormalDistribution[0, 1], nobs];
s1ts = TimeSeries[s1, {dates}];
s2 = Accumulate[RandomVariate[NormalDistribution[0, 5], nobs - 25]];
s2ts = TimeSeries[s2, {dates[[26 ;;]]}];
s3 = RandomInteger[{-10, 100}, nobs - 50];
s3ts = TimeSeries[s3, {dates[[51 ;;]]}];
mergeddata2 = 
  TimeSeriesThread[# &, {s1ts, s2ts, s3ts}, 
   ResamplingMethod -> Missing[]];

Eu quero traçar mergeddata2o seguinte.

Tempo e 2020 na parte inferior do gráfico não são necessários. Agradeço antecipadamente.

Atualização 2:

Incorporando a sugestão de Rohit, consegui gerar meu gráfico com os seguintes códigos:

drange = {mergeddata2["Dates"][[1]], mergeddata2["Dates"][[-1]]};
ResourceFunction["PlotGrid"][{
  {DateListPlot[s1ts, Frame -> True, 
    PlotRange -> {drange, Automatic}]},
  {DateListPlot[s2ts, Frame -> True, 
    PlotRange -> {drange, Automatic}]},
  {DateListPlot[s3ts, Frame -> True, PlotRange -> {drange, Automatic}]}
  }
 ]

Quaisquer sugestões para melhorias adicionais serão bem-vindas.

Respostas

6 RohitNamjoshi Oct 21 2020 at 20:02

A função de recurso PlotGrid possui várias opções para controlar o layout e os eixos.

ResourceFunction["PlotGrid"][mytsdata // Map[DateListPlot /* List]]

4 Jagra Oct 21 2020 at 19:39

Isso lhe dá a maior parte do que você deseja.

Dito isso, editei esta postagem algumas vezes para chegar à solução atual.

Grid[{
{"TS1",DateListPlot[mytsdata[[1]], ImageSize -> 350, AspectRatio -> 1/2, ImagePadding -> {{25, 1}, {0, 0}}]},
{"TS2", DateListPlot[mytsdata[[2]], ImageSize -> 350, AspectRatio -> 1/2, ImagePadding -> {{25, 1}, {0, 0}}]},
{"TS3", DateListPlot[mytsdata[[3]], ImageSize -> 350, AspectRatio -> 1/2, ImagePadding -> {{25, 1}, {15, 0}}]},
{"", "TIME               2020"}
},
Alignment -> {Right, Left}]

Algumas explicações seguem ...

O Mathematica, pelo menos que eu saiba, não permite que você faça o que deseja de maneira direta.

Minha solução usa Gridpara replicar o saque de grafos que você deseja.

Como você pode ver, Gridtem 3 DateListPlots.

Eu uso ImagePaddingpara alinhar os eixos verticais esquerdos das plotagens. Não encontrei uma maneira automática de fazer isso. Talvez outra pessoa tenha uma sugestão.

Eu também uso ImagePaddingpara sobrepor cada gráfico sucessivo sobre o acima para o efeito de ocultar os rótulos do mês inferior para os 2 gráficos superiores. Existem outras maneiras de fazer isso. Vamos ver o que outras respostas trazem.

Também adicionei TIME & 2020 na linha inferior do Grid.

3 joka Oct 21 2020 at 22:23

Só para ter certeza que os TimeSeriesobjetos têm únicas assinaturas de tempo, eu escrevi algumas funções que criam espaçadas aleatoriamente intervalos de datas. Esses intervalos começam com Todaymais ou menos um número aleatório de dias e voltam no tempo para um determinado número de etapas .

Além disso, forneço uma função que retorna o intervalo comum para vários intervalos de datas aleatórios diferentes (conforme descrito acima).

Por fim, na seção de código, há uma função que compõe uma série de listas de valores e suas listas de datas correspondentes em TimeSeriesobjetos.

Clear[randf, randomDate, aroundToday, randomDates]

(* Returns an integer between 3 and 7 *)
randf = (RandomInteger[{3, 7}, #] &) /* First;

(* Returns a date that is a random number of days before the input date *)
randomDate[date_, random_ : randf, unit_ : "Days"] := DatePlus[date, {-random[1], unit}];

(* Returns a random number of days before of after Today's date *)
aroundToday[random_ : randf, unit_ : "Days"] := DatePlus[Today, {RandomChoice[{-1, 1}] random[1], unit}];

(* Returns n randomly generated days starting from around Today and going back in random number of steps *)
randomDates[n_, random_ : randf, unit_ : "Days"] := With[{r = randomDate[#, random, unit] &},
  NestList[r, aroundToday[random, unit], n] // Reverse
 ]

(* Accepts lists of dates and returns their common range *)
dateRange[dates__] := Map[Through[{Min, Max}[#]] &, {dates}] // Transpose /* (
  MapThread[Construct, {{Min, Max}, #}] &)

(* Composes TimeSeries objects from a list of date lists and a list of value lists *)
(* A working assumption is that corresponding dates and values sublists are of the same Length ns[i]] *)
(* The returned TimeSeries have a random number of the original entries removed *)
makeTimeSeries[dates_, values_, ns_, random_ : randf] := MapThread[
  With[{t = #1, y = #2, is = RandomInteger[{1, #3}, randf[1]]},
    TimeSeries[#2, {#1}] & @@ Transpose[ReplacePart[Transpose[{t, y}], is -> Nothing // Thread]]
   ] &, {dates, values, ns}]

Fazendo uso do código acima, podemos gerar datas realmente não uniformemente espaçadas para os valores fornecidos em {s1, s2, s3}, que são os dados com comprimentos diferentes na edição OP.

(* Obtained required data lengths *)
ns = Length /@ {s1, s2, s3};

(* Generate randomly spaced dates, starting from Today and extending back into the past *)
dates = Table[randomDates[n - 1], {n, ns}];

(* Finally, compose the corresponding TimeSeries objects *)
mytsdata = makeTimeSeries[dates, {s1, s2, s3}, ns]

(* Record the common range of the various date lists *)
rng = dateRange @@ ((#["Dates"] &) /@ mytsdata)

No meu sistema, uma avaliação do código acima produziu, por exemplo.

Por favor, observe como todas as séries temporais têm diferentes intervalos e diferentes números de observações.

Agora, para responder ao OP, usei a seguinte função:

ClearAll[manyPlots]
Options[manyPlots] = {"Plot1" -> None, "Plot2" -> None, "Plot3" -> None};
manyPlots[ts_, opts : OptionsPattern[manyPlots]] := Module[{opts1, opts2, opts3, allOpts},
  allOpts = {opts1, opts2, opts3} = OptionValue[{"Plot1", "Plot2", "Plot3"}];
  MapThread[DateListPlot[#1, Apply[Sequence, #2]] &, {ts, allOpts}] // List /* Transpose /* GraphicsGrid
 ]

A manyPlotsfunção permite que o usuário passe diferentes opções para os diversos gráficos. Por exemplo.

manyPlots[mytsdata, 
  "Plot1" -> {PlotLabel -> "a", PlotRange -> {rng, Automatic}}, 
    "Plot2" -> {PlotLabel -> "b", PlotRange -> {rng, Automatic}, PlotStyle -> ColorData[97, "ColorList"][[2]]}, 
      "Plot3" -> {PlotLabel -> "c", PlotRange -> {rng, Automatic}, PlotStyle -> ColorData[97, "ColorList"][[3]]}]

fornece rótulos diferentes para os gráficos e garante que todos sejam exibidos em sua faixa comum. Além disso, ele modifica o PlotStylepara o segundo e o terceiro gráfico.

Acho que há muitas outras mudanças que podem ser acomodadas usando essa abordagem.