{"id":22353557,"url":"https://github.com/dp6/customer-journey-analysis-toolbox","last_synced_at":"2025-08-12T02:37:15.010Z","repository":{"id":62572155,"uuid":"410965602","full_name":"DP6/customer-journey-analysis-toolbox","owner":"DP6","description":"Repositório com funções, bibliotecas e notebooks para auxiliar o processo de análise de jornadas utilizado em projetos DP6","archived":false,"fork":false,"pushed_at":"2023-04-18T13:11:12.000Z","size":1419,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-07-10T02:48:44.249Z","etag":null,"topics":["attribution","exploratory-data-analysis","python","user-journey"],"latest_commit_sha":null,"homepage":"https://dp6.github.io/customer-journey-analysis-toolbox/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DP6.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-27T16:43:52.000Z","updated_at":"2023-01-14T12:18:29.000Z","dependencies_parsed_at":"2022-11-03T18:26:53.753Z","dependency_job_id":null,"html_url":"https://github.com/DP6/customer-journey-analysis-toolbox","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/DP6/customer-journey-analysis-toolbox","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DP6%2Fcustomer-journey-analysis-toolbox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DP6%2Fcustomer-journey-analysis-toolbox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DP6%2Fcustomer-journey-analysis-toolbox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DP6%2Fcustomer-journey-analysis-toolbox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DP6","download_url":"https://codeload.github.com/DP6/customer-journey-analysis-toolbox/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DP6%2Fcustomer-journey-analysis-toolbox/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269990503,"owners_count":24508865,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-12T02:00:09.011Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attribution","exploratory-data-analysis","python","user-journey"],"created_at":"2024-12-04T13:08:59.450Z","updated_at":"2025-08-12T02:37:14.953Z","avatar_url":"https://github.com/DP6.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"## O que é a Jatoolbox ?\n\n  No contexto da DP6 é comum lidarmos com uma base de dados para atribuição que apresentas jornadas no formato 'A \u003e B \u003e C', em que as jornadas são representadas por uma string em que os pontos de contato de um usuário são representados por substrings separados por ' \u003e ', na ordem em que aconteceram, da esquerda para a direita. \n\n  Análisar essas funções pode se tornar tedioso e trabalhoso, uma vez que para extrair informações relevantes das mesmas é necessário criar uma combinação de funções muitas vezes repetitivas, baseadas em splits, contagens de elementos, combinações, etc. \n\n  Nesse cenário, a Jatoolbox (Journey Analisys Toolbox) é uma classe implementada em Python, que deve ser encarada como um conjunto de ferramentas úteis para análise de jornadas. Nos métodos da classe o usuário irá encontrar funções prontas e frequentemente úteis para extrair informações das jornadas. Na sessão a seguir serão apresentados alguns exemplos práticos.\n\n## Usando a Jatoobox em alguns exemplos práticos\n\n\n### Instalação e import da biblioteca\n\nAs seguintes linhas de código irão instalar e importar a Jatoolbox\n\n\n```python\n#instalação\n!pip install Jatoolbox\n```\n\n    Collecting Jatoolbox\n      Downloading JATOOLBOX-0.0.2-py3-none-any.whl (6.1 kB)\n    Installing collected packages: Jatoolbox\n    Successfully installed Jatoolbox-0.0.2\n    \n\n\n```python\n#import\nfrom jatoolbox import jatoolbox as jt\n```\n\nCom a linha abaixo, o usuário irá instanciar um objeto da classe Jatoolbox, e a partir dele terá acesso a todos os métodos (funções) da classe:\n\n\n```python\n#instanciando um objeto analisador\nan = jt.JAToolbox()\n```\n\n### Import dos dados\n\n\n```python\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n```\n\n\n```python\nimport pandas as pd\n\nbase = 'https://raw.githubusercontent.com/DP6/customer-journey-analysis-toolbox/main/base_demonstracao.csv?token=ATC3AU2LXPVSV6B5F6G3GU3BRF35G'\n\ndf = pd.read_csv(base, index_col=0)\ndf.reset_index(inplace = True)\n\n```\n\n\n```python\ndf.head()\n```\n\n\n\n\n\u003cdiv\u003e\n\n\u003ctable border=\"1\" class=\"dataframe\"\u003e\n  \u003cthead\u003e\n    \u003ctr style=\"text-align: right;\"\u003e\n      \u003cth\u003e\u003c/th\u003e\n      \u003cth\u003eid_jornada\u003c/th\u003e\n      \u003cth\u003eid_usuario\u003c/th\u003e\n      \u003cth\u003ehorario_ultima_sessao\u003c/th\u003e\n      \u003cth\u003ejornada\u003c/th\u003e\n      \u003cth\u003etempo_para_conversao\u003c/th\u003e\n      \u003cth\u003etransacoes\u003c/th\u003e\n      \u003cth\u003ereceita\u003c/th\u003e\n      \u003cth\u003ehas_transaction\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003e0\u003c/th\u003e\n      \u003ctd\u003e00576442052177483560_0\u003c/td\u003e\n      \u003ctd\u003e0057644205217748356\u003c/td\u003e\n      \u003ctd\u003e2017-07-19T00:09:34Z\u003c/td\u003e\n      \u003ctd\u003eDisplay\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e1\u003c/th\u003e\n      \u003ctd\u003e00666113575347467730_0\u003c/td\u003e\n      \u003ctd\u003e0066611357534746773\u003c/td\u003e\n      \u003ctd\u003e2017-07-19T17:37:50Z\u003c/td\u003e\n      \u003ctd\u003eDirect \u0026gt; Organic Search\u003c/td\u003e\n      \u003ctd\u003e240 \u0026gt; 0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e2\u003c/th\u003e\n      \u003ctd\u003e03252031254674218300_0\u003c/td\u003e\n      \u003ctd\u003e0325203125467421830\u003c/td\u003e\n      \u003ctd\u003e2017-08-01T02:19:08Z\u003c/td\u003e\n      \u003ctd\u003eDirect \u0026gt; Organic Search \u0026gt; Paid Search\u003c/td\u003e\n      \u003ctd\u003e346 \u0026gt; 200 \u0026gt; 0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e3\u003c/th\u003e\n      \u003ctd\u003e03383384021862613320_0\u003c/td\u003e\n      \u003ctd\u003e0338338402186261332\u003c/td\u003e\n      \u003ctd\u003e2017-07-21T12:03:32Z\u003c/td\u003e\n      \u003ctd\u003eDirect \u0026gt; Direct \u0026gt; Direct\u003c/td\u003e\n      \u003ctd\u003e140 \u0026gt; 16 \u0026gt; 0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e4\u003c/th\u003e\n      \u003ctd\u003e0720215518020975470_0\u003c/td\u003e\n      \u003ctd\u003e072021551802097547\u003c/td\u003e\n      \u003ctd\u003e2017-07-28T04:53:24Z\u003c/td\u003e\n      \u003ctd\u003eDisplay \u0026gt; Organic Search \u0026gt; Referral \u0026gt; Referral...\u003c/td\u003e\n      \u003ctd\u003e245 \u0026gt; 215 \u0026gt; 155 \u0026gt; 151 \u0026gt; 143 \u0026gt; 132 \u0026gt; 3 \u0026gt; 0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e0.0\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\n\n\n### Respondendo algumas perguntas com funções da Jatoolbox\n\n#### Quais são os canais introdutores?\n\n\n```python\nintro_df = df.copy()\nintro_df['first_touch_point'] = intro_df['jornada'].apply(lambda x: an.get_first_tp(x))\n\nintro_df = intro_df.groupby('first_touch_point').size().reset_index()\nintro_df.columns = ['first_touch_point', 'journeys']\nintro_df = intro_df.sort_values(by='journeys', ascending=False)\n```\n\n\n```python\nintro_df\n```\n\n\n\n\n\u003cdiv\u003e\n\n\u003ctable border=\"1\" class=\"dataframe\"\u003e\n  \u003cthead\u003e\n    \u003ctr style=\"text-align: right;\"\u003e\n      \u003cth\u003e\u003c/th\u003e\n      \u003cth\u003efirst_touch_point\u003c/th\u003e\n      \u003cth\u003ejourneys\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003e3\u003c/th\u003e\n      \u003ctd\u003eOrganic Search\u003c/td\u003e\n      \u003ctd\u003e27156\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e1\u003c/th\u003e\n      \u003ctd\u003eDirect\u003c/td\u003e\n      \u003ctd\u003e9340\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e6\u003c/th\u003e\n      \u003ctd\u003eSocial\u003c/td\u003e\n      \u003ctd\u003e6997\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e5\u003c/th\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e5128\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e4\u003c/th\u003e\n      \u003ctd\u003ePaid Search\u003c/td\u003e\n      \u003ctd\u003e1524\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e0\u003c/th\u003e\n      \u003ctd\u003eAffiliates\u003c/td\u003e\n      \u003ctd\u003e1325\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e2\u003c/th\u003e\n      \u003ctd\u003eDisplay\u003c/td\u003e\n      \u003ctd\u003e325\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\n\n\n#### Quais são os canais mais conversores? \n\n\n```python\nconv_df = df.copy()\nconv_df = conv_df[conv_df['transacoes'] != 0]\nconv_df['last_touch_point'] = conv_df['jornada'].apply(lambda x: an.get_last_tp(x))\n\nconv_df = conv_df.groupby('last_touch_point').size().reset_index()\nconv_df.columns = ['last_touch_point', 'journeys']\nconv_df = conv_df.sort_values(by='journeys', ascending=False)\n```\n\n    /usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:3: SettingWithCopyWarning: \n    A value is trying to be set on a copy of a slice from a DataFrame.\n    Try using .loc[row_indexer,col_indexer] = value instead\n    \n    See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n      This is separate from the ipykernel package so we can avoid doing imports until\n    \n\n\n```python\nconv_df\n```\n\n\n\n\n\u003cdiv\u003e\n\n\u003ctable border=\"1\" class=\"dataframe\"\u003e\n  \u003cthead\u003e\n    \u003ctr style=\"text-align: right;\"\u003e\n      \u003cth\u003e\u003c/th\u003e\n      \u003cth\u003elast_touch_point\u003c/th\u003e\n      \u003cth\u003ejourneys\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003e5\u003c/th\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e384\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e3\u003c/th\u003e\n      \u003ctd\u003eOrganic Search\u003c/td\u003e\n      \u003ctd\u003e243\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e1\u003c/th\u003e\n      \u003ctd\u003eDirect\u003c/td\u003e\n      \u003ctd\u003e108\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e4\u003c/th\u003e\n      \u003ctd\u003ePaid Search\u003c/td\u003e\n      \u003ctd\u003e61\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e2\u003c/th\u003e\n      \u003ctd\u003eDisplay\u003c/td\u003e\n      \u003ctd\u003e27\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e0\u003c/th\u003e\n      \u003ctd\u003eAffiliates\u003c/td\u003e\n      \u003ctd\u003e4\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e6\u003c/th\u003e\n      \u003ctd\u003eSocial\u003c/td\u003e\n      \u003ctd\u003e3\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\n\n\n#### Qual é a distribuição da quatidade de pontos de contato das jornadas?\n\n\n```python\ndist_df = df.copy()\ndist_df = dist_df['jornada'].apply(lambda x: an.get_size(x))\n\nprint(\"Menor jornada:\",dist_df.min())\nprint(\"Maior jornada:\", dist_df.max())\n\nsns.histplot(data=dist_df, discrete=True)\nplt.show()\n```\n\n    Menor jornada: 1\n    Maior jornada: 46\n    \n\n\n![png](figures/output_21_1.png)\n\n\n#### Quais são as principais transições entre canais?\n\nA jatoolbox possui uma função 'get_transitions' que retorna quais as transações ocorreram em uma dada jornada\n\n\n```python\nj_example = 'A \u003e A \u003e A \u003e B \u003e A \u003e C \u003e B \u003e C'\n```\n\n\n```python\nan.get_transitions(j_example,count=True)\n```\n\n\n\n\n    (A, A)    2\n    (B, A)    1\n    (A, C)    1\n    (C, B)    1\n    (B, C)    1\n    (A, B)    1\n    dtype: int64\n\n\n\nEntão podemos utiliza-la para descobrir quais as principais transições aconteceram na nossa base:\n\n\n```python\ntransitions=df['jornada'].apply(an.get_transitions,count=True)\n```\n\n\n```python\ntransitions_counts = transitions.sum()\ntransitions_counts.sort_values(ascending=False)\n```\n\n\n\n\n    (Organic Search, Organic Search)    2736.0\n    (Referral, Referral)                1933.0\n    (Direct, Direct)                    1850.0\n    (Paid Search, Paid Search)           540.0\n    (Social, Social)                     539.0\n    (Organic Search, Referral)           340.0\n    (Affiliates, Affiliates)             322.0\n    (Paid Search, Organic Search)        271.0\n    (Direct, Organic Search)             227.0\n    (Referral, Organic Search)           219.0\n    (Display, Display)                   192.0\n    (Direct, Referral)                   179.0\n    (Organic Search, Paid Search)        166.0\n    (Organic Search, Display)            119.0\n    (Organic Search, Affiliates)         111.0\n    (Organic Search, Social)              65.0\n    (Affiliates, Organic Search)          54.0\n    (Affiliates, Referral)                54.0\n    (Social, Organic Search)              52.0\n    (Display, Organic Search)             42.0\n    (Social, Referral)                    29.0\n    (Display, Referral)                   29.0\n    (Direct, Display)                     28.0\n    (Referral, Display)                   27.0\n    (Paid Search, Display)                25.0\n    (Direct, Social)                      25.0\n    (Direct, Paid Search)                 24.0\n    (Direct, Affiliates)                  23.0\n    (Referral, Social)                    22.0\n    (Paid Search, Referral)               15.0\n    (Display, Paid Search)                11.0\n    (Referral, Paid Search)                9.0\n    (Social, Display)                      9.0\n    (Social, Paid Search)                  8.0\n    (Referral, Affiliates)                 8.0\n    (Affiliates, Paid Search)              4.0\n    (Paid Search, Affiliates)              4.0\n    (Social, Affiliates)                   3.0\n    (Paid Search, Social)                  3.0\n    (Affiliates, Social)                   3.0\n    (Display, Affiliates)                  1.0\n    (Organic Search, Direct)               1.0\n    (Display, Social)                      1.0\n    (Direct, (Other))                      1.0\n    (Affiliates, Display)                  1.0\n    dtype: float64\n\n\n\nPodemos ver que três transações se destacam: Organic Search para si mesmo, Referral para si mesmo, e Direct para si mesmo. A transição entre canais distintos que mais ocorreu foi de Organic Search para Referral\n\n#### Quais são os canais que mais transitam para o canal mais finalizador?\n\nFoi visto que o canal que mais finaliza jornadas em conversões foi Referral. Então podemos utilizar o resultado obtido no item anterior para obter qual canal mais transiciona para Referral\n\n\n```python\ntrans_df = pd.DataFrame({'from':[x[0] for x in transitions_counts.index],\n                         'to':[x[1] for x in transitions_counts.index],\n                         'counts':transitions_counts.values})\n\ntrans_df.loc[trans_df['to']=='Referral'].sort_values(by='counts',ascending=False)\n```\n\n\n\n\n\u003cdiv\u003e\n\n\u003ctable border=\"1\" class=\"dataframe\"\u003e\n  \u003cthead\u003e\n    \u003ctr style=\"text-align: right;\"\u003e\n      \u003cth\u003e\u003c/th\u003e\n      \u003cth\u003efrom\u003c/th\u003e\n      \u003cth\u003eto\u003c/th\u003e\n      \u003cth\u003ecounts\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003e37\u003c/th\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e1933.0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e25\u003c/th\u003e\n      \u003ctd\u003eOrganic Search\u003c/td\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e340.0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e12\u003c/th\u003e\n      \u003ctd\u003eDirect\u003c/td\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e179.0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e4\u003c/th\u003e\n      \u003ctd\u003eAffiliates\u003c/td\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e54.0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e18\u003c/th\u003e\n      \u003ctd\u003eDisplay\u003c/td\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e29.0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e43\u003c/th\u003e\n      \u003ctd\u003eSocial\u003c/td\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e29.0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e31\u003c/th\u003e\n      \u003ctd\u003ePaid Search\u003c/td\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e15.0\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\n\n\nPodemos ver que os canais que mais transicionaram para Referral foram o próprio Referral, seguido por Organic Search e Direct. \n\n#### Dado que existem canais pagos e canais orgânicos, as jornadas se iniciam mais por canais pagos ou orgânicos?\n\n#### Quanto tempo dura cada jornada?\n\n\n```python\ndef tempo_do_canal(lista):\n  for itens in range(len(lista)):\n    if itens != len(lista)-1:\n      lista[itens] = lista[itens] - lista[itens+1]\n    return lista\n\ndf_tempo_dos_canais = df.copy()\ndf_tempo_dos_canais['tempo'] = df_tempo_dos_canais['tempo_para_conversao'].apply(lambda x: x.split(\" \u003e \"))\ndf_tempo_dos_canais['tamanho'] = df_tempo_dos_canais['tempo'].apply(lambda x: len(x))\ndf_tempo_dos_canais['tempo'] = df_tempo_dos_canais['tempo'].apply(lambda x: [int(item) for item in x])\ndf_tempo_dos_canais['duracao'] = df_tempo_dos_canais['tempo'].apply(lambda x: an.get_duration(x,(-1,0)))\ndf_tempo_dos_canais['duracao_canal'] = df_tempo_dos_canais['tempo'].apply(lambda x: tempo_do_canal(x))\ndf_tempo_dos_canais['lista_canais'] = df_tempo_dos_canais['jornada'].apply(lambda x: x.split(\" \u003e \"))\ndf_tempo_dos_canais = df_tempo_dos_canais[df_tempo_dos_canais['duracao']!=0]\ndf_aux = df_tempo_dos_canais[['lista_canais','tempo']].apply(pd.Series.explode)\ndf_aux = df_aux[df_aux['tempo']!=0]\n```\n\n\n```python\nprint(df_tempo_dos_canais['duracao'].describe())\nsns.boxplot(x = 'duracao', data = df_tempo_dos_canais)\n```\n\n    count    3097.000000\n    mean      223.189538\n    std       218.322967\n    min         1.000000\n    25%        27.000000\n    50%       147.000000\n    75%       389.000000\n    max       719.000000\n    Name: duracao, dtype: float64\n    \n\n\n\n\n    \u003cmatplotlib.axes._subplots.AxesSubplot at 0x7f4eeac63e50\u003e\n\n\n\n\n![png](figures/output_38_2.png)\n\n\nPelo boxplot podemos ver que as jornadas duram em torno de 2h\n\n\n```python\nsns.scatterplot(x = 'duracao', y = 'tamanho' , data = df_tempo_dos_canais, hue = 'has_transaction')\n```\n\n\n\n\n    \u003cmatplotlib.axes._subplots.AxesSubplot at 0x7f4ee94644d0\u003e\n\n\n\n\n![png](figures/output_40_1.png)\n\n\nAlém disso, não há relação entre tamanho da jornada, duração e conversão\n\n#### Há canais em que os usuários demoram mais ou menos até sua próximada etapa da jornada?\n\n\n```python\nsns.boxplot(data = df_aux , y = 'tempo', x ='lista_canais')\n```\n\n              lista_canais  tempo\n    count             9034   9034\n    unique               7    686\n    top     Organic Search      1\n    freq              3165    620\n    \n\n\n\n\n    \u003cmatplotlib.axes._subplots.AxesSubplot at 0x7f4eeae9e710\u003e\n\n\n\n\n![png](figures/output_43_2.png)\n\n\nO canal \"Affiliates\" é o canal onde os usuários demoram menos tempo, em geral, até sua próxima ação.\n\n#### Qual é o canal que mais aparece nas jornadas? E nas jornadas que terminaram em transação?\n\n\n```python\ndf_agrupado = df[['jornada','has_transaction','transacoes']].copy()\ndf_agrupado = df_agrupado[['jornada','has_transaction']].groupby(['jornada','has_transaction']).size().reset_index(name='ocurrencies').merge(df_agrupado.groupby(['jornada','has_transaction']).sum().reset_index(),on=['jornada','has_transaction'])\n```\n\n\n\n\n\u003cdiv\u003e\n\n\u003ctable border=\"1\" class=\"dataframe\"\u003e\n  \u003cthead\u003e\n    \u003ctr style=\"text-align: right;\"\u003e\n      \u003cth\u003e\u003c/th\u003e\n      \u003cth\u003ejornada\u003c/th\u003e\n      \u003cth\u003ehas_transaction\u003c/th\u003e\n      \u003cth\u003eocurrencies\u003c/th\u003e\n      \u003cth\u003etransacoes\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003e0\u003c/th\u003e\n      \u003ctd\u003eAffiliates\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e1090\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e1\u003c/th\u003e\n      \u003ctd\u003eAffiliates\u003c/td\u003e\n      \u003ctd\u003e1\u003c/td\u003e\n      \u003ctd\u003e2\u003c/td\u003e\n      \u003ctd\u003e2\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e2\u003c/th\u003e\n      \u003ctd\u003eAffiliates \u0026gt; Affiliates\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e104\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e3\u003c/th\u003e\n      \u003ctd\u003eAffiliates \u0026gt; Affiliates\u003c/td\u003e\n      \u003ctd\u003e1\u003c/td\u003e\n      \u003ctd\u003e1\u003c/td\u003e\n      \u003ctd\u003e1\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e4\u003c/th\u003e\n      \u003ctd\u003eAffiliates \u0026gt; Affiliates \u0026gt; Affiliates\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n      \u003ctd\u003e27\u003c/td\u003e\n      \u003ctd\u003e0\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\n\n\n\n```python\nqtd_canais = an.tps_by_channel(df = df_agrupado, j = 'jornada', ocurrencies = 'transacoes').merge(an.tps_by_channel(df = df_agrupado, j = 'jornada', ocurrencies = 'ocurrencies'), on='Channel')\nqtd_canais.columns = ['Channel','transacoes','ocurrencies']\nqtd_canais\n```\n\n\n\n\n\u003cdiv\u003e\n\n\u003ctable border=\"1\" class=\"dataframe\"\u003e\n  \u003cthead\u003e\n    \u003ctr style=\"text-align: right;\"\u003e\n      \u003cth\u003e\u003c/th\u003e\n      \u003cth\u003eChannel\u003c/th\u003e\n      \u003cth\u003etransacoes\u003c/th\u003e\n      \u003cth\u003eocurrencies\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003e0\u003c/th\u003e\n      \u003ctd\u003eAffiliates\u003c/td\u003e\n      \u003ctd\u003e7\u003c/td\u003e\n      \u003ctd\u003e1797\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e1\u003c/th\u003e\n      \u003ctd\u003eOrganic Search\u003c/td\u003e\n      \u003ctd\u003e522\u003c/td\u003e\n      \u003ctd\u003e30757\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e2\u003c/th\u003e\n      \u003ctd\u003eDirect\u003c/td\u003e\n      \u003ctd\u003e247\u003c/td\u003e\n      \u003ctd\u003e11191\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e3\u003c/th\u003e\n      \u003ctd\u003eReferral\u003c/td\u003e\n      \u003ctd\u003e841\u003c/td\u003e\n      \u003ctd\u003e7707\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e4\u003c/th\u003e\n      \u003ctd\u003eDisplay\u003c/td\u003e\n      \u003ctd\u003e71\u003c/td\u003e\n      \u003ctd\u003e726\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e5\u003c/th\u003e\n      \u003ctd\u003ePaid Search\u003c/td\u003e\n      \u003ctd\u003e129\u003c/td\u003e\n      \u003ctd\u003e2286\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e6\u003c/th\u003e\n      \u003ctd\u003eSocial\u003c/td\u003e\n      \u003ctd\u003e6\u003c/td\u003e\n      \u003ctd\u003e7655\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/div\u003e\n\n\n\nApesar do canal Orgânico ser o quie mais aparece de forma geral, o Referral foi o que mais apareceu em jornadas conversoras.\n\n#### Como é a distribuição de canal ao longo das etapas da jornada?\n\n\n```python\ndf_heatmap = an.channels_by_tp(df_agrupado, j = 'jornada', ocurrencies = 'ocurrencies', max_journey_size=10)\n\nfig, ax = plt.subplots(figsize=(14,10))\ndf_heatmap.set_index('channels',inplace=True)\nsns.heatmap(data = df_heatmap, annot = True, fmt=\".10g\", cmap=\"YlGnBu\")\n```\n\n\n\n\n    \u003cmatplotlib.axes._subplots.AxesSubplot at 0x7f4ee8e5f550\u003e\n\n\n\n\n![png](figures/output_50_1.png)\n\n\n## Tabela de funções presentes na Jatoolbox (Função vs. O que ela Faz)\n\n Nome da Função ⚙|  Entradas 🚪|  Ação 🦾 \n-- | -- | --\nget_size | \u003cli\u003e journey \u003c/li\u003e\u003cli\u003e separator \u003c/li\u003e | Calculates the number of touch points in a given journey.\nget_last_tp | \u003cli\u003e journey \u003c/li\u003e\u003cli\u003e separator \u003c/li\u003e| Returns the last touch point of the journey.\nget_first_tp | \u003cli\u003e journey \u003c/li\u003e\u003cli\u003e separator \u003c/li\u003e| Returns the first touch point of the journey.\nget_nth_tp | \u003cli\u003e journey \u003c/li\u003e\u003cli\u003e separator \u003c/li\u003e| Returns the n_th touch point of the journey.\nget_intermediate_tp | \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e range \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e | Returns a subjourney formed by the touch points \u003cbr\u003e between the points indicated in the range parameter.\nget_tps_counts | \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e norm \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e | Returns how many times each distinct touch point \u003cbr\u003e occurres in the journey.\nskip_tp | \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e tp_to_skip \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e | Returns a transformed version of the journey, where \u003cbr\u003e all occurencies of a given touch point is skipped\nskip_tp_group | \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e tps_to_skip \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e |Returns a transformed version of the journey, where \u003cbr\u003e all occurencies of any element from a given group of \u003cbr\u003e touch points is skipped\ncheck_tp | \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e tp_to_check \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e |Checks if there is any occurency of a indicated touch \u003cbr\u003e point in the journey.\ncheck_tp_group | \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e tp_group_to_check \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e | Checks if there is any occurency of each touch \u003cbr\u003e indicated in a group of touch points.\nget_tp_counts | \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e tp \u003c/li\u003e \u003cli\u003e norm \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e | Returns how many occurrecies of a given touch \u003cbr\u003e point there is in the journey\nget_duration | \u003cli\u003e timestamps \u003c/li\u003e \u003cli\u003e range \u003c/li\u003e | Returns the time interval between two indicated touch \u003cbr\u003e points in the journey. By default the interval between \u003cbr\u003e the first and the last touch point.\ntranslate_tp | \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e translation_dict \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e | Returns a transformed version of the journey, where \u003cbr\u003e indicated touch points are replaced by other values \u003cbr\u003e according to a traslation dictionary.\nget_transitions | \u003cli\u003e self \u003c/li\u003e \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e count \u003c/li\u003e \u003cli\u003e norm \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e | Returns all the transitions between two touch points that \u003cbr\u003e occured in the journey. If the optional parameter count is \u003cbr\u003e set to True, the occurency counts for each transition is also  \u003cbr\u003e  returned.   \nchannels_by_tp | \u003cli\u003e dataframe \u003c/li\u003e \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e ocurrencies \u003c/li\u003e \u003cli\u003e max_journey_size \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e | How many times did each channel appear at each stage of \u003cbr\u003e the journeys.\ntps_by_channel | \u003cli\u003e self \u003c/li\u003e \u003cli\u003e dataframe \u003c/li\u003e \u003cli\u003e journey \u003c/li\u003e \u003cli\u003e ocurrencies \u003c/li\u003e \u003cli\u003e separator \u003c/li\u003e |How many times each channel appeared on the journeys.\n\n## Links úteis\n\n[MAM - Marketing Attribution Models](https://github.com/DP6/Marketing-Attribution-Models)\n\n[Github da DP6](https://dp6.github.io/)\n\n[Blog da DP6](https://medium.com/@dp6blog)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdp6%2Fcustomer-journey-analysis-toolbox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdp6%2Fcustomer-journey-analysis-toolbox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdp6%2Fcustomer-journey-analysis-toolbox/lists"}