{"id":573203,"date":"2025-06-05T03:35:02","date_gmt":"2025-06-05T02:35:02","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=pycharm&#038;p=573203"},"modified":"2025-06-05T03:35:09","modified_gmt":"2025-06-05T02:35:09","slug":"deteccao-de-anomalias-em-series-temporais","status":"publish","type":"pycharm","link":"https:\/\/blog.jetbrains.com\/pt-br\/pycharm\/2025\/06\/deteccao-de-anomalias-em-series-temporais\/","title":{"rendered":"Detec\u00e7\u00e3o de anomalias em s\u00e9ries temporais"},"content":{"rendered":"<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-573207\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/06\/PC-social-BlogFeatured-1280x720-2x-4.png\" alt=\"\" width=\"2559\" height=\"1439\" \/><\/figure>\n<p>Como identificar padr\u00f5es incomuns em dados, que podem revelar problemas cr\u00edticos ou oportunidades ocultas? A <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-machine-learning\/\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-machine-learning\/\">detec\u00e7\u00e3o de anomalias<\/a> ajuda a identificar dados que divergem significativamente do normal. Dados de s\u00e9ries temporais, ou seja, coletados ao longo do tempo, costumam incluir tend\u00eancias e padr\u00f5es sazonais. Ocorrem anomalias nesses dados quando esses padr\u00f5es s\u00e3o rompidos, o que torna a detec\u00e7\u00e3o de anomalias uma ferramenta valiosa em setores como vendas, finan\u00e7as, ind\u00fastria e sa\u00fade.<\/p>\n<p>Como dados de s\u00e9ries temporais t\u00eam caracter\u00edsticas \u00fanicas, como sazonalidade e tend\u00eancias, s\u00e3o necess\u00e1rios m\u00e9todos especializados para detectar anomalias de forma eficaz. Nesta postagem de blog, vamos explorar alguns m\u00e9todos populares de detec\u00e7\u00e3o de anomalias em s\u00e9ries temporais, incluindo a decomposi\u00e7\u00e3o de STL e a previs\u00e3o com LSTM, com exemplos detalhados de c\u00f3digo para ajudar voc\u00ea a come\u00e7ar a us\u00e1-la.<\/p>\n<h2 class=\"wp-block-heading\">Detec\u00e7\u00e3o de anomalias em s\u00e9ries temporais em empresas<\/h2>\n<p>Dados de s\u00e9ries temporais s\u00e3o essenciais em muitos ramos de atividade. Muitas empresas registram dados com marca\u00e7\u00f5es temporais, permitindo que mudan\u00e7as sejam analisadas e que os dados sejam comparados ao longo do tempo. S\u00e9ries temporais s\u00e3o \u00fateis ao comparar uma certa quantidade ao longo de um certo per\u00edodo \u2014 por exemplo, compara\u00e7\u00f5es de um ano para o outro quando os dados t\u00eam caracter\u00edsticas sazonais.<\/p>\n<p><strong>Monitoramento de vendas<\/strong><\/p>\n<p>Dados de vendas s\u00e3o um dos exemplos mais comuns de dados de s\u00e9ries temporais com diferen\u00e7as sazonais. Como muitas vendas s\u00e3o afetadas pelos feriados anuais e pela \u00e9poca do ano, \u00e9 dif\u00edcil tirar conclus\u00f5es sobre dados de vendas sem considerar as varia\u00e7\u00f5es sazonais. Por isso, um m\u00e9todo comum de analisar e encontrar anomalias em dados de vendas \u00e9 a decomposi\u00e7\u00e3o de STL, que abordaremos em detalhes <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-time-series\/#stl-beehive\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-time-series\/#stl-beehive\">mais adiante nesta postagem<\/a>.<\/p>\n<p><strong>Finan\u00e7as<\/strong><\/p>\n<p>Dados financeiros, como transa\u00e7\u00f5es e pre\u00e7os de a\u00e7\u00f5es, s\u00e3o exemplos t\u00edpicos de dados de s\u00e9ries temporais. No setor financeiro, \u00e9 pr\u00e1tica comum analisar e detectar anomalias nesses dados. Por exemplo, pode-se usar modelos de previs\u00e3o de s\u00e9ries temporais em transa\u00e7\u00f5es autom\u00e1ticas. Usaremos uma previs\u00e3o de s\u00e9rie temporal para identificar anomalias em dados de a\u00e7\u00f5es <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-time-series\/#lstm-stock\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-time-series\/#lstm-stock\">mais adiante nesta postagem<\/a>.<\/p>\n<p><strong>Ind\u00fastria<\/strong><\/p>\n<p>Outro caso de uso da detec\u00e7\u00e3o de anomalias em s\u00e9ries temporais \u00e9 o monitoramento de falhas em linhas de produ\u00e7\u00e3o. M\u00e1quinas costumam ser monitoradas, disponibilizando dados de s\u00e9ries temporais. \u00c9 essencial poder notificar a administra\u00e7\u00e3o sobre falhas em potencial e a detec\u00e7\u00e3o de anomalias desempenha um papel-chave nisso.<\/p>\n<p><strong>Medicina e sa\u00fade<\/strong><\/p>\n<p>Em Medicina e sa\u00fade, sinais vitais humanos s\u00e3o monitorados e pode-se detectar anomalias. Isso j\u00e1 \u00e9 muito importante na pesquisa m\u00e9dica, mas \u00e9 cr\u00edtico no diagn\u00f3stico. Se um paciente hospitalizado tiver sinais vitais an\u00f4malos e n\u00e3o for tratado imediatamente, os resultados podem ser fatais.<\/p>\n<h2 class=\"wp-block-heading\">Por que \u00e9 importante usar m\u00e9todos especiais na detec\u00e7\u00e3o de anomalias em s\u00e9ries temporais?<\/h2>\n<p>Dados de s\u00e9ries temporais s\u00e3o especiais, no sentido de que \u00e0s vezes n\u00e3o podem ser tratados como outros tipos de dados. Por exemplo, se aplicarmos uma divis\u00e3o de teste de treinamento a dados de s\u00e9ries temporais, a natureza sequencial desses dados significa que n\u00e3o podemos embaralh\u00e1-los. Isso tamb\u00e9m se aplica ao usarmos dados de s\u00e9ries temporais em modelos de aprendizado profundo. \u00c9 comum usar uma rede neural recorrente (RNN) para levar em conta essa rela\u00e7\u00e3o sequencial e os dados de treinamento s\u00e3o introduzidos como janelas de tempo, o que preserva a sequ\u00eancia de eventos dentro delas.<\/p>\n<p>Dados de s\u00e9ries temporais tamb\u00e9m s\u00e3o especiais porque costumam ser sazonais e ter tend\u00eancias que n\u00e3o podemos ignorar. Essa sazonalidade pode se manifestar em ciclos de 24 horas, 7 dias ou 12 meses, para citar apenas algumas possibilidades. S\u00f3 podem ser determinadas anomalias depois de levar em conta a sazonalidade e as tend\u00eancias, como voc\u00ea ver\u00e1 no <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-time-series\/#stl-beehive\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-time-series\/#stl-beehive\">nosso exemplo abaixo<\/a>.\u00a0<\/p>\n<h2 class=\"wp-block-heading\">M\u00e9todos usados para a detec\u00e7\u00e3o de anomalias em s\u00e9ries temporais<\/h2>\n<p>Como dados de s\u00e9ries temporais s\u00e3o especiais, h\u00e1 m\u00e9todos espec\u00edficos para detectar anomalias neles. Dependendo do tipo dos dados, alguns dos m\u00e9todos e algoritmos que mencionamos na nossa <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-machine-learning\/\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-machine-learning\/\">postagem anterior sobre detec\u00e7\u00e3o de anomalias<\/a> podem ser usados em dados de s\u00e9ries temporais. Por\u00e9m, com esses m\u00e9todos, a detec\u00e7\u00e3o de anomalias pode n\u00e3o ser t\u00e3o robusta quanto ao usar m\u00e9todos projetados especificamente para dados de s\u00e9ries temporais. Em alguns casos, pode ser usada uma combina\u00e7\u00e3o de m\u00e9todos de detec\u00e7\u00e3o para reconfirmar os resultados da detec\u00e7\u00e3o e evitar falsos positivos ou negativos.<\/p>\n<h3 class=\"wp-block-heading\">Decomposi\u00e7\u00e3o de STL<\/h3>\n<p>Uma das maneiras mais populares de usar dados sazonais de s\u00e9ries temporais \u00e9 a decomposi\u00e7\u00e3o de STL \u2014 sigla em ingl\u00eas para &#8220;decomposi\u00e7\u00e3o de tend\u00eancias sazonais usando LOESS (suaviza\u00e7\u00e3o estimada localmente de gr\u00e1ficos de dispers\u00e3o)&#8221;. Nesse m\u00e9todo, decomp\u00f5e-se uma s\u00e9rie temporal usando uma estimativa da sazonalidade (com o per\u00edodo fornecido ou determinado atrav\u00e9s de um algoritmo), uma tend\u00eancia (estimada) e um res\u00edduo (o ru\u00eddo nos dados). A biblioteca de <a href=\"https:\/\/www.jetbrains.com\/help\/pycharm\/python.html\" target=\"_blank\" rel=\"noopener\">Python<\/a> <a href=\"https:\/\/www.statsmodels.org\/stable\/index.html\" target=\"_blank\" rel=\"noopener\">statsmodels<\/a> tem <a href=\"https:\/\/www.statsmodels.org\/stable\/examples\/notebooks\/generated\/stl_decomposition.html\" target=\"_blank\" rel=\"noopener\">ferramentas para decomposi\u00e7\u00e3o de STL<\/a>.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539263\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-70.png\" alt=\"Decomposi\u00e7\u00e3o de STL\" width=\"1600\" height=\"900\" \/><\/figure>\n<p>Detecta-se uma anomalia quando o res\u00edduo ultrapassa um determinado limiar.\u00a0<\/p>\n<h3 id=\"stl-beehive\" class=\"wp-block-heading\">Usando a decomposi\u00e7\u00e3o de STL em dados de colmeias de abelhas<\/h3>\n<p>Em uma <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-machine-learning\/\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-machine-learning\/\">postagem anterior no blog<\/a>, exploramos a detec\u00e7\u00e3o de anomalias em colmeias de abelhas usando os m\u00e9todos <a href=\"https:\/\/scikit-learn.org\/stable\/modules\/generated\/sklearn.svm.OneClassSVM.html\" target=\"_blank\" rel=\"noopener\">OneClassSVM<\/a> e <a href=\"https:\/\/scikit-learn.org\/stable\/modules\/generated\/sklearn.ensemble.IsolationForest.html\" target=\"_blank\" rel=\"noopener\">IsolationForest<\/a>.\u00a0<\/p>\n<p>Neste tutorial, vamos analisar <a href=\"https:\/\/www.kaggle.com\/datasets\/vivovinco\/beehives\" target=\"_blank\" rel=\"noopener\">dados de colmeias<\/a> como uma s\u00e9rie temporal, usando a classe <code>STL<\/code>, fornecida pela biblioteca statsmodels. Para come\u00e7ar, configure o seu ambiente usando o arquivo <a href=\"https:\/\/github.com\/Cheukting\/anomaly-detection\/blob\/main\/requirements.txt\" target=\"_blank\" rel=\"noopener\">requirements.txt<\/a>.\u00a0<\/p>\n<h4 class=\"wp-block-heading\"><strong>1. Instale a biblioteca<\/strong><\/h4>\n<p>J\u00e1 que vimos usando apenas o modelo fornecido pelo Scikit-learn, precisaremos instalar a biblioteca statsmodels a partir do PyPI. Isso \u00e9 f\u00e1cil de fazer no <a href=\"https:\/\/www.jetbrains.com\/pycharm\/data-science\/\" target=\"_blank\" rel=\"noreferrer noopener\">PyCharm<\/a>.<\/p>\n<div class=\"buttons\">\n<div class=\"buttons__row\"><a class=\"btn\" href=\"https:\/\/www.jetbrains.com\/pycharm\/data-science\/\" target=\"\" rel=\"noopener\">Comece a usar o PyCharm Pro gratuitamente<\/a><\/div>\n<\/div>\n<p>V\u00e1 at\u00e9 a janela <em><a href=\"https:\/\/www.jetbrains.com\/help\/pycharm\/installing-uninstalling-and-upgrading-packages.html\" target=\"_blank\" rel=\"noopener\">Python Packages<\/a><\/em> (clique no \u00edcone na parte inferior esquerda do IDE) e digite &#8220;statsmodels&#8221; na caixa de pesquisa.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539351\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-76.png\" alt=\"Statsmodels no PyCharm\" width=\"1600\" height=\"630\" \/><\/figure>\n<p>Voc\u00ea pode ver todas as informa\u00e7\u00f5es sobre o pacote no lado direito. Para instal\u00e1-lo, basta clicar em <em>Install package<\/em>.<\/p>\n<h4 class=\"wp-block-heading\"><strong>2. Cria\u00e7\u00e3o de um notebook do Jupyter<\/strong><\/h4>\n<p>Para investigar o conjunto de dados mais a fundo, vamos criar um <a href=\"https:\/\/www.jetbrains.com\/help\/pycharm\/jupyter-notebook-support.html\" target=\"_blank\" rel=\"noopener\">notebook do Jupyter<\/a> para aproveitarmos as ferramentas do ambiente do PyCharm para esses notebooks.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539362\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-77.png\" alt=\"Cria\u00e7\u00e3o de um notebook do Jupyter no PyCharm\" width=\"1098\" height=\"410\" \/><\/figure>\n<p>Vamos importar o <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2024\/10\/data-exploration-with-pandas\/\">pandas<\/a> e carregar o arquivo em <code>.csv<\/code>.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import pandas as pd\n\ndf = pd.read_csv('..\/data\/Hive17.csv', sep=\";\")\ndf = df.dropna()\ndf<\/pre>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539310\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-73.png\" alt=\"Importa\u00e7\u00e3o do pandas no PyCharm\" width=\"1600\" height=\"930\" \/><\/figure>\n<h4 class=\"wp-block-heading\"><strong>3. Inspe\u00e7\u00e3o dos dados como gr\u00e1ficos<\/strong><\/h4>\n<p>Agora podemos inspecionar os dados como gr\u00e1ficos. Aqui, gostar\u00edamos de ver a temperatura da colmeia 17 ao longo do tempo. Clique em <em>Chart view<\/em> no inspetor de dataframes e selecione <em>T17<\/em> como o eixo Y nas configura\u00e7\u00f5es da s\u00e9rie.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539299\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-2.gif\" alt=\"Inspe\u00e7\u00e3o dos dados como gr\u00e1ficos no PyCharm\" width=\"720\" height=\"290\" \/><\/figure>\n<p>Quando expressa na forma de uma s\u00e9rie temporal, a temperatura tem muitos altos e baixos. Isso indica um comportamento peri\u00f3dico, provavelmente devido ao ciclo de dias e noites. Portanto, pode-se presumir com confian\u00e7a que h\u00e1 um per\u00edodo de 24 horas para a temperatura.\u00a0<\/p>\n<p>Depois, h\u00e1 uma tend\u00eancia de queda da temperatura ao longo do tempo. Se voc\u00ea inspecionar a coluna <em>DateTime<\/em>, ver\u00e1 que as datas est\u00e3o no per\u00edodo de agosto a novembro. Como a <a href=\"https:\/\/www.kaggle.com\/datasets\/vivovinco\/beehives\/data\" target=\"_blank\" rel=\"noopener\">p\u00e1gina de Kaggle do conjunto de dados<\/a> indica que os dados foram coletados na Turquia, a transi\u00e7\u00e3o do ver\u00e3o para o outono explica nossa observa\u00e7\u00e3o de que a temperatura est\u00e1 caindo com o tempo.<\/p>\n<h4 class=\"wp-block-heading\"><strong>4. Decomposi\u00e7\u00e3o da s\u00e9rie temporal<\/strong><\/h4>\n<p>Para compreendermos a s\u00e9rie temporal e detectarmos anomalias, faremos uma decomposi\u00e7\u00e3o de STL, importando a classe <code>STL<\/code> da statsmodels e alimentando-a com nossos dados de temperatura.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">from statsmodels.tsa.seasonal import STL\n\nstl = STL(df[\"T17\"], period=24, robust=True) \nresult = stl.fit()<\/pre>\n<p>Para a decomposi\u00e7\u00e3o funcionar, teremos que informar um per\u00edodo. Como j\u00e1 mencionado, podemos presumir um ciclo de 24 horas.<\/p>\n<p>Segundo a documenta\u00e7\u00e3o, a classe <code>STL<\/code> decomp\u00f5e uma s\u00e9rie temporal em tr\u00eas componentes: de tend\u00eancia, sazonal e residual. Para termos uma vis\u00e3o mais clara do resultado decomposto, podemos usar o m\u00e9todo incorporado <code>plot<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">result.plot()<\/pre>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539541\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/Time-series-decomposition.png\" alt=\"Decomposi\u00e7\u00e3o da s\u00e9rie temporal\" width=\"1600\" height=\"1100\" \/><\/figure>\n<p>Voc\u00ea pode ver que os gr\u00e1ficos <em>Trend<\/em> e <em>Season<\/em> parecem se alinhar com nossas premissas acima. Por\u00e9m, estamos interessados no gr\u00e1fico residual, embaixo, que \u00e9 a s\u00e9rie original sem as altera\u00e7\u00f5es sazonais e de tend\u00eancia. Qualquer valor residual extremamente alto ou baixo indica uma anomalia.<\/p>\n<h4 id=\"anomaly-threshold\" class=\"wp-block-heading\"><strong>5. Limiar de anomalia<\/strong><\/h4>\n<p>Agora, gostar\u00edamos de determinar quais valores residuais consideraremos anormais. Para isso, podemos olhar o histograma dos valores residuais.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">result.resid.plot.hist()<\/pre>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539375\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-78.png\" alt=\"Limiar de anomalia no PyCharm\" width=\"1328\" height=\"1048\" \/><\/figure>\n<p>Esta pode ser considerada uma distribui\u00e7\u00e3o normal em torno de 0, com uma cauda longa acima de 5 e abaixo de \u22125. Ent\u00e3o, vamos determinar um limiar de 5.<\/p>\n<p>Para mostrar as anomalias na s\u00e9rie temporal original, podemos colorir todas elas em vermelho no gr\u00e1fico, assim:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import matplotlib.pyplot as plt\n\nthreshold = 5\nanomalies_filter = result.resid.apply(lambda x: True if abs(x) &gt; threshold else False)\nanomalies = df[\"T17\"][anomalies_filter]\n\nplt.figure(figsize=(14, 8))\nplt.scatter(x=anomalies.index, y=anomalies, color=\"red\", label=\"anomalies\")\nplt.plot(df.index, df['T17'], color='blue')\nplt.title('Temperatures in Hive 17')\nplt.xlabel('Hours')\nplt.ylabel('Temperature')\nplt.legend()\nplt.show()<\/pre>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539386\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-79.png\" alt=\"Anomalias na s\u00e9rie temporal original no PyCharm\" width=\"1600\" height=\"976\" \/><\/figure>\n<p>Sem a decomposi\u00e7\u00e3o de STL, \u00e9 muito dif\u00edcil identificar essas anomalias em uma s\u00e9rie temporal que consista de per\u00edodos e tend\u00eancias.<\/p>\n<h3 class=\"wp-block-heading\">Previs\u00e3o com LSTM<\/h3>\n<p>Uma outra maneira de detectar anomalias em dados de s\u00e9ries temporais \u00e9 fazer uma previs\u00e3o usando m\u00e9todos de aprendizado profundo para estimar o desfecho de pontos de dados. Se uma estimativa for muito diferente do ponto real de dados correspondente, isso pode ser um sinal de dados an\u00f4malos.<\/p>\n<p>Um algoritmo popular de aprendizado profundo para fazer previs\u00f5es em dados sequenciais \u00e9 o modelo &#8220;long short-term memory&#8221; (LSTM), um tipo de rede neural recorrente (RNN). Esse modelo tem port\u00f5es de entrada, esquecimento e sa\u00edda, que s\u00e3o matrizes num\u00e9ricas. Isso garante que as informa\u00e7\u00f5es importantes sejam passadas para a pr\u00f3xima itera\u00e7\u00e3o dos dados.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539580\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/LSTM-memory-cell.png\" alt=\"C\u00e9lula de LSTM\" width=\"1600\" height=\"900\" \/><\/figure>\n<p>Como os dados de uma s\u00e9rie temporal s\u00e3o sequenciais, o que significa que a ordem dos pontos de dados \u00e9 sequencial e n\u00e3o deve ser embaralhada, o modelo LSTM de aprendizado profundo \u00e9 eficaz para prever um desfecho em um determinado momento. Essa previs\u00e3o pode ser comparada com os dados reais e pode-se determinar um limiar para decidir se os dados reais s\u00e3o an\u00f4malos.<\/p>\n<h3 id=\"lstm-stock\" class=\"wp-block-heading\">Usando a previs\u00e3o com LSTM em pre\u00e7os de a\u00e7\u00f5es<\/h3>\n<p>Agora, vamos criar um novo projeto do Jupyter para detectar quaisquer anomalias no pre\u00e7o das a\u00e7\u00f5es da Apple nos \u00faltimos 5 anos. O <a href=\"https:\/\/www.nasdaq.com\/market-activity\/stocks\/aapl\/historical?page=1&amp;rows_per_page=25&amp;timeline=y5\" target=\"_blank\" rel=\"noopener\">conjunto de dados de pre\u00e7os de a\u00e7\u00f5es<\/a> cont\u00e9m os dados mais atualizados. Se voc\u00ea tamb\u00e9m quiser fazer isso para acompanhar esta postagem, pode <a href=\"https:\/\/github.com\/Cheukting\/lstm_anomaly_detection\/tree\/main\/data\" target=\"_blank\" rel=\"noopener\">baixar o conjunto de dados<\/a> que estamos usando.<\/p>\n<h4 class=\"wp-block-heading\"><strong>1. Cria\u00e7\u00e3o de um projeto do Jupyter<\/strong><\/h4>\n<p>Ao iniciar um novo projeto, voc\u00ea pode optar por criar um projeto do Jupyter, que \u00e9 otimizado para ci\u00eancia de dados. Na janela <em>New Project<\/em>, voc\u00ea pode criar um reposit\u00f3rio de Git e determinar qual instala\u00e7\u00e3o do conda deve ser usada para gerenciar o seu ambiente.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539627\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/Jupyter-project-in-PyCharm.png\" alt=\"Cria\u00e7\u00e3o de um projeto do Jupyter no PyCharm\" width=\"1592\" height=\"1282\" \/><\/figure>\n<p>Depois de criar o projeto, voc\u00ea ver\u00e1 um notebook de exemplo. V\u00e1 em frente e crie um novo notebook do Jupyter para este exerc\u00edcio.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539604\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-4.gif\" alt=\"Um notebook de exemplo no PyCharm\" width=\"716\" height=\"382\" \/><\/figure>\n<p>Depois disso, vamos configurar o <code>requirements.txt<\/code>. Precisaremos do pandas, do matplotlib e do PyTorch, que \u00e9 chamado de &#8220;torch&#8221; no PyPI. Como o PyTorch n\u00e3o est\u00e1 inclu\u00eddo no ambiente do conda, o PyCharm nos avisar\u00e1 que esse pacote est\u00e1 faltando. Para instal\u00e1-lo, clique no \u00edcone da l\u00e2mpada e selecione <em>Install all missing packages<\/em>.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539615\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-5.gif\" alt=\"Instala\u00e7\u00e3o de todos os pacotes faltando no PyCharm\" width=\"1358\" height=\"762\" \/><\/figure>\n<h4 class=\"wp-block-heading\"><strong>2. Carregamento e inspe\u00e7\u00e3o dos dados<\/strong><\/h4>\n<p>Em seguida, vamos colocar nosso conjunto de dados <a href=\"https:\/\/github.com\/Cheukting\/lstm_anomaly_detection\/tree\/main\/data\" target=\"_blank\" rel=\"noopener\">apple_stock_5y.csv<\/a> na pasta de dados e carreg\u00e1-lo como uma dataframe do pandas para inspecion\u00e1-lo.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import pandas as pd\n \ndf = pd.read_csv('data\/apple_stock_5y.csv')\ndf<\/pre>\n<p>Com a tabela interativa, podemos ver facilmente se est\u00e1 faltando algum dado.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539640\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-6.gif\" alt=\"\" width=\"824\" height=\"442\" \/><\/figure>\n<p>N\u00e3o h\u00e1 dados faltando, mas temos outro problema: gostar\u00edamos de usar o pre\u00e7o <em>Close\/Last<\/em>, mas seu tipo de dados n\u00e3o \u00e9 num\u00e9rico. Vamos fazer uma convers\u00e3o e inspecionar nossos dados de novo:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">df[\"Close\/Last\"] = df[\"Close\/Last\"].apply(lambda x: float(x[1:]))\ndf<\/pre>\n<p>Agora podemos inspecionar o pre\u00e7o com a tabela interativa. Clique no \u00edcone de gr\u00e1fico \u00e0 esquerda e ser\u00e1 criado um gr\u00e1fico. Como padr\u00e3o, esse gr\u00e1fico usar\u00e1 <em>Date<\/em> como o eixo X e <em>Volume<\/em> como eixo Y. Como gostar\u00edamos de inspecionar o pre\u00e7o <em>Close\/Last<\/em>, acesse as configura\u00e7\u00f5es clicando no \u00edcone da engrenagem \u00e0 direita e selecione <em>Close\/Last<\/em> como o eixo Y.<\/p>\n<figure class=\"wp-block-image size-full is-resized\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539652\" style=\"aspect-ratio: 1.8662790697674418; width: 642px; height: auto;\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-7.gif\" alt=\"\" width=\"642\" height=\"344\" \/><\/figure>\n<h4 class=\"wp-block-heading\"><strong>3. Prepara\u00e7\u00e3o dos dados de treinamento para LSTM<\/strong><\/h4>\n<p>Agora, temos que preparar os dados de treinamento a serem usados no modelo de LSTM. Precisamos preparar uma sequ\u00eancia de vetores (a partir do eixo X), cada um representando uma janela de tempo, para prevermos o pr\u00f3ximo pre\u00e7o. Este formar\u00e1 outra sequ\u00eancia (alvo no eixo Y). Aqui, podemos determinar a dura\u00e7\u00e3o dessa janela de tempo atrav\u00e9s da vari\u00e1vel <code>lookback<\/code>. O c\u00f3digo a seguir cria as sequ\u00eancias de X e Y, que ser\u00e3o ent\u00e3o convertidas em tensores do PyTorch:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import torch\n\nlookback = 5\ntimeseries = df[[\"Close\/Last\"]].values.astype('float32')\n\nX, y = [], []\nfor i in range(len(timeseries)-lookback):\n    feature = timeseries[i:i+lookback]\n    target = timeseries[i+1:i+lookback+1]\n    X.append(feature)\n    y.append(target)\n    \nX = torch.tensor(X)\ny = torch.tensor(y)\n\nprint(X.shape, y.shape)<\/pre>\n<p>Em linhas gerais, quanto mais longa for a janela, maior ser\u00e1 o nosso modelo, pois o vetor de entrada ser\u00e1 maior. Por\u00e9m, com uma janela mais longa, a sequ\u00eancia de entradas ser\u00e1 menor. Portanto, a determina\u00e7\u00e3o dessa janela de &#8220;lookback&#8221; \u00e9 um balanceamento. Vamos come\u00e7ar com 5, mas fique \u00e0 vontade para experimentar outros valores e ver as diferen\u00e7as.<\/p>\n<h4 class=\"wp-block-heading\"><strong>4. Cria\u00e7\u00e3o e treinamento do modelo<\/strong><\/h4>\n<p>Podemos criar o modelo antes de trein\u00e1-lo usando o m\u00e9todo <a href=\"https:\/\/pytorch.org\/docs\/stable\/nn.html\" target=\"_blank\" rel=\"noopener\">nn module<\/a> no PyTorch para criar uma classe. Esse m\u00e9todo fornece blocos de constru\u00e7\u00e3o, tais como diferentes camadas de redes neurais. Neste exerc\u00edcio, criaremos uma <a href=\"https:\/\/pytorch.org\/docs\/stable\/generated\/torch.nn.LSTM.html\" target=\"_blank\" rel=\"noopener\">camada simples de LSTM<\/a>, seguida de uma <a href=\"https:\/\/pytorch.org\/docs\/stable\/generated\/torch.nn.Linear.html\" target=\"_blank\" rel=\"noopener\">camada linear<\/a>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import torch.nn as nn\n\nclass StockModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.lstm = nn.LSTM(input_size=1, hidden_size=50, num_layers=1, batch_first=True)\n        self.linear = nn.Linear(50, 1)\n    def forward(self, x):\n        x, _ = self.lstm(x)\n        x = self.linear(x)\n        return x<\/pre>\n<p>Em seguida, treinaremos nosso modelo, mas antes disso, precisaremos criar um otimizador, uma <a href=\"https:\/\/pytorch.org\/docs\/stable\/nn.html#loss-functions\" target=\"_blank\" rel=\"noopener\">fun\u00e7\u00e3o de perda<\/a> usada para calcular a perda entre o valor real e o previsto de Y, e um <a href=\"https:\/\/pytorch.org\/docs\/stable\/data.html#data-loading-order-and-sampler\" target=\"_blank\" rel=\"noopener\">carregador de dados<\/a> para alimentar o modelo com nossos dados de treinamento:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import numpy as np\nimport torch.optim as optim\nimport torch.utils.data as data\n\nmodel = StockModel()\noptimizer = optim.Adam(model.parameters())\nloss_fn = nn.MSELoss()\nloader = data.DataLoader(data.TensorDataset(X, y), shuffle=True, batch_size=8)<\/pre>\n<p>O carregador de dados pode embaralhar a entrada, pois j\u00e1 criamos as janelas de tempo. Isso preserva a rela\u00e7\u00e3o sequencial em cada janela.<\/p>\n<p>O treinamento \u00e9 feito atrav\u00e9s de um loop <code>for<\/code> iterado para cada per\u00edodo. A cada 100 per\u00edodos, imprimiremos a perda e observaremos enquanto o modelo converge:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">n_epochs = 1000\nfor epoch in range(n_epochs):\n    model.train()\n    for X_batch, y_batch in loader:\n        y_pred = model(X_batch)\n        loss = loss_fn(y_pred, y_batch)\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n    if epoch % 100 != 0:\n        continue\n    model.eval()\n    with torch.no_grad():\n        y_pred = model(X)\n        rmse = np.sqrt(loss_fn(y_pred, y))\n    print(f\"Epoch {epoch}: RMSE {rmse:.4f}\")<\/pre>\n<p>Come\u00e7amos com 1000 per\u00edodos, mas o modelo converge bem rapidamente. Fique \u00e0 vontade para experimentar outros n\u00fameros de per\u00edodos no treinamento, para obter o melhor resultado.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539664\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/Epochs-for-training.png\" alt=\"Per\u00edodos para o treinamento\" width=\"1346\" height=\"1046\" \/><\/figure>\n<p>No PyCharm, uma c\u00e9lula que demore algum tempo para ser executada mandar\u00e1 uma notifica\u00e7\u00e3o de quanto tempo falta e dar\u00e1 um atalho para a c\u00e9lula. Isso \u00e9 muito \u00fatil ao treinar modelos de aprendizado de m\u00e1quina, especialmente de aprendizado profundo, em notebooks do Jupyter.<\/p>\n<h4 class=\"wp-block-heading\"><strong>5. Cria\u00e7\u00e3o de um gr\u00e1fico da previs\u00e3o e localiza\u00e7\u00e3o de erros<\/strong><\/h4>\n<p>Depois, vamos criar a previs\u00e3o e fazer um gr\u00e1fico dela, juntamente com a s\u00e9rie temporal real. Observe que teremos que criar uma s\u00e9rie 2D np para coincidir com a s\u00e9rie temporal real. Esta aparecer\u00e1 em azul, enquanto a s\u00e9rie prevista aparecer\u00e1 em vermelho.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import matplotlib.pyplot as plt\n\nwith torch.no_grad():\n    pred_series = np.ones_like(timeseries) * np.nan\n    pred_series[lookback:] = model(X)[:, -1, :]\n\nplt.plot(timeseries, c='b')\nplt.plot(pred_series, c='r')\nplt.show()<\/pre>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539687\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/Plot-the-prediction-and-find-the-errors.png\" alt=\"Cria\u00e7\u00e3o de um gr\u00e1fico da previs\u00e3o e localiza\u00e7\u00e3o de erros\" width=\"1180\" height=\"856\" \/><\/figure>\n<p>Se voc\u00ea observar com aten\u00e7\u00e3o, ver\u00e1 que a previs\u00e3o e os valores reais n\u00e3o se alinham perfeitamente. Por\u00e9m, a maioria das previs\u00f5es \u00e9 razo\u00e1vel.<\/p>\n<p>Para inspecionar os erros mais de perto, podemos criar uma s\u00e9rie dos erros e usar a tabela interativa para observ\u00e1-los. Desta vez, vamos usar o erro absoluto.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">error = abs(timeseries-pred_series)\nerror<\/pre>\n<p>Use as configura\u00e7\u00f5es para criar um histograma com o valor do erro absoluto como o eixo X e a contagem do valor como o eixo Y.<\/p>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539434\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/image-3.gif\" alt=\"\" width=\"452\" height=\"362\" \/><\/figure>\n<h4 class=\"wp-block-heading\"><strong>6. Determina\u00e7\u00e3o do limiar de anomalia e visualiza\u00e7\u00e3o<\/strong><\/h4>\n<p>A maioria dos pontos ter\u00e1 um erro absoluto de menos de 6. Portanto, podemos determinar esse valor como o limiar de anomalia. De forma semelhante <a href=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-time-series\/#anomaly-threshold\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/pycharm\/2025\/01\/anomaly-detection-in-time-series\/#anomaly-threshold\">ao que fizemos com as anomalias na colmeia<\/a>, podemos tra\u00e7ar os pontos de dados an\u00f4malos no gr\u00e1fico.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">threshold = 6\nerror_series = pd.Series(error.flatten())\nprice_series = pd.Series(timeseries.flatten())\n\nanomalies_filter = error_series.apply(lambda x: True if x &gt; threshold else False)\nanomalies = price_series[anomalies_filter]\n\nplt.figure(figsize=(14, 8))\nplt.scatter(x=anomalies.index, y=anomalies, color=\"red\", label=\"anomalies\")\nplt.plot(df.index, timeseries, color='blue')\nplt.title('Closing price')\nplt.xlabel('Days')\nplt.ylabel('Price')\nplt.legend()\nplt.show()<\/pre>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-539699\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/Plot-the-anomalous-data-points-in-the-graph.png\" alt=\"Tra\u00e7ando os pontos de dados an\u00f4malos no gr\u00e1fico\" width=\"1600\" height=\"963\" \/><\/figure>\n<h2 class=\"wp-block-heading\">Resumo<\/h2>\n<p>\u00c9 comum usar dados de s\u00e9ries temporais em muitas aplica\u00e7\u00f5es, inclusive de neg\u00f3cios e de pesquisa cient\u00edfica. Devido \u00e0 natureza sequencial desses dados, usam-se m\u00e9todos e algoritmos especiais para ajudar a detectar anomalias neles. Nesta postagem de blog, demonstramos como identificar anomalias usando a decomposi\u00e7\u00e3o de STL para eliminar a sazonalidade e as tend\u00eancias. Tamb\u00e9m demonstramos como usar o aprendizado profundo e o modelo LSTM para comparar a estimativa prevista com os dados reais, com a finalidade de detectar anomalias.<\/p>\n<h2 class=\"wp-block-heading\">Detecte anomalias com o PyCharm<\/h2>\n<p>Com um projeto do Jupyter no PyCharm Professional, voc\u00ea pode facilmente organizar a sua detec\u00e7\u00e3o de anomalias, usando diversos arquivos de dados e notebooks. Podem ser gerados gr\u00e1ficos de sa\u00edda, muito acess\u00edveis no PyCharm, para inspecionar anomalias. Outros recursos, como sugest\u00f5es de complementa\u00e7\u00e3o autom\u00e1tica, tornam muito f\u00e1cil a navega\u00e7\u00e3o pelos modelos do Scikit-learn e configura\u00e7\u00f5es de gr\u00e1ficos da Matplotlib.<\/p>\n<p>Incremente os seus projetos de ci\u00eancia de dados usando o PyCharm e <a href=\"https:\/\/www.jetbrains.com\/pycharm\/data-science\/\" target=\"_blank\" rel=\"noopener\">confira os recursos que ele oferece<\/a> para otimizar o seu fluxo de trabalho em ci\u00eancia de dados.<\/p>\n<div class=\"buttons\">\n<div class=\"buttons__row\"><a class=\"btn\" href=\"https:\/\/www.jetbrains.com\/pycharm\/data-science\/\" target=\"\" rel=\"noopener\">Comece a usar o PyCharm Pro gratuitamente<\/a><\/div>\n<\/div>\n\n\n<p><em>Artigo original em ingl\u00eas por:<\/em><\/p>\n\n\n    <div class=\"about-author \">\n        <div class=\"about-author__box\">\n            <div class=\"row\">\n                <div class=\"about-author__box-img\">\n                    <img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/01\/CheukTingHo-Kimono-e1738750639162-200x200.jpg\" width=\"200\" height=\"200\" alt=\"Cheuk Ting Ho\" loading=\"lazy\"  class=\"avatar avatar-200 wp-user-avatar wp-user-avatar-200 photo avatar-default\">\n                <\/div>\n                <div class=\"about-author__box-text\">\n                                            <h4>Cheuk Ting Ho<\/h4>\n                                                        <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n","protected":false},"author":1086,"featured_media":573207,"comment_status":"closed","ping_status":"closed","template":"","categories":[952,1401],"tags":[8670],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/pycharm\/573203"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/pycharm"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/types\/pycharm"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/users\/1086"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/comments?post=573203"}],"version-history":[{"count":4,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/pycharm\/573203\/revisions"}],"predecessor-version":[{"id":573220,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/pycharm\/573203\/revisions\/573220"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/media\/573207"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/media?parent=573203"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/categories?post=573203"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/tags?post=573203"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/pt-br\/wp-json\/wp\/v2\/cross-post-tag?post=573203"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}