import%20marimo%0A%0A__generated_with%20%3D%20%220.15.2%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%20B3%20Index%20Composition%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%23%201.%20Library%20Imports%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20os%0A%20%20%20%20import%20requests%0A%20%20%20%20import%20json%0A%20%20%20%20import%20base64%0A%20%20%20%20from%20datetime%20import%20datetime%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20from%20matplotlib.patches%20import%20Rectangle%0A%20%20%20%20import%20seaborn%20as%20sns%0A%20%20%20%20return%20base64%2C%20datetime%2C%20json%2C%20np%2C%20os%2C%20pd%2C%20plt%2C%20requests%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%201.%20Available%20Indexes%0A%0A%20%20%20%20The%20index%20composition%20with%20all%20stocks%20that%20comprise%20them%20can%20be%20found%20on%20the%20official%20website%20of%20the%20brazilian%20stock%20exchange%20(B3)%20using%20the%20following%20links%3A%0A%0A%20%20%20%20%23%23%23%20Broad%20Indices%3A%20%0A%20%20%20%20-%20%5BBovespa%20Index%20(Ibovespa)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findices%2Fbroad-indices%2Findice-ibovespa-ibovespa-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBovespa%20B3%20BR%2B%20(Ibovespa%20B3%20BR%2B)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Fbroad-indexes%2Fbovespa-b3-br-index-ibovespa-b3-br-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BB3%20BR%2B%20Equal%20Weight%20Bovespa%20Index%20(Ibovespa%20B3%20BR%2B%20Equal%20Weight)**%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Fbroad-indexes%2Fb3-br-equal-weight-bovespa-index-ibovespa-b3-br-equal-weight-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBrazil%20100%20Index%20(IBrX%20100)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Fbroad-indexes%2Findice-brasil-100-ibrx-100-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBrazil%2050%20Index%20(IBrX%2050)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Fbroad-indexes%2Findice-brasil-50-ibrx-50-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBrazil%20Broad-Based%20Index%20(IBrA)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Fbroad-indexes%2Fbrazil-broad-based-index-ibra-composition-index-portfolio.htm)%20%0A%0A%20%20%20%20%23%23%23%20Indices%20for%20Segments%20and%20Sectors%3A%20%0A%20%20%20%20-%20%5BBM%26FBOVESPA%20Basic%20Materials%20Index%20(IMAT)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Fbasic-materials-index-imat-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBM%26FBOVESPA%20Consumer%20Stock%20Index%20(ICON)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Fconsumer-stock-index-icon-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBM%26FBOVESPA%20Dividend%20Index%20(IDIV)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Fdividend-index-idiv-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBM%26FBOVESPA%20Electric%20Utilities%20Index%20(IEE)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Felectric-utilities-index-iee-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBM%26FBOVESPA%20Financials%20Index%20(IFNC)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Ffinancials-index-ifnc-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBM%26FBOVESPA%20Industrials%20Index%20(INDX)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Findustrials-index-indx-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBM%26FBOVESPA%20Public%20Utilities%20Index%20(UTIL)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Fpublic-utilities-index-util-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BBM%26FBOVESPA%20Real%20Estate%20Index%20(IMOB)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Freal-estate-index-imob-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BSmallCap%20Index%20(SMLL)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Fsmallcap-index-smll-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BUnsponsored%20BDR%20Index-GLOBAL%20(BRDX)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Funsponsored-bdr-index-global-bdrx-composition-index-portfolio.htm)%0A%20%20%20%20-%20%5BValor%20BM%26FBOVESPA%20Index%20(IVBX%202)%5D(https%3A%2F%2Fwww.b3.com.br%2Fen_us%2Fmarket-data-and-indices%2Findexes%2Findexes-for-segments-and-sectors%2Fvalor-index-ivbx-2-composition-index-portfolio.htm)%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%23%202.%20Select%20Index%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(base64%2C%20json%2C%20requests)%3A%0A%20%20%20%20def%20get_available_indexes()%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Make%20a%20request%20to%20the%20specified%20URL%20and%20return%20a%20list%20of%20all%20available%20indexes.%0A%0A%20%20%20%20%20%20%20%20Parameters%3A%0A%20%20%20%20%20%20%20%20-----------%0A%20%20%20%20%20%20%20%20url%20%3A%20str%0A%20%20%20%20%20%20%20%20%20%20%20%20The%20URL%20to%20make%20the%20request%20to%0A%0A%20%20%20%20%20%20%20%20Returns%3A%0A%20%20%20%20%20%20%20%20--------%0A%20%20%20%20%20%20%20%20list%0A%20%20%20%20%20%20%20%20%20%20%20%20List%20of%20all%20available%20indexes%20as%20strings%0A%20%20%20%20%20%20%20%20%22%22%22%0A%0A%20%20%20%20%20%20%20%20base64.decodebytes(b%22eyJwYWdlTnVtYmVyIjoxLCJwYWdlU2l6ZSI6MjB9%22)%0A%20%20%20%20%20%20%20%20params%20%3D%20json.dumps(%7B%22pageNumber%22%3A%201%2C%20%22pageSize%22%3A%20999%7D)%0A%20%20%20%20%20%20%20%20params_enc%20%3D%20base64.encodebytes(bytes(params%2C%20%22utf8%22)).decode(%22utf8%22)%0A%0A%20%20%20%20%20%20%20%20url%20%3D%20f%22https%3A%2F%2Fsistemaswebb3-listados.b3.com.br%2FindexProxy%2FindexCall%2FGetStockIndex%2F%7Bparams_enc.strip()%7D%22%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Make%20the%20request%0A%20%20%20%20%20%20%20%20%20%20%20%20response%20%3D%20requests.get(url)%0A%20%20%20%20%20%20%20%20%20%20%20%20response.raise_for_status()%20%20%23%20Raise%20an%20exception%20for%20bad%20status%20codes%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Parse%20JSON%20response%0A%20%20%20%20%20%20%20%20%20%20%20%20data%20%3D%20response.json()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Extract%20results%0A%20%20%20%20%20%20%20%20%20%20%20%20results%20%3D%20data.get('results'%2C%20%5B%5D)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Get%20all%20indixes%20for%20each%20company%0A%20%20%20%20%20%20%20%20%20%20%20%20all_indexes%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20item%20in%20results%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20indexes%20%3D%20item%5B'indexes'%5D.split('%2C')%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20all_indexes%20%2B%3D%20indexes%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20sorted(list(set(all_indexes)))%0A%0A%20%20%20%20%20%20%20%20except%20requests.exceptions.RequestException%20as%20e%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Error%20making%20request%3A%20%7Be%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%5B%5D%0A%20%20%20%20%20%20%20%20except%20KeyError%20as%20e%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Error%20parsing%20response%3A%20%7Be%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%5B%5D%0A%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Unexpected%20error%3A%20%7Be%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%5B%5D%0A%20%20%20%20return%20(get_available_indexes%2C)%0A%0A%0A%40app.cell%0Adef%20_(get_available_indexes%2C%20mo)%3A%0A%20%20%20%20list_index%20%3D%20get_available_indexes()%0A%0A%20%20%20%20callout_index%20%3D%20mo.ui.dropdown(%0A%20%20%20%20%20%20%20%20label%3D%22Select%20Index%22%2C%20%0A%20%20%20%20%20%20%20%20options%3Dlist_index%2C%20%0A%20%20%20%20%20%20%20%20value%3D'IBOV'%0A%20%20%20%20)%0A%20%20%20%20return%20(callout_index%2C)%0A%0A%0A%40app.cell%0Adef%20_(base64%2C%20datetime%2C%20json%2C%20os%2C%20pd%2C%20requests)%3A%0A%20%20%20%20def%20return_index(index%3Astr)%3A%20%0A%0A%20%20%20%20%20%20%20%20URL_B3%20%3D%20'https%3A%2F%2Fsistemaswebb3-listados.b3.com.br%2FindexProxy%2FindexCall%2FGetPortfolioDay%2F'%0A%20%20%20%20%20%20%20%20DEFAULT_PAYLOAD%20%3D%20%7B'language'%3A%20'pt-br'%2C%20'index'%3A%20index.upper().strip()%2C%20'segment'%3A%20'2'%7D%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20payload_json%20%3D%20json.dumps(DEFAULT_PAYLOAD)%0A%20%20%20%20%20%20%20%20%20%20%20%20payload_b64%20%3D%20base64.b64encode(payload_json.encode()).decode()%0A%20%20%20%20%20%20%20%20%20%20%20%20url%20%3D%20URL_B3%20%2B%20payload_b64%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20response%20%3D%20requests.get(url)%0A%20%20%20%20%20%20%20%20%20%20%20%20response.raise_for_status()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20dados_json%20%3D%20response.json()%0A%20%20%20%20%20%20%20%20%20%20%20%20lista_acoes%20%3D%20dados_json.get('results')%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20lista_acoes%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(%22key%20'results'%20not%20found%20or%20empty%20json.%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20pd.DataFrame(lista_acoes)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20casting%20of%20cols%0A%20%20%20%20%20%20%20%20%20%20%20%20df%5B'part'%5D%20%3D%20df%5B'part'%5D.str.replace('%2C'%2C%20'.').astype(float)%0A%20%20%20%20%20%20%20%20%20%20%20%20df%5B'partAcum'%5D%20%3D%20df%5B'partAcum'%5D.str.replace('%2C'%2C%20'.').astype(float)%0A%20%20%20%20%20%20%20%20%20%20%20%20df%5B'theoricalQty'%5D%20%3D%20df%5B'theoricalQty'%5D.str.replace('.'%2C%20''%2C%20regex%3DFalse).astype(int)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20sort%20by%20'part'%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20df.sort_values('part'%2C%20ascending%3DFalse)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20add%20date%0A%20%20%20%20%20%20%20%20%20%20%20%20today_date%20%3D%20datetime.now().strftime('%25Y-%25m-%25d')%0A%20%20%20%20%20%20%20%20%20%20%20%20df%5B'date'%5D%20%3D%20today_date%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20df.sort_values(by%3D'part'%2C%20ascending%3DFalse)%5B%5B'date'%2C%20'cod'%2C%20'segment'%2C%20'part'%5D%5D%0A%0A%20%20%20%20%20%20%20%20except%20requests.exceptions.RequestException%20as%20e%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22HTTP%20request%20errror%3A%20%7Be%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%20%20%20%20except%20json.JSONDecodeError%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22json%20decode%20error.%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22unexpected%20error%3A%20%7Be%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%0A%20%20%20%20def%20salvar_dados(df%3A%20pd.DataFrame%2C%20index%3A%20str)%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Salva%20o%20DataFrame%20em%20arquivos%20CSV%20e%20Parquet.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20if%20df%20is%20None%20or%20df.empty%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22O%20DataFrame%20est%C3%A1%20vazio.%20Nenhum%20arquivo%20ser%C3%A1%20salvo.%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20dir_path%20%3D%20os.path.join('data'%2C%20f'%7Bindex.lower()%7D%20composition')%0A%20%20%20%20%20%20%20%20%20%20%20%20os.makedirs(dir_path%2C%20exist_ok%3DTrue)%0A%20%20%20%20%20%20%20%20%20%20%20%20today_date%20%3D%20datetime.now().strftime('%25Y-%25m-%25d')%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20caminho_csv%20%3D%20os.path.join(dir_path%2C%20f%22%7Btoday_date%7D.csv%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20df.to_csv(caminho_csv%2C%20index%3DFalse%2C%20encoding%3D'utf-8-sig')%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%5CnArquivo%20salvo%20com%20sucesso%20em%3A%20%7Bcaminho_csv%7D%22)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20caminho_parquet%20%3D%20os.path.join(dir_path%2C%20f%22%7Btoday_date%7D.parquet%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20df.to_parquet(caminho_parquet%2C%20index%3DFalse%2C%20engine%3D'pyarrow')%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Arquivo%20salvo%20com%20sucesso%20em%3A%20%7Bcaminho_parquet%7D%22)%0A%0A%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Erro%20ao%20salvar%20os%20arquivos%3A%20%7Be%7D%22)%0A%20%20%20%20return%20return_index%2C%20salvar_dados%0A%0A%0A%40app.cell%0Adef%20_(callout_index%2C%20return_index)%3A%0A%20%20%20%20df%20%3D%20return_index(index%3Dcallout_index.value)%0A%20%20%20%20return%20(df%2C)%0A%0A%0A%40app.cell%0Adef%20_(callout_index%2C%20df%2C%20salvar_dados)%3A%0A%20%20%20%20if%20df%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20salvar_dados(df%2C%20callout_index.value)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(callout_index%2C%20df%2C%20mo)%3A%0A%20%20%20%20mo.vstack(%5Bcallout_index%2C%20df%5D%2C%20align%3D'stretch'%2C%20gap%3D0)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20pd%2C%20plt)%3A%0A%20%20%20%20def%20top_part(data%2C%20n%3D10)%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Extract%20top%20n%20parted%20components%20and%20prepare%20data%20for%20donut%20chart%20visualization.%0A%0A%20%20%20%20%20%20%20%20Parameters%3A%0A%20%20%20%20%20%20%20%20-----------%0A%20%20%20%20%20%20%20%20data%20%3A%20pd.DataFrame%0A%20%20%20%20%20%20%20%20%20%20%20%20DataFrame%20containing%20'cod'%20and%20'part'%20columns%0A%20%20%20%20%20%20%20%20n%20%3A%20int%2C%20default%3D10%0A%20%20%20%20%20%20%20%20%20%20%20%20Number%20of%20top%20components%20to%20extract%0A%0A%20%20%20%20%20%20%20%20Returns%3A%0A%20%20%20%20%20%20%20%20--------%0A%20%20%20%20%20%20%20%20pd.DataFrame%0A%20%20%20%20%20%20%20%20%20%20%20%20DataFrame%20with%20columns%3A%20cod%2C%20part%2C%20cum_part%2C%20ymax%2C%20ymin%2C%20label_pos%2C%20label%0A%20%20%20%20%20%20%20%20%22%22%22%0A%0A%20%20%20%20%20%20%20%20%23%20Get%20top%20n%20components%20by%20'part'%0A%20%20%20%20%20%20%20%20top_n%20%3D%20(data%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.head(n)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B%5B'cod'%2C%20'part'%5D%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.copy())%0A%0A%20%20%20%20%20%20%20%20%23%20Calculate%20total%20part%20of%20top%20components%0A%20%20%20%20%20%20%20%20total_part%20%3D%20top_n%5B'part'%5D.sum()%0A%0A%20%20%20%20%20%20%20%20%23%20Create%20%22Others%22%20category%20for%20remaining%20part%0A%20%20%20%20%20%20%20%20others%20%3D%20pd.DataFrame(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20'cod'%3A%20%5B'Others'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20'part'%3A%20%5B100%20-%20total_part%5D%0A%20%20%20%20%20%20%20%20%7D)%0A%0A%20%20%20%20%20%20%20%20%23%20Combine%20top%20components%20with%20others%0A%20%20%20%20%20%20%20%20result%20%3D%20pd.concat(%5Btop_n%2C%20others%5D%2C%20ignore_index%3DTrue)%0A%0A%20%20%20%20%20%20%20%20%23%20Calculate%20cumulative%20parts%20and%20positions%20for%20visualization%0A%20%20%20%20%20%20%20%20result%5B'cum_part'%5D%20%3D%20result%5B'part'%5D.cumsum()%0A%20%20%20%20%20%20%20%20result%5B'ymax'%5D%20%3D%20result%5B'cum_part'%5D%0A%20%20%20%20%20%20%20%20result%5B'ymin'%5D%20%3D%20np.concatenate(%5B%5B0%5D%2C%20result%5B'cum_part'%5D.iloc%5B%3A-1%5D.values%5D)%0A%20%20%20%20%20%20%20%20result%5B'label_pos'%5D%20%3D%20(result%5B'ymax'%5D%20%2B%20result%5B'ymin'%5D)%20%2F%202%0A%20%20%20%20%20%20%20%20result%5B'label'%5D%20%3D%20result%5B'cod'%5D%20%2B%20'%5Cn'%20%2B%20(result%5B'part'%5D).round(1).astype(str)%20%2B%20'%25'%0A%0A%20%20%20%20%20%20%20%20%23%20Convert%20cod%20to%20categorical%20for%20consistent%20ordering%0A%20%20%20%20%20%20%20%20result%5B'cod'%5D%20%3D%20pd.Categorical(result%5B'cod'%5D%2C%20categories%3Dresult%5B'cod'%5D%2C%20ordered%3DTrue)%0A%0A%20%20%20%20%20%20%20%20return%20result%0A%0A%20%20%20%20def%20create_donut_chart(data%2C%20index_name%2C%20figsize%3D(10%2C%2010))%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Create%20a%20donut%20chart%20visualization%20from%20participation%20data.%0A%0A%20%20%20%20%20%20%20%20Parameters%3A%0A%20%20%20%20%20%20%20%20-----------%0A%20%20%20%20%20%20%20%20data%20%3A%20pd.DataFrame%0A%20%20%20%20%20%20%20%20%20%20%20%20DataFrame%20with%20columns%3A%20ymax%2C%20ymin%2C%20cod%2C%20label_pos%2C%20label%20(from%20top_part%20function)%0A%20%20%20%20%20%20%20%20index_name%20%3A%20str%0A%20%20%20%20%20%20%20%20%20%20%20%20Name%20of%20the%20index%20to%20display%20in%20the%20center%0A%20%20%20%20%20%20%20%20figsize%20%3A%20tuple%2C%20default%3D(10%2C%2010)%0A%20%20%20%20%20%20%20%20%20%20%20%20Figure%20size%20for%20the%20plot%0A%0A%20%20%20%20%20%20%20%20Returns%3A%0A%20%20%20%20%20%20%20%20--------%0A%20%20%20%20%20%20%20%20matplotlib.figure.Figure%0A%20%20%20%20%20%20%20%20%20%20%20%20The%20created%20figure%20object%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3Dfigsize%2C%20subplot_kw%3Ddict(projection%3D'polar'))%0A%0A%20%20%20%20%20%20%20%20%23%20Set%20theta%20direction%20and%20zero%20location%0A%20%20%20%20%20%20%20%20ax.set_theta_direction(-1)%0A%20%20%20%20%20%20%20%20ax.set_theta_zero_location('N')%0A%0A%20%20%20%20%20%20%20%20%23%20Create%20color%20palette%0A%20%20%20%20%20%20%20%20colors%20%3D%20plt.cm.Set3(np.linspace(0%2C%201%2C%20len(data)))%0A%0A%20%20%20%20%20%20%20%20%23%20Draw%20donut%20segments%0A%20%20%20%20%20%20%20%20for%20i%2C%20row%20in%20data.iterrows()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Convert%20percentages%20to%20radians%20(divide%20by%20100%20to%20get%20fraction%2C%20then%20multiply%20by%202%CF%80)%0A%20%20%20%20%20%20%20%20%20%20%20%20theta1%20%3D%20(row%5B'ymin'%5D%20%2F%20100)%20*%202%20*%20np.pi%0A%20%20%20%20%20%20%20%20%20%20%20%20theta2%20%3D%20(row%5B'ymax'%5D%20%2F%20100)%20*%202%20*%20np.pi%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Create%20wedge%20for%20donut%20segment%0A%20%20%20%20%20%20%20%20%20%20%20%20theta%20%3D%20np.linspace(theta1%2C%20theta2%2C%20100)%0A%20%20%20%20%20%20%20%20%20%20%20%20r_inner%20%3D%20np.full_like(theta%2C%200.6)%0A%20%20%20%20%20%20%20%20%20%20%20%20r_outer%20%3D%20np.full_like(theta%2C%201.0)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20ax.fill_between(theta%2C%20r_inner%2C%20r_outer%2C%20color%3Dcolors%5Bi%5D%2C%20alpha%3D0.8%2C%20edgecolor%3D'white'%2C%20linewidth%3D2)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Add%20labels%20-%20calculate%20middle%20angle%20for%20label%20positioning%0A%20%20%20%20%20%20%20%20%20%20%20%20theta_mid%20%3D%20(theta1%20%2B%20theta2)%20%2F%202%0A%20%20%20%20%20%20%20%20%20%20%20%20r_label%20%3D%201.15%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Only%20show%20labels%20for%20segments%20larger%20than%202%25%20to%20avoid%20overcrowding%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20row%5B'part'%5D%20%3E%202%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ax.text(theta_mid%2C%20r_label%2C%20row%5B'label'%5D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ha%3D'center'%2C%20va%3D'center'%2C%20fontsize%3D9%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20bbox%3Ddict(boxstyle%3D'round%2Cpad%3D0.3'%2C%20facecolor%3D'white'%2C%20alpha%3D0.8))%0A%20%20%20%20%20%20%20%20%20%20%20%20ax.text(0%2C%200%2C%20index_name%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fontsize%3D16%2C%20fontweight%3D'bold'%2C%20color%3D'grey')%0A%0A%20%20%20%20%20%20%20%20%23%20Style%20the%20plot%0A%20%20%20%20%20%20%20%20ax.set_ylim(0%2C%201.3)%0A%20%20%20%20%20%20%20%20ax.set_rticks(%5B%5D)%0A%20%20%20%20%20%20%20%20ax.set_thetagrids(%5B%5D)%0A%20%20%20%20%20%20%20%20ax.spines%5B'polar'%5D.set_visible(False)%0A%20%20%20%20%20%20%20%20ax.grid(False)%0A%0A%20%20%20%20%20%20%20%20plt.tight_layout()%0A%20%20%20%20%20%20%20%20plt.show()%0A%0A%20%20%20%20%23%20Example%20usage%20function%0A%20%20%20%20def%20plot_index_composition(data%2C%20index_name%2C%20n%3D10%2C%20figsize%3D(8%2C%208))%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Complete%20workflow%20to%20create%20donut%20chart%20from%20raw%20part%20data.%0A%0A%20%20%20%20%20%20%20%20Parameters%3A%0A%20%20%20%20%20%20%20%20-----------%0A%20%20%20%20%20%20%20%20data%20%3A%20pd.DataFrame%0A%20%20%20%20%20%20%20%20%20%20%20%20DataFrame%20with%20'cod'%20and%20'part'%20columns%0A%20%20%20%20%20%20%20%20index_name%20%3A%20str%0A%20%20%20%20%20%20%20%20%20%20%20%20Name%20of%20the%20index%0A%20%20%20%20%20%20%20%20n%20%3A%20int%2C%20default%3D10%0A%20%20%20%20%20%20%20%20%20%20%20%20Number%20of%20top%20components%20to%20show%0A%20%20%20%20%20%20%20%20figsize%20%3A%20tuple%2C%20default%3D(8%2C%208)%0A%20%20%20%20%20%20%20%20%20%20%20%20Figure%20size%0A%0A%20%20%20%20%20%20%20%20Returns%3A%0A%20%20%20%20%20%20%20%20--------%0A%20%20%20%20%20%20%20%20matplotlib.figure.Figure%0A%20%20%20%20%20%20%20%20%20%20%20%20The%20created%20donut%20chart%20figure%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20processed_data%20%3D%20top_part(data%2C%20n%3Dn)%0A%20%20%20%20%20%20%20%20return%20create_donut_chart(processed_data%2C%20index_name%2C%20figsize%3Dfigsize)%0A%20%20%20%20return%20(plot_index_composition%2C)%0A%0A%0A%40app.cell%0Adef%20_(callout_index%2C%20df%2C%20plot_index_composition)%3A%0A%20%20%20%20plot_index_composition(df%2C%20index_name%3Dcallout_index.value)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
169ccfbc031697bcd871e9fe8facca72461e859bb33f46a6d1ca5b0f5b0aaace