{ "cells": [ { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "try:\n", " markets = pd.read_parquet(\"../data/fpmms.parquet\")\n", "except Exception:\n", " print(\"Error reading the parquet file\")\n", "\n", "markets[\"currentAnswer\"] = markets[\"currentAnswer\"].apply(lambda x: x.lower())\n", "# filter only markets with yes, no answers\n", "valid_answers = [\"yes\", \"no\"]\n", "markets = markets.loc[markets[\"currentAnswer\"].isin(valid_answers)]" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "try:\n", " markets_df = pd.read_parquet(\"../data/fpmmTrades.parquet\")\n", "except Exception:\n", " print(\"Error reading the parquet file\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 61442 entries, 0 to 61441\n", "Data columns (total 24 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 collateralAmount 61442 non-null object\n", " 1 collateralAmountUSD 61442 non-null object\n", " 2 collateralToken 61442 non-null object\n", " 3 creationTimestamp 61442 non-null object\n", " 4 trader_address 61442 non-null object\n", " 5 feeAmount 61442 non-null object\n", " 6 id 61442 non-null object\n", " 7 oldOutcomeTokenMarginalPrice 61442 non-null object\n", " 8 outcomeIndex 61442 non-null object\n", " 9 outcomeTokenMarginalPrice 61442 non-null object\n", " 10 outcomeTokensTraded 61442 non-null object\n", " 11 title 61442 non-null object\n", " 12 transactionHash 61442 non-null object\n", " 13 type 61442 non-null object\n", " 14 market_creator 61442 non-null object\n", " 15 fpmm.answerFinalizedTimestamp 47113 non-null object\n", " 16 fpmm.arbitrationOccurred 61442 non-null bool \n", " 17 fpmm.currentAnswer 47113 non-null object\n", " 18 fpmm.id 61442 non-null object\n", " 19 fpmm.isPendingArbitration 61442 non-null bool \n", " 20 fpmm.openingTimestamp 61442 non-null object\n", " 21 fpmm.outcomes 61442 non-null object\n", " 22 fpmm.title 61442 non-null object\n", " 23 fpmm.condition.id 61442 non-null object\n", "dtypes: bool(2), object(22)\n", "memory usage: 10.4+ MB\n" ] } ], "source": [ "markets_df.info()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['collateralAmount', 'collateralAmountUSD', 'collateralToken',\n", " 'creationTimestamp', 'trader_address', 'feeAmount', 'id',\n", " 'oldOutcomeTokenMarginalPrice', 'outcomeIndex',\n", " 'outcomeTokenMarginalPrice', 'outcomeTokensTraded', 'title',\n", " 'transactionHash', 'type', 'market_creator',\n", " 'fpmm.answerFinalizedTimestamp', 'fpmm.arbitrationOccurred',\n", " 'fpmm.currentAnswer', 'fpmm.id', 'fpmm.isPendingArbitration',\n", " 'fpmm.openingTimestamp', 'fpmm.outcomes', 'fpmm.title',\n", " 'fpmm.condition.id'],\n", " dtype='object')" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "markets_df.columns" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4686" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(markets.id.unique())" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
currentAnsweridtitlemarket_creator
0no0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5Will the first floating offshore wind research...quickstart
1no0x0020d13c89140b47e10db54cbd53852b90bc1391Will the Francis Scott Key Bridge in Baltimore...quickstart
2no0x003ae5e007cc38b3f86b0ed7c82f938a1285ac07Will FC Saarbrucken reach the final of the Ger...quickstart
3yes0x004c8d4c619dc6b9caa940f5ea7ef699ae85359cWill the pro-life activists convicted for 'con...quickstart
4yes0x005e3f7a90585acbec807425a750fbba1d0c2b5cWill Apple announce the release of a new M4 ch...quickstart
\n", "
" ], "text/plain": [ " currentAnswer id \\\n", "0 no 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5 \n", "1 no 0x0020d13c89140b47e10db54cbd53852b90bc1391 \n", "2 no 0x003ae5e007cc38b3f86b0ed7c82f938a1285ac07 \n", "3 yes 0x004c8d4c619dc6b9caa940f5ea7ef699ae85359c \n", "4 yes 0x005e3f7a90585acbec807425a750fbba1d0c2b5c \n", "\n", " title market_creator \n", "0 Will the first floating offshore wind research... quickstart \n", "1 Will the Francis Scott Key Bridge in Baltimore... quickstart \n", "2 Will FC Saarbrucken reach the final of the Ger... quickstart \n", "3 Will the pro-life activists convicted for 'con... quickstart \n", "4 Will Apple announce the release of a new M4 ch... quickstart " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "markets.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "trades = pd.read_parquet(\"../data/fpmmTrades.parquet\")" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
collateralAmountcollateralAmountUSDcollateralTokencreationTimestamptrader_addressfeeAmountidoldOutcomeTokenMarginalPriceoutcomeIndexoutcomeTokenMarginalPrice...market_creatorfpmm.answerFinalizedTimestampfpmm.arbitrationOccurredfpmm.currentAnswerfpmm.idfpmm.isPendingArbitrationfpmm.openingTimestampfpmm.outcomesfpmm.titlefpmm.condition.id
04504264746507386880.45042696940341457163080730941680060xe91d153e0b41518a2ce8dd3d7944fa863463a97d17245534550x022b36c50b85b8ae7addfb8a35d76c59d581483490085294930147730x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x02...0.59278521060961027063412533557212910.6171295391012242250994586583534301...quickstart1725071760False0x00000000000000000000000000000000000000000000...0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5False1724976000[Yes, No]Will the first floating offshore wind research...0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1...
16101632145469414000.61016362322151501356540073370152980xe91d153e0b41518a2ce8dd3d7944fa863463a97d17248119400x034c4ad84f7ac6638bf19300d5bbe7d9b981e736122032642909388280x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x03...0.84299263652375506193482212939481210.8523396372892128845826889719620915...quickstart1725071760False0x00000000000000000000000000000000000000000000...0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5False1724976000[Yes, No]Will the first floating offshore wind research...0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1...
27890650923324606720.78906441205273240719087938227960860xe91d153e0b41518a2ce8dd3d7944fa863463a97d17248157550x09e9d42a029e8b0c2df3871709a762117a681d92157813018466492130x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x09...0.798377574371244289110459877033902810.8152123711444691659642000374025623...quickstart1725071760False0x00000000000000000000000000000000000000000000...0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5False1724976000[Yes, No]Will the first floating offshore wind research...0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1...
310000000000000000001.0000006053836603290484917949391260xe91d153e0b41518a2ce8dd3d7944fa863463a97d17245466200x09e9d42a029e8b0c2df3871709a762117a681d92200000000000000000x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x09...0.511074590773343880544707225262270810.5746805204222762335911904727318937...quickstart1725071760False0x00000000000000000000000000000000000000000000...0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5False1724976000[Yes, No]Will the first floating offshore wind research...0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1...
41000000000000000000.10000042712628624195473946465679060xe91d153e0b41518a2ce8dd3d7944fa863463a97d17247712600x0d049dcaece0ecb6fc81a460da7bcc2a4785d6e520000000000000000x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x0d...0.271396821866231938898868198738940800.2804586217805511523845593360379658...quickstart1725071760False0x00000000000000000000000000000000000000000000...0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5False1724976000[Yes, No]Will the first floating offshore wind research...0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1...
\n", "

5 rows × 24 columns

\n", "
" ], "text/plain": [ " collateralAmount collateralAmountUSD \\\n", "0 450426474650738688 0.4504269694034145716308073094168006 \n", "1 610163214546941400 0.6101636232215150135654007337015298 \n", "2 789065092332460672 0.7890644120527324071908793822796086 \n", "3 1000000000000000000 1.000000605383660329048491794939126 \n", "4 100000000000000000 0.1000004271262862419547394646567906 \n", "\n", " collateralToken creationTimestamp \\\n", "0 0xe91d153e0b41518a2ce8dd3d7944fa863463a97d 1724553455 \n", "1 0xe91d153e0b41518a2ce8dd3d7944fa863463a97d 1724811940 \n", "2 0xe91d153e0b41518a2ce8dd3d7944fa863463a97d 1724815755 \n", "3 0xe91d153e0b41518a2ce8dd3d7944fa863463a97d 1724546620 \n", "4 0xe91d153e0b41518a2ce8dd3d7944fa863463a97d 1724771260 \n", "\n", " trader_address feeAmount \\\n", "0 0x022b36c50b85b8ae7addfb8a35d76c59d5814834 9008529493014773 \n", "1 0x034c4ad84f7ac6638bf19300d5bbe7d9b981e736 12203264290938828 \n", "2 0x09e9d42a029e8b0c2df3871709a762117a681d92 15781301846649213 \n", "3 0x09e9d42a029e8b0c2df3871709a762117a681d92 20000000000000000 \n", "4 0x0d049dcaece0ecb6fc81a460da7bcc2a4785d6e5 2000000000000000 \n", "\n", " id \\\n", "0 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x02... \n", "1 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x03... \n", "2 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x09... \n", "3 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x09... \n", "4 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f50x0d... \n", "\n", " oldOutcomeTokenMarginalPrice outcomeIndex \\\n", "0 0.592785210609610270634125335572129 1 \n", "1 0.842992636523755061934822129394812 1 \n", "2 0.7983775743712442891104598770339028 1 \n", "3 0.5110745907733438805447072252622708 1 \n", "4 0.2713968218662319388988681987389408 0 \n", "\n", " outcomeTokenMarginalPrice ... market_creator \\\n", "0 0.6171295391012242250994586583534301 ... quickstart \n", "1 0.8523396372892128845826889719620915 ... quickstart \n", "2 0.8152123711444691659642000374025623 ... quickstart \n", "3 0.5746805204222762335911904727318937 ... quickstart \n", "4 0.2804586217805511523845593360379658 ... quickstart \n", "\n", " fpmm.answerFinalizedTimestamp fpmm.arbitrationOccurred \\\n", "0 1725071760 False \n", "1 1725071760 False \n", "2 1725071760 False \n", "3 1725071760 False \n", "4 1725071760 False \n", "\n", " fpmm.currentAnswer \\\n", "0 0x00000000000000000000000000000000000000000000... \n", "1 0x00000000000000000000000000000000000000000000... \n", "2 0x00000000000000000000000000000000000000000000... \n", "3 0x00000000000000000000000000000000000000000000... \n", "4 0x00000000000000000000000000000000000000000000... \n", "\n", " fpmm.id fpmm.isPendingArbitration \\\n", "0 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5 False \n", "1 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5 False \n", "2 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5 False \n", "3 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5 False \n", "4 0x0017cd58d6a7ee1451388c7d5b1051b4c0a041f5 False \n", "\n", " fpmm.openingTimestamp fpmm.outcomes \\\n", "0 1724976000 [Yes, No] \n", "1 1724976000 [Yes, No] \n", "2 1724976000 [Yes, No] \n", "3 1724976000 [Yes, No] \n", "4 1724976000 [Yes, No] \n", "\n", " fpmm.title \\\n", "0 Will the first floating offshore wind research... \n", "1 Will the first floating offshore wind research... \n", "2 Will the first floating offshore wind research... \n", "3 Will the first floating offshore wind research... \n", "4 Will the first floating offshore wind research... \n", "\n", " fpmm.condition.id \n", "0 0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1... \n", "1 0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1... \n", "2 0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1... \n", "3 0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1... \n", "4 0x0e940f12f30e928e4879c52d065d9da739a3d3f020d1... \n", "\n", "[5 rows x 24 columns]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trades.head()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 26835 entries, 0 to 26834\n", "Data columns (total 24 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 collateralAmount 26835 non-null object\n", " 1 collateralAmountUSD 26835 non-null object\n", " 2 collateralToken 26835 non-null object\n", " 3 creationTimestamp 26835 non-null object\n", " 4 trader_address 26835 non-null object\n", " 5 feeAmount 26835 non-null object\n", " 6 id 26835 non-null object\n", " 7 oldOutcomeTokenMarginalPrice 26835 non-null object\n", " 8 outcomeIndex 26835 non-null object\n", " 9 outcomeTokenMarginalPrice 26835 non-null object\n", " 10 outcomeTokensTraded 26835 non-null object\n", " 11 title 26835 non-null object\n", " 12 transactionHash 26835 non-null object\n", " 13 type 26835 non-null object\n", " 14 market_creator 26835 non-null object\n", " 15 fpmm.answerFinalizedTimestamp 24829 non-null object\n", " 16 fpmm.arbitrationOccurred 26835 non-null bool \n", " 17 fpmm.currentAnswer 24829 non-null object\n", " 18 fpmm.id 26835 non-null object\n", " 19 fpmm.isPendingArbitration 26835 non-null bool \n", " 20 fpmm.openingTimestamp 26835 non-null object\n", " 21 fpmm.outcomes 26835 non-null object\n", " 22 fpmm.title 26835 non-null object\n", " 23 fpmm.condition.id 26835 non-null object\n", "dtypes: bool(2), object(22)\n", "memory usage: 4.6+ MB\n" ] } ], "source": [ "trades.info()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['collateralAmount', 'collateralAmountUSD', 'collateralToken',\n", " 'creationTimestamp', 'trader_address', 'feeAmount', 'id',\n", " 'oldOutcomeTokenMarginalPrice', 'outcomeIndex',\n", " 'outcomeTokenMarginalPrice', 'outcomeTokensTraded', 'title',\n", " 'transactionHash', 'type', 'market_creator',\n", " 'fpmm.answerFinalizedTimestamp', 'fpmm.arbitrationOccurred',\n", " 'fpmm.currentAnswer', 'fpmm.id', 'fpmm.isPendingArbitration',\n", " 'fpmm.openingTimestamp', 'fpmm.outcomes', 'fpmm.title',\n", " 'fpmm.condition.id'],\n", " dtype='object')" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trades.columns" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "markets = list(trades[\"fpmm.id\"].unique())" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "803" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(markets)" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/gp/02mb1d514ng739czlxw1lhh00000gn/T/ipykernel_3094/2495807215.py:12: SettingWithCopyWarning: \n", "A value is trying to be set on a copy of a slice from a DataFrame\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", " trade_markets.rename(\n" ] } ], "source": [ "from datetime import datetime\n", "INVALID_ANSWER_HEX = (\n", " \"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"\n", ")\n", "columns_of_interest = [\n", " \"fpmm.currentAnswer\",\n", " \"fpmm.id\",\n", " \"fpmm.openingTimestamp\",\n", " \"market_creator\",\n", " ]\n", "trade_markets = trades[columns_of_interest]\n", "trade_markets.rename(\n", " columns={\n", " \"fpmm.currentAnswer\": \"currentAnswer\",\n", " \"fpmm.openingTimestamp\": \"openingTimestamp\",\n", " \"fpmm.id\": \"id\",\n", " },\n", " inplace=True,\n", ")\n", "trade_markets = trade_markets.drop_duplicates(subset=['id'], keep='last')\n", "# remove invalid answers\n", "\n", "trade_markets = trade_markets.loc[trade_markets[\"currentAnswer\"]!= INVALID_ANSWER_HEX]\n", "trade_markets[\"currentAnswer\"] = trade_markets[\"currentAnswer\"].apply(\n", " lambda x: convert_hex_to_int(x)\n", ")\n", "trade_markets[\"opening_datetime\"] = trade_markets[\"openingTimestamp\"].apply(\n", " lambda x: datetime.fromtimestamp(int(x))\n", ")\n", "trade_markets = trade_markets.sort_values(by=\"opening_datetime\", ascending=True)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "648" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(trade_markets.id.unique())" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "648" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(trade_markets)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "trade_markets.tail()" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "719" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(trade_markets)" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "trade_markets.dropna(inplace=True)" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "648" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(trade_markets)" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
currentAnsweridopeningTimestampmarket_creatoropening_datetime
207921.00xcc9c26a86dd55aa04dcb0066c9b8fca2983f407d1727136000quickstart2024-09-24 02:00:00
211301.00xd1bd18d7601d106639f922f1b5d2eda025c26be71727136000quickstart2024-09-24 02:00:00
74940.00x4eba0ec2464ec7c746e8872078165c8ad52d346f1727136000quickstart2024-09-24 02:00:00
99111.00x61065f131e2ec851c40765bb0b078a318a36f53e1727136000quickstart2024-09-24 02:00:00
261820.00x7e191324f0efb8aa20b8c702d95e812e55b4179c1727136000pearl2024-09-24 02:00:00
\n", "
" ], "text/plain": [ " currentAnswer id \\\n", "20792 1.0 0xcc9c26a86dd55aa04dcb0066c9b8fca2983f407d \n", "21130 1.0 0xd1bd18d7601d106639f922f1b5d2eda025c26be7 \n", "7494 0.0 0x4eba0ec2464ec7c746e8872078165c8ad52d346f \n", "9911 1.0 0x61065f131e2ec851c40765bb0b078a318a36f53e \n", "26182 0.0 0x7e191324f0efb8aa20b8c702d95e812e55b4179c \n", "\n", " openingTimestamp market_creator opening_datetime \n", "20792 1727136000 quickstart 2024-09-24 02:00:00 \n", "21130 1727136000 quickstart 2024-09-24 02:00:00 \n", "7494 1727136000 quickstart 2024-09-24 02:00:00 \n", "9911 1727136000 quickstart 2024-09-24 02:00:00 \n", "26182 1727136000 pearl 2024-09-24 02:00:00 " ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trade_markets.tail()" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "import math\n", "def market_KL_divergence(market_row: pd.DataFrame) -> float:\n", " \"\"\"Function to compute the divergence based on the formula\n", " Formula in https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence\"\"\"\n", " current_answer = market_row.currentAnswer # \"yes\", \"no\"\n", " target_prob = 1 # = 100%\n", " if current_answer == \"yes\":\n", " candidate_prob = market_row.first_outcome_prob\n", " else: # \"no\"\n", " candidate_prob = market_row.second_outcome_prob\n", "\n", " # we have only one sample, the final probability based on tokens\n", " kl_divergence = candidate_prob * round(math.log(candidate_prob / target_prob), 4)\n", " return kl_divergence" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "719" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(list(trade_markets.id.unique()))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "trade_markets.currentAnswer.value_counts()" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "INVALID_ANSWER_HEX = (\n", " \"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"\n", ")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "def convert_hex_to_int(x):\n", " \"\"\"Convert hex to int\"\"\"\n", " if isinstance(x, float):\n", " return np.nan\n", " if isinstance(x, str):\n", " if x == INVALID_ANSWER_HEX:\n", " return -1\n", " answer = int(x, 16)\n", " return answer\n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "market_ids = list(markets.id.unique())\n", "for i in range(len(trade_markets)):\n", " market = trade_markets.iloc[i]\n", " if market.id in market_ids:\n", " current_answer = convert_hex_to_int(market.currentAnswer)\n", " market_answer = markets.loc[markets[\"id\"]==market.id].currentAnswer.values[0]\n", " print(f\"current answer = {current_answer} and market answer {market_answer}\")\n", " trade_markets.at[i, \"currentAnswer\"] = market_answer" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "markets[\"currentAnswer\"] = markets[\"currentAnswer\"].apply(lambda x: convert_hex_to_int(x))" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "currentAnswer\n", " 1.0 407\n", " 0.0 241\n", "-1.0 84\n", "Name: count, dtype: int64" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "markets.currentAnswer.value_counts()" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.0769610411361284" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import math\n", "\n", "candidate_prob = 9/25\n", "target_prob = 1/3\n", "math.log(candidate_prob/target_prob)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "KL divergence: 6.296890976997244\n" ] } ], "source": [ "import numpy as np\n", "\n", "def kl_divergence(p, q):\n", " \"\"\"\n", " Compute KL divergence for a single sample with two probabilities.\n", " \n", " :param p: First probability (true distribution)\n", " :param q: Second probability (approximating distribution)\n", " :return: KL divergence value\n", " \"\"\"\n", " # Ensure probabilities sum to 1\n", " p = np.array([p, 1-p])\n", " q = np.array([q, 1-q])\n", " \n", " # Avoid division by zero\n", " epsilon = 1e-10\n", " q = np.clip(q, epsilon, 1-epsilon)\n", " \n", " # Compute KL divergence\n", " kl_div = np.sum(p * np.log(p / q))\n", " \n", " return kl_div\n", "\n", "# Example usage\n", "p = 0.7 # probability from true distribution\n", "q = 1.0 # probability from approximating distribution\n", "\n", "result = kl_divergence(p, q)\n", "print(f\"KL divergence: {result}\")" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "KL divergence: inf\n" ] } ], "source": [ "from scipy.special import kl_div\n", "\n", "# For multiple probabilities\n", "p = np.array([0.3, 0.7])\n", "q = np.array([0.0, 1.0])\n", "\n", "kl = np.sum(kl_div(p, q))\n", "print(f\"KL divergence: {kl}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This library is not useful if we have extreme values" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
currentAnsweridopeningTimestampmarket_creatoropening_datetimefirst_outcome_probsecond_outcome_probkl_divergence
0yes0x67490193504b49a247d6a3ba7d441e9894d9615f1722470400quickstart2024-08-01 02:00:000.81450.18553.791664
1no0x17f2c97bf52a79671878201bf2995a3b6daba0411722470400quickstart2024-08-01 02:00:000.19750.80254.050688
2no0xbca6aa704a02a5c5a766ff829dacc81aee5547cf1722470400quickstart2024-08-01 02:00:000.69690.303115.433247
3no0x221c71bab604691b0b8805c1c433fc8e22123a671722470400pearl2024-08-01 02:00:000.47570.524310.261432
4no0xe4d078b9be12319c0063f58dc10f19604a5df1631722470400quickstart2024-08-01 02:00:000.34730.65277.351119
\n", "
" ], "text/plain": [ " currentAnswer id openingTimestamp \\\n", "0 yes 0x67490193504b49a247d6a3ba7d441e9894d9615f 1722470400 \n", "1 no 0x17f2c97bf52a79671878201bf2995a3b6daba041 1722470400 \n", "2 no 0xbca6aa704a02a5c5a766ff829dacc81aee5547cf 1722470400 \n", "3 no 0x221c71bab604691b0b8805c1c433fc8e22123a67 1722470400 \n", "4 no 0xe4d078b9be12319c0063f58dc10f19604a5df163 1722470400 \n", "\n", " market_creator opening_datetime first_outcome_prob second_outcome_prob \\\n", "0 quickstart 2024-08-01 02:00:00 0.8145 0.1855 \n", "1 quickstart 2024-08-01 02:00:00 0.1975 0.8025 \n", "2 quickstart 2024-08-01 02:00:00 0.6969 0.3031 \n", "3 pearl 2024-08-01 02:00:00 0.4757 0.5243 \n", "4 quickstart 2024-08-01 02:00:00 0.3473 0.6527 \n", "\n", " kl_divergence \n", "0 3.791664 \n", "1 4.050688 \n", "2 15.433247 \n", "3 10.261432 \n", "4 7.351119 " ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "markets_div = pd.read_parquet(\"../data/closed_markets_div.parquet\")\n", "markets_div.head()" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
currentAnsweridopeningTimestampmarket_creatoropening_datetimefirst_outcome_probsecond_outcome_probkl_divergence
642yes0x4eba0ec2464ec7c746e8872078165c8ad52d346f1727136000quickstart2024-09-24 02:00:000.53920.46089.920241
643no0x3535b4cea3ea7b1862fbe1af5a458702cc1c0dad1727136000quickstart2024-09-24 02:00:000.28120.71885.880786
644yes0x7e191324f0efb8aa20b8c702d95e812e55b4179c1727136000pearl2024-09-24 02:00:000.50000.500010.819778
645no0xd1bd18d7601d106639f922f1b5d2eda025c26be71727136000quickstart2024-09-24 02:00:000.50000.500010.819778
646no0x61065f131e2ec851c40765bb0b078a318a36f53e1727136000quickstart2024-09-24 02:00:000.50000.500010.819778
\n", "
" ], "text/plain": [ " currentAnswer id \\\n", "642 yes 0x4eba0ec2464ec7c746e8872078165c8ad52d346f \n", "643 no 0x3535b4cea3ea7b1862fbe1af5a458702cc1c0dad \n", "644 yes 0x7e191324f0efb8aa20b8c702d95e812e55b4179c \n", "645 no 0xd1bd18d7601d106639f922f1b5d2eda025c26be7 \n", "646 no 0x61065f131e2ec851c40765bb0b078a318a36f53e \n", "\n", " openingTimestamp market_creator opening_datetime first_outcome_prob \\\n", "642 1727136000 quickstart 2024-09-24 02:00:00 0.5392 \n", "643 1727136000 quickstart 2024-09-24 02:00:00 0.2812 \n", "644 1727136000 pearl 2024-09-24 02:00:00 0.5000 \n", "645 1727136000 quickstart 2024-09-24 02:00:00 0.5000 \n", "646 1727136000 quickstart 2024-09-24 02:00:00 0.5000 \n", "\n", " second_outcome_prob kl_divergence \n", "642 0.4608 9.920241 \n", "643 0.7188 5.880786 \n", "644 0.5000 10.819778 \n", "645 0.5000 10.819778 \n", "646 0.5000 10.819778 " ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "markets_div.tail()" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "647" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(markets_div)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import math" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "def kl_divergence(p, q):\n", " \"\"\"\n", " Compute KL divergence for a single sample with two probabilities.\n", "\n", " :param p: First probability (true distribution)\n", " :param q: Second probability (approximating distribution)\n", " :return: KL divergence value\n", " \"\"\"\n", " # Ensure probabilities sum to 1\n", " p = np.array([p, 1 - p])\n", " q = np.array([q, 1 - q])\n", "\n", " # Avoid division by zero\n", " epsilon = 1e-10\n", " q = np.clip(q, epsilon, 1 - epsilon)\n", " print(q)\n", "\n", " # Compute KL divergence\n", " kl_div = np.sum(p * np.log(p / q))\n", "\n", " return kl_div" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([-22.82067008, 1.6847004 ])" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p= 0\n", "q = 0.8145\n", "p = np.array([p, 1 - p])\n", "q = np.array([q, 1 - q])\n", "epsilon = 1e-10\n", "p = np.clip(p, epsilon, 1 - epsilon)\n", "np.log(p/q)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([-2.28206701e-09, 1.68470040e+00])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p*np.log(p/q)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.6847003943841101" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.sum(p * np.log(p / q))" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.791663620863367" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = 0.8145\n", "q = 1.0\n", "kl_divergence(p,q)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.16397451204513597" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = 0.99\n", "q = 0.8145\n", "kl_divergence(p, q)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "KL divergence: 0.16397451204513597\n" ] } ], "source": [ "from scipy.special import kl_div\n", "\n", "# For multiple probabilities\n", "p = np.array([0.99, 0.01])\n", "q = np.array([0.8145, 0.1855])\n", "\n", "kl = np.sum(kl_div(p, q))\n", "print(f\"KL divergence: {kl}\")" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.2051808486854041" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p= 1\n", "q = 0.8145\n", "kl_divergence(p, q)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "KL divergence: 0.20518085094003724\n" ] } ], "source": [ "# For multiple probabilities\n", "p = np.array([1.0, 0.0])\n", "q = np.array([0.8145, 0.1855])\n", "\n", "kl = np.sum(kl_div(p, q))\n", "print(f\"KL divergence: {kl}\")" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1.e+00 1.e-10]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/gp/02mb1d514ng739czlxw1lhh00000gn/T/ipykernel_28964/3714966623.py:19: RuntimeWarning: divide by zero encountered in log\n", " kl_div = np.sum(p * np.log(p / q))\n", "/var/folders/gp/02mb1d514ng739czlxw1lhh00000gn/T/ipykernel_28964/3714966623.py:19: RuntimeWarning: invalid value encountered in multiply\n", " kl_div = np.sum(p * np.log(p / q))\n" ] }, { "data": { "text/plain": [ "nan" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = 0\n", "q = 1\n", "kl_divergence(p, q)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "KL divergence: 0.010050335853501449\n" ] } ], "source": [ "p = np.array([0.0, 1.0])\n", "q = np.array([0.01, 0.99])\n", "\n", "kl = np.sum(kl_div(p, q))\n", "print(f\"KL divergence: {kl}\")" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.17425697504355725" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p = 0.01\n", "q = 0\n", "kl_divergence(p, q)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "KL divergence: inf\n" ] } ], "source": [ "p = np.array([0.01, 0.99])\n", "q = np.array([0.0, 1.0])\n", "\n", "kl = np.sum(kl_div(p, q))\n", "print(f\"KL divergence: {kl}\")" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.01, 0.99])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "epsilon = 1e-10\n", "q = 0\n", "q = np.clip(p, epsilon, 1 - epsilon)\n", "q" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# New function" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "def kl_divergence(P, Q):\n", " \"\"\"\n", " Compute KL divergence for a single sample with two prob distributions.\n", "\n", " :param P: True distribution)\n", " :param Q: Approximating distribution)\n", " :return: KL divergence value\n", " \"\"\"\n", " # Review edge cases\n", " if P[0] == Q[0]:\n", " return 0.0\n", " # If P is complete opposite of Q, divergence is some max value.\n", " # Here set to 20--allows for Q [\\mu, 1-\\mu] or Q[1-\\mu, \\mu] where \\mu = 10^-8\n", " if P[0] == Q[1]:\n", " return 20\n", "\n", " nonzero = P > 0.0\n", " # Compute KL divergence\n", " kl_div = np.sum(P[nonzero] * np.log(P[nonzero] / Q[nonzero]))\n", "\n", " return kl_div" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.0\n" ] } ], "source": [ "P = np.array([0.0, 1.0])\n", "Q = np.array([0.0, 1.0])\n", "print(kl_divergence(P,Q))" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "20\n" ] } ], "source": [ "P = np.array([0.0, 1.0])\n", "Q = np.array([1.0, 0.0])\n", "print(kl_divergence(P,Q))" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "20\n" ] } ], "source": [ "P = np.array([1.0, 0.0])\n", "Q = np.array([0.0, 1.0])\n", "print(kl_divergence(P,Q))" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.010050335853501506\n" ] } ], "source": [ "P = np.array([0.0, 1.0])\n", "Q = np.array([0.01, 0.99])\n", "print(kl_divergence(P,Q))" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.010050335853501506" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 * np.log(1 / 0.99)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.5108256237659907\n" ] } ], "source": [ "P = np.array([1.0, 0.0])\n", "Q = np.array([0.60, 0.05])\n", "print(kl_divergence(P,Q))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "13.815510557964274\n" ] } ], "source": [ "Q = np.array([1e-6, 0.999999])#or [0.99, 0.01]\n", "P = np.array([1.0, 0.0])\n", "print(kl_divergence(P,Q))" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4.605170185988092" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.log(1/0.01)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAJOCAYAAACqS2TfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAAB7lUlEQVR4nO3deVwV9f7H8fcBBFwQNAQEFVRcckFNk2vuSaKVpdlNvd1c8uqvrno1stLKXSPNyrqaS2WabbZpt80yEs0yLU3NTFMyyQXcAgEVlfP9/UGcPAIKyHAAX8/H4zxkvjPznc8AR32f78x3bMYYIwAAAAAAUOzcXF0AAAAAAADlFaEbAAAAAACLELoBAAAAALAIoRsAAAAAAIsQugEAAAAAsAihGwAAAAAAixC6AQAAAACwCKEbAAAAAACLELoBAAAAALAIoRsALPDbb7/JZrNpyZIlri4FLjJ58mTZbDZXl5GvJUuWyGaz6bfffivwtt9//731hV1g1apVatmypby9vWWz2ZSSkiJJWrZsmRo3bqwKFSrIz88v3/3DwsJ06623lkyxAADkg9ANoEzLLwykpqaqbdu28vb21qpVqyT9FYKOHTtW6OPYbDbHy8PDQ9WrV1fr1q01evRo7dy5s1jOBQUXFhbm9DPx9vZWgwYN9NBDD+nEiROuLu+KvfDCCy75wMbq43799dfq06ePAgMD5eXlpbCwMP3f//2fEhMTc217/Phx3XXXXapYsaLmzZunZcuWqXLlytq1a5cGDx6s+vXr68UXX9SiRYssq7cgcj5gy3m5u7urTp066tOnj7Zu3erS2orDqVOnNHnyZMXHx7u6FAAoszxcXQAAFLeTJ0+qe/fu2r59u1asWKEePXoUS7833XSTBg4cKGOMUlNTtW3bNi1dulQvvPCCZs6cqZiYGMe2oaGhOn36tCpUqFAsx0ZuLVu21IMPPihJOnPmjDZv3qw5c+Zo7dq12rRpk4uruzIvvPCC/P39NXjwYMuOcc8996h///7y8vIqkeP+97//1ejRo1WvXj2NGjVKNWvW1M8//6yXXnpJy5cv1yeffKIbbrjBsf13332ntLQ0TZs2TVFRUY72+Ph42e12PffccwoPDy/2OotqwIABuvnmm5WVlaWff/5Z8+fP16effqpvv/1WLVu2dHV5RXbq1ClNmTJFktSlSxfXFgMAZRShG0C5kpaWpujoaG3dulXvv/++evbsWWx9N2zYUP/85z+d2p588kn16tVLDz74oBo3bqybb75ZkhyjryUtIyNDlStXLvHjukJISIjTz+Nf//qXqlSpotmzZ2vPnj1q0KCBC6sr/dzd3eXu7l4ix/r66681ZswYdejQQatWrVKlSpUc6+6//361b99ed955p3766SdVq1ZNknTkyBFJynX5eH7trnbdddc5/T62b99et912m+bPn6+FCxdeUd9X0/saAMojLi8HUG6kp6erR48e2rJli9577z3dcsstlh/zmmuu0VtvvSUPDw/NmDHD0X7xPd2zZ8+WzWbT/v37c/Uxfvx4eXp66o8//nC0bdy4UT169JCvr68qVaqkzp076+uvv3baL+dy+Z07d+of//iHqlWrpg4dOkiS7Ha7Jk+erODgYFWqVEldu3bVzp07FRYWlmsUMyUlRWPGjFHt2rXl5eWl8PBwzZw5U3a7Pdf5zJ49W4sWLVL9+vXl5eWl66+/Xt99912uc9q1a5fuuusu1ahRQxUrVlSjRo302GOPOW1z8OBB3XvvvY5LjZs2barFixcX7Bufj6CgIEmSh4fzZ8pffvmlOnbsqMqVK8vPz0+33367fv75Z8f6V155RTabLdfxn3jiCdlsNn3yySe5vg/PPvusQkNDVbFiRXXu3Fk7duy4bH3nz5/XtGnTHN+/sLAwPfroo8rMzHRsExYWpp9++klr1651XLJ8qRHG6667TnfccYdTW/PmzWWz2bR9+3ZH2/Lly2Wz2RznffE93QU5bmZmpmJiYlSjRg1VrlxZffr00dGjRy973tOmTZPNZtPSpUudArck1a9fX7NmzdLhw4cd4bRLly4aNGiQJOn666+XzWbT4MGDFRYWpkmTJkmSatSoIZvNpsmTJ1/2+J9//rnj3vAmTZro/fffd6z79ddfZbPZ9Oyzz+ba75tvvpHNZtObb7552WNc7MYbb5Qk7du3z9F2pe9rSXrttdfUtm1bVapUSdWqVVOnTp30+eefO/Xx6aefOn7ffXx8dMstt+inn35y2mbw4MGqUqWKDh48qN69e6tKlSqqUaOGxo4dq6ysLEnZv+81atSQJE2ZMsXxe5HzPd++fbsGDx6sevXqydvbW0FBQbr33nt1/PjxXN+P+Ph4tWnTRt7e3qpfv74WLlyY77wHr732mlq3bq2KFSuqevXq6t+/v37//feCfusBoPQxAFCGvfLKK0aSiY+PNx06dDAVKlQwH3zwQZ7bTpo0yUgyR48eLfRxJJkRI0bku75bt27Gzc3NpKamGmOM2bdvn5FkXnnlFWOMMfv37zc2m83MmjUr17716tUzt9xyi2M5Li7OeHp6mnbt2pmnn37aPPvssyYiIsJ4enqajRs35jqfJk2amNtvv9288MILZt68ecYYYx5++GEjyfTq1cvMnTvXDBs2zNSqVcv4+/ubQYMGOfrIyMgwERER5pprrjGPPvqoWbBggRk4cKCx2Wxm9OjRju1yzqdVq1YmPDzczJw508yaNcv4+/ubWrVqmbNnzzq23bZtm6lataq55pprzPjx483ChQvNww8/bJo3b+7YJikpydSqVcvUrl3bTJ061cyfP9/cdtttRpJ59tlnL/vzCA0NNd27dzdHjx41R48eNb///rv53//+Z4KDg02nTp2ctl29erXx8PAwDRs2NLNmzTJTpkwx/v7+plq1ambfvn2O7W699Vbj6+trEhMTjTHGbN++3Xh6epqhQ4fm+j40b97chIWFmZkzZ5opU6aY6tWrmxo1apikpKRcP58LDRo0yEgyd955p5k3b54ZOHCgkWR69+7t2GbFihWmVq1apnHjxmbZsmVm2bJl5vPPP8/3e/Gf//zH1KhRw7F8/PhxY7PZjJubm5k7d66jfcSIEU7b5bx3cr4HlzpuzratWrUyN954o/nvf/9rHnzwQePu7m7uuuuufGszJvt3zMPDw3Tp0iXfbc6cOWO8vLxM+/btjTHGfP7552b48OFGkpk6dapZtmyZ+eabb8yKFStMnz59jCQzf/58s2zZMrNt27Z8+w0NDTUNGzY0fn5+Zty4ceaZZ54xzZs3N25ubk7f0/bt25vWrVvn2v/f//638fHxMRkZGfkeI+d34qmnnnJq37Ztm5Fk+vfvb4wpnvf15MmTjSRzww03mKeeeso899xz5h//+Id55JFHHPu/+uqrxmazmR49epj//ve/ZubMmSYsLMz4+fk5/b4PGjTIeHt7m6ZNm5p7773XzJ8/3/Tt29dIMi+88IIxxpj09HQzf/58I8n06dPH8XuR8z2fPXu26dixo5k6dapZtGiRGT16tKlYsaJp27atsdvtjmNt2bLFeHl5mbCwMPPkk0+aGTNmmODgYNOiRYtc75Hp06cbm81m+vXrZ1544QXH+zUsLMz88ccf+f4cAKA0I3QDKNNywkBoaKipUKGCWblyZb7bWhm6R48ebSQ5/jN6ceg2xph27drl+o/9pk2bjCTz6quvGmOMsdvtpkGDBiY6OtrpP62nTp0ydevWNTfddFOu8xkwYIBTn0lJScbDw8MpyBnz13/YLwzd06ZNM5UrVza//PKL07bjxo0z7u7ujgCacz7XXHONOXHihGO7Dz74wEgyH374oaOtU6dOxsfHx+zfv9+pzwvPZ+jQoaZmzZrm2LFjTtv079/f+Pr6mlOnTplLCQ0NNZJyvdq3b5+rz5YtW5qAgABz/PhxR9u2bduMm5ubGThwoKPt8OHDpnr16uamm24ymZmZplWrVqZOnTqOD1Iu/D5UrFjRHDhwwNG+ceNGI8k88MADjraLQ/fWrVuNJPOvf/3Lqb6xY8caSebLL790tDVt2tR07tz5kt+DHO+8846RZHbu3GmMMeZ///uf8fLyMrfddpvp16+fY7uIiAjTp08fx/LFoftSx83ZNioqyunn+MADDxh3d3eTkpKSb305533hhzh5iYiIMNWrV891zO+++85pu8K8j3N+T9577z1HW2pqqqlZs6Zp1aqVo23hwoVGkvn5558dbWfPns31IVVecn4npkyZYo4ePWqSkpJMfHy8adWqlePYxfG+3rNnj3FzczN9+vQxWVlZTuty+kxLSzN+fn5m2LBhTuuTkpKMr6+vU3vOB0BTp0512rZVq1ZOf08dPXrUSDKTJk3Kde55vU/ffPNNI8msW7fO0darVy9TqVIlc/DgQafz8fDwcHqP/Pbbb8bd3d3MmDHDqc8ff/zReHh45GoHgLKCy8sBlAvJycny9vZW7dq1XXL8KlWqSMq+pzw//fr10+bNm5WQkOBoW758uby8vHT77bdLkrZu3ao9e/boH//4h44fP65jx47p2LFjysjIULdu3bRu3Tqny74l6b777nNajouL0/nz5/Xvf//bqX3UqFG5anrnnXfUsWNHVatWzXGsY8eOKSoqSllZWVq3bl2uc8i551aSOnbsKCn7El1JOnr0qNatW6d7771XderUcdo35zJSY4zee+899erVS8YYp+NGR0crNTVVW7Zsyff7mCMyMlKrV6/W6tWr9dFHH2nGjBn66aefdNttt+n06dOSpMOHD2vr1q0aPHiwqlev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k09q3Lhx6t+/vx5//HFHQLvQypUrdfDgQcfypk2btHHjxks+Fz7nWHPmzHFqf+aZZyTJ6RF3lStXdqr3cnIuG585c6YiIiLk6+vraI+Li9P3339foEvLC3vcgnr88cdljNHgwYMd99vn2Ldvnx5++GHVrFlT//d//1fovuvVq6eoqCjHq3379k7rDx06pBUrVjiWT548qVdffVUtW7Z0Gg328PDQgAED9Pbbb2vJkiVq3rx5ka+4uFhxvK979+4tNzc3TZ06Ndcocs57Kzo6WlWrVtUTTzyhc+fOFek4F8t5xNvFvxc5H+BcfJXDxb/f7u7uioqK0sqVK3Xo0CFH+969e/Xpp586bXvHHXfI3d1dU6ZMydWvMSbPR5EBQFnASDeAcqdPnz568cUXde+99+q2227TqlWr5O3t7Vj/zDPP5HpWsJubmx599NFL9vvLL7/otddekzFGJ0+e1LZt2/TOO+8oPT1dzzzzjHr06HHZ2gICAtS1a1c988wzSktLU79+/XLV8dJLL6lnz55q2rSphgwZopCQEB08eFBr1qxR1apVHcEyP4GBgRo9erSefvpp3XbbberRo4e2bdumTz/9VP7+/k4jVw899JD+97//6dZbb9XgwYPVunVrZWRk6Mcff9S7776r3377Tf7+/pc9rws9//zz6tChg6677joNHz5cdevW1W+//aaPP/5YW7dulSQ9+eSTWrNmjSIjIzVs2DA1adJEJ06c0JYtW/TFF1/oxIkTlz3OwYMH9dprr0mSzp49q23btmnhwoXy9/d3urT8qaeeUs+ePdWuXTsNHTpUp0+f1n//+1/5+vo6njd85MgR3X///eratatGjhwpKftDijVr1mjw4MFav36902XG4eHh6tChg+6//35lZmZqzpw5uuaaa/K9TF/K/iBg0KBBWrRokVJSUtS5c2dt2rRJS5cuVe/evdW1a1fHtq1bt9b8+fM1ffp0hYeHKyAg4JL3FoeHhysoKEi7d+92OvdOnTrpkUcekaQChe7CHregOnXqpNmzZysmJkYREREaPHiwatasqV27dunFF1+U3W7XJ598kmvOgeLQsGFDDR06VN99950CAwO1ePFiJScn65VXXsm17cCBA/X8889rzZo1Trd9XKnieF+Hh4frscce07Rp09SxY0fdcccd8vLy0nfffafg4GDFxsaqatWqmj9/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ooWio6N15MiRS+7322+/aezYserYsWOe63v06KHDhw87Xm+++aYV5QMAAAAAimDt2rUaMWKEvv32W61evVrnzp1T9+7dlZGRIUk6dOiQDh06pNmzZ2vHjh1asmSJVq1apaFDh16279KUBz1cduQ/PfPMMxo2bJiGDBkiSVqwYIE+/vhjLV68WOPGjctzn6ysLN19992aMmWKvvrqK6WkpOTaxsvLS0FBQVaWDgAAAAAoolWrVjktL1myRAEBAdq8ebM6deqkZs2a6b333nOsr1+/vmbMmKF//vOfOn/+vDw88o+zpSkPunSk++zZs9q8ebOioqIcbW5uboqKitKGDRvy3W/q1KkKCAi45Ccc8fHxCggIUKNGjXT//ffr+PHjxVo7AAAAAKD45Fw2Xr169UtuU7Vq1UsGbql05UGXjnQfO3ZMWVlZCgwMdGoPDAzUrl278txn/fr1evnll7V169Z8++3Ro4fuuOMO1a1bVwkJCXr00UfVs2dPbdiwQe7u7rm2z8zMVGZmpmM5LS2taCcEAABwFXM7neLqEoByryy8z9LS0nTy5EnHspeXl7y8vC65j91u15gxY9S+fXs1a9Ysz22OHTumadOmafjw4Zfsq7B50Gouv7y8MNLS0nTPPffoxRdflL+/f77b9e/f3/F18+bNFRERofr16ys+Pl7dunXLtX1sbKymTJliSc0AAABXi4r71rm6BAClQJMmTZyWJ02apMmTJ19ynxEjRmjHjh1av359nutPnjypW265RU2aNLlsX4XNg1Zzaej29/eXu7u7kpOTndqTk5PzvP4+ISFBv/32m3r16uVos9vtkiQPDw/t3r1b9evXz7VfvXr15O/vr7179+b5TR4/frxiYmIcywcPHsz1iwIAAIBLO123k+wV/VxdBlCuuZ1OKfUfcO3cuVMhISGO5cuNco8cOVIfffSR1q1bp1q1auVan5aWph49esjHx0crVqxQhQoVClXP5fKg1Vwauj09PdW6dWvFxcU5Hvtlt9sVFxenkSNH5tq+cePG+vHHH53aHn/8caWlpem5555T7dq18zzOgQMHdPz4cdWsWTPP9Rdf7nDhpRAAAAAoGHtFP9kr5381IoCrg4+Pj6pWrXrZ7YwxGjVqlFasWKH4+HjVrVs31zYnT55UdHS0vLy89L///U/e3t6FrudyedBqLr+8PCYmRoMG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lJvbXwPyGRkZ+ve//60DBw6oYsWKaty4sV577TX169dPkuTu7q7t27dr6dKlSklJUXBwsLp3765p06YVy70EAAAAAIAr9/3336tr166O5Zx5tgYNGqQlS5ZIkt566y0ZY/INzQkJCTp27Jhj+cCBAxowYICOHz+uGjVqqEOHDvr2229Vo0YN607kMmzGGOOyo5dSBw4cUO3atfX777+rVq1ari4HAACHX375RcOHD2eGbwBAqVMeclS1atVks9lytdtsNnl7eys8PFyDBw92XJldEC4f6QYAAAAAoDSYOHGiZsyYoZ49e6pt27aSpE2bNmnVqlUaMWKE9u3bp/vvv1/nz5/XsGHDCtQnoRsAAAAAAEnr16/X9OnTdd999zm1L1y4UJ9//rnee+89RURE6Pnnny9w6C7S7OUpKSl66aWXNH78eJ04cUKStGXLFh08eLAo3QEAAAAA4HKfffaZoqKicrV369ZNn332mSTp5ptv1q+//lrgPgsdurdv366GDRtq5syZmj17tlJSUiRJ77//vsaPH1/Y7gAAAAAAKBWqV6+uDz/8MFf7hx9+qOrVq0vKntzbx8enwH0W+vLymJgYDR48WLNmzXI60M0336x//OMfhe0OAAAAAIBSYcKECbr//vu1Zs0axz3d3333nT755BMtWLBAkrR69Wp17ty5wH0WOnR/9913WrhwYa72kJAQJSUlFbY7AAAAAABKhWHDhqlJkyaaO3eu3n//fUlSo0aNtHbtWt1www2SpAcffLBQfRY6dHt5eenkyZO52n/55ReXPvsMAAAAAIAr1b59e7Vv377Y+iv0Pd233Xabpk6dqnPnzknKfl5ZYmKiHnnkEfXt27fYCgMAAAAAwFXOnDmjkydPOr2KotCh++mnn1Z6eroCAgJ0+vRpde7cWeHh4fLx8dGMGTOKVAQAAAAAAK526tQpjRw5UgEBAapcubKqVavm9CqKQl9e7uvrq9WrV+vrr7/Wtm3blJ6eruuuuy7PadUBAAAAACgrHnroIa1Zs0bz58/XPffco3nz5ungwYNauHChnnzyySL1WejQnaO4r3MHAAAAAMCVPvzwQ7366qvq0qWLhgwZoo4dOyo8PFyhoaF6/fXXdffddxe6z0JfXv6f//xHzz//fK72uXPnasyYMYUuAAAAAACA0uDEiROqV6+eJKlq1ao6ceKEJKlD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6GjHPt26dVOfPn0cofpy+fJyVq5cqeXLl+tvf/ub01wqTZs2VUJCQpHOs9Che9iwYRo9erQWL14sm82mQ4cOacOGDRo7dqwmTJhQ6AL69euno0ePauLEiUpKSlLLli21atUqx83viYmJcrvg0/uMjAz9+9//1oEDB1SxYkU1btxYr732mvr16+fY5uGHH1ZGRoaGDx+ulJQUdejQQatWrZK3t3eh6wMAwOVs2Y8Fc/vzMWE5z+Y2NrfswE3oBgCUQd9//726du3qWM655XfQoEGaP3++tm/frqVLlyolJUXBwcHq3r27pk2b5nSVckJCgo4dO+ZYvly+vJyjR48qICAgV3tGRkbeE5oWgM2YP//lLiBjjJ544gnFxsbq1KlTkrIvzx47dqymTZtWpCJKmwMHDqh27dr6/fffVatWLVeXAwCW+OWXXzR8+HBlNLmNy8vLkgv/2SZslxluGcdUeef/tGjRIjVs2NDV5QCAJcpDjurUqZP+/ve/a9SoUfLx8dH27dtVt25djRo1Snv27NGqVasK3WehR7ptNpsee+wxPfTQQ9q7d6/S09PVpEkTValSpdAHBwAAhUTQBgDAMk888YR69uypnTt36vz583ruuee0c+dOffPNN1q7dm2R+iz0rCuvvfaaTp06JU9PTzVp0kRt27YlcAMAAAAAyrwOHTpo27ZtOn/+vJo3b67PP/9cAQEB2rBhg1q3bl2kPgs90v3AAw/ovvvu02233aZ//vOfio6OzvPB5AAAAAAAlBU5T706e/asnnjiCdWoUaNY+i30SPfhw4f11ltvyWaz6a677lLNmjU1YsQIffPNN8VSEAAAAAAAJWnr1q1q3LixevTooV69eik8PFyfffZZsfRd6NDt4eGhW2+9Va+//rqOHDmiZ599Vr/99pu6du2q+vXrF0tRAAAAAACUlEceeUR169bV+vXrtXnzZnXr1q3Az/a+nEJfXn6hSpUqKTo6Wn/88Yf279+vn3/+uViKAgAAAACgpGzevFmff/65rrvuOknS4sWLVb16dZ08eVJVq1a9or4LPdItSadOndLrr7+um2++WSEhIZozZ4769Omjn3766YqKAQAAAACgpJ04ccLpMWd+fn6qXLmyjh8/fsV9F3qku3///vroo49UqVIl3XXXXZowYYLatWt3xYUAAAAAAOAqO3fuVFJSkmPZGKOff/5ZaWlpjraIiIhC91vo0O3u7q63336bWcsBAAAAAOVGt27dZIxxarv11ltls9lkjJHNZlNWVlah+y106H799dcLfRAAAAAAAEqrffv2WdZ3gUL3888/r+HDh8vb21vPP//8Jbf9z3/+UyyFAQAAAABQEkJDQy3ru0Ch+9lnn9Xdd98tb29vPfvss/luZ7PZCN0AAAAAAPypQKH7wqF2K4fdAQAAAAAoT4r0yDAAAAAAAHB5hQrdGRkZmjhxopo1a6YqVarIx8dHERERmjp1qk6dOmVVjQAAAAAAlEkFnr387Nmz6ty5s3bs2KGePXuqV69ejueWzZgxQ59++qnWrVunChUqWFkvAAAAAACWmDRpku69995inVitwKF7/vz5OnDggLZt26ZGjRo5rdu1a5e6dOmiBQsWaNSoUcVWHAAAAAAAJeWDDz7QjBkz1LlzZw0dOlR9+/aVl5fXFfVZ4MvL33//fU2YMCFX4Jakxo0b67HHHtO77757RcUAAAAAAOAqW7du1XfffaemTZtq9OjRCgoK0v3336/vvvuuyH0WOHTv3LlTXbp0yXd9165dtXPnziIXAgAAAACAq7Vq1UrPP/+8Dh06pJdfflkHDhxQ+/btFRERoeeee06pqamF6q/AoTslJUXXXHNNvuuvueaaQh8cAAAAAIDSyBijc+fO6ezZszLGqFq1apo7d65q166t5cuXF7ifAoduu90ud3f3/Dtyc1NWVlaBDwwAAAAAQGmzefNmjRw5UjVr1tQDDzygVq1a6eeff9batWu1Z88ezZgxQ//5z38K3F+BJ1Izxqhbt27y8Mh7l/Pnzxf4oACA0sPtDFcpAVbjfQYAZUPz5s21a9cude/eXS+//LJ69eqVa/B5wIABGj16dIH7LHDonjRp0mW36du3b4EPDABwLV9fX1Xw9JJ+XevqUoCrQgVPL/n6+rq6DADAJdx111269957FRISku82/v7+stvtBe7TZowxxVFceXLgwAHVrl1bv//+u2rVquXqcgDAMsnJyczHUcbs379fM2bM0GOPPVaszxCF9Xx9fRUYGOjqMgDAMuUtR+VEZZvNdkX9FHikGwBQ/gQGBhICyqjQ0FA1bNjQ1WUAAFDuvPzyy3r22We1Z88eSVKDBg00ZswY/etf/ypSf4RuAAAAAAAkTZw4Uc8884xGjRqldu3aSZI2bNigBx54QImJiZo6dWqh+yR0AwAAAAAgaf78+XrxxRc1YMAAR9ttt92miIgIjRo1qkihu8CPDAMAAAAAoDw7d+6c2rRpk6u9devWRX5iV7GF7gMHDmj48OHF1R0AAAAAACXqnnvu0fz583O1L1q0SHfffXeR+iy2y8uPHz+ul19+WYsWLSquLgEAAAAAsFRMTIzja5vNppdeekmff/65/va3v0mSNm7cqMTERA0cOLBI/XNPNwAAAADgqvXDDz84Lbdu3VqSlJCQICn7udz+/v766aefitQ/oRsAAAAAcNVas2aNpf0zkRoAAAAAABd58803lZGRccX9FHik+4477rjk+pSUlCutBQAAAACAUuH//u//FBkZqXr16l1RPwUO3b6+vpddX9QbywEAAAAAKE2MMcXST4FD9yuvvHLZbdLT06+oGAAAAAAAStLJkydVtWpVy/ov8D3dzz777CXXp6WlKTo6+ooLAgAAAACgpFSrVk1HjhyRJN14442OW6c//fRTBQcHX3H/BQ7djz76qF599dU816Wnp6tHjx46fvz4FRcEAAAAAEBJqVKliiPLxsfH69y5c5KkDh06yNvb+4r7L/Dl5cuWLdM999wjPz8/3XbbbY72jIwM9ejRQ0ePHtXatWuvuCAAAAAAAEpKVFSUunbtqmuvvVaS1KdPH3l6eua57Zdfflno/gscuu+8806lpKRowIAB+vjjj9WlSxdH4E5OTtbatWtVs2bNQhcAAAAAAICrvPbaa1q6dKkSEhK0du1aNW3aVJUqVSq2/gscuiXpX//6l06cOKHbb79dH3zwgSZOnKhDhw5p7dq1xXKtOwAAAAAAJencuXO67777JEnff/+9Zs6cKT8/v2Lrv1ChW5IefvhhnThxQt26dVNYWJji4+NVq1atYisIAAAAAICSUq1aNR0+fFgBAQGy2WzF3n+BQ/cdd9zhtFyhQgX5+/tr9OjRTu3vv/9+8VQGAAAAAIDFciZSCwgI0Nq1ax0TqRWXAoduX19fp+UBAwYUayEAAAAAAJS0CydSM8a4biK1V155pdCdAwAAAABQmpWqidQAAAAAAChPKlasaOlEam7F1tMVmDdvnsLCwuTt7a3IyEht2rQp321ffPFFdezYUdWqVVO1atUUFRWVa/vBgwfLZrM5vXr06GH1aQAAAAAACmjdunXq1auXgoODZbPZtHLlSse6c+fO6ZFHHlHz5s1VuXJlBQcHa+DAgTp06NAl+5w8eXKuLNi4ceMC17RmzRr5+fnp2LFjOnbsWFFPzYnLQ/fy5csVExOjSZMmacuWLWrRooWio6N15MiRPLePj4/XgAEDtGbNGm3YsEG1a9dW9+7ddfDgQaftevToocOHDzteb775ZkmcDgAAAACgADIyMtSiRQvNmzcv17pTp05py5YtmjBhgrZs2aL3339fu3fv1m233XbZfps2beqUBdevX1+gelJSUjRixAj5+/srMDBQgYGB8vf318iRI5WSklLY03Nw+eXlzzzzjIYNG6YhQ4ZIkhYsWKCPP/5Yixcv1rhx43Jt//rrrzstv/TSS3rvvfcUFxengQMHOtq9vLwUFBRkbfEAAAAAgCLp2bOnevbsmec6X19frV692qlt7ty5atu2rRITE1WnTp18+/Xw8Ch0Fjxx4oTatWungwcP6u6779a1114rSdq5c6eWLFmiuLg4ffPNN6pWrVqh+pVcPNJ99uxZbd68WVFRUY42Nzc3RUVFacOGDQXq49SpUzp37pyqV6/u1B4fH6+AgAA1atRI999/v44fP16stQMAAAAASk5qaqpsNttl77fes2ePgoODVa9ePd19991KTEy8bN9Tp06Vp6enEhIStHDhQo0ZM0ZjxozRokWLtHfvXlWoUEFTp04tUt0uHek+duyYsrKyFBgY6NQeGBioXbt2FaiPRx55RMHBwU7BvUePHrrjjjtUt25dJSQk6NFHH1XPnj21YcMGubu75+ojMzNTmZmZjuW0tLQinhEAoDQ4c+ZMgf6BLYv279/v9Gd5VKdOHXl7e7u6DABAEaWlpenkyZOOZS8vL3l5eV1Rn2fOnNEjjzyiAQMGqGrVqvluFxkZqSVLlqhRo0Y6fPiwpkyZoo4dO2rHjh3y8fHJd7+VK1dq4cKFubKpJAUFBWnWrFm677779Oyzzxa6dpdfXn4lnnzySb311luKj493+se5f//+jq+bN2+uiIgI1a9fX/Hx8erWrVuufmJjYzVlypQSqRkAYL3ExEQNHz7c1WVYasaMGa4uwTKLFi1Sw4YNXV0GAKCImjRp4rQ8adIkTZ48ucj9nTt3TnfddZeMMZo/f/4lt73wcvWIiAhFRkYqNDRUb7/9toYOHZrvfocPH1bTpk3zXd+sWTMlJSUVvni5OHT7+/vL3d1dycnJTu3JycmXvQZ/9uzZevLJJ/XFF18oIiLiktvWq1dP/v7+2rt3b56he/z48YqJiXEsHzx4MNcvCgCg7KhTp44WLVrk6jJQRJe6Tw8AUPrt3LlTISEhjuUrGeXOCdz79+/Xl19+eclR7rz4+fmpYcOG2rt37yW38/f312+//aZatWrluX7fvn25bmkuKJeGbk9PT7Vu3VpxcXHq3bu3JMlutysuLk4jR47Md79Zs2ZpxowZ+uyzz9SmTZvLHufAgQM6fvy4atasmef6iy93uPBSCABA2ePt7c1IKQAALuLj41PocJyXnMC9Z88erVmzRtdcc02h+0hPT1dCQoLuueeeS24XHR2txx57TKtXr5anp6fTuszMTE2YMKHIj6F2+eXlMTExGjRokNq0aaO2bdtqzpw5ysjIcMxmPnDgQIWEhCg2NlaSNHPmTE2cOFFvvPGGwsLCHEP8VapUUZUqVZSenq4pU6aob9++CgoKUkJCgh5++GGFh4crOjraZecJAAAAAPhLenq60wj0vn37tHXrVlWvXl01a9bUnXfeqS1btuijjz5SVlaWI/tVr17dEYy7deumPn36OAZtx44dq169eik0NFSHDh3SpEmT5O7urgEDBlyylqlTp6pNmzZq0KCBRowYocaNG8sYo59//lkvvPCCMjMztWzZsiKdp8tDd79+/XT06FFNnDhRSUlJatmypVatWuW4gT0xMVFubn9Nsj5//nydPXtWd955p1M/OfcJuLu7a/v27Vq6dKlSUlIUHBys7t27a9q0aVd88z4AAAAAoHh8//336tq1q2M555bfQYMGafLkyfrf//4nSWrZsqXTfmvWrFGXLl0kSQkJCTp27Jhj3YEDBzRgwAAdP35cNWrUUIcOHfTtt9+qRo0al6ylVq1a2rBhg/79739r/PjxMsZIkmw2m2666SbNnTtXtWvXLtJ52kxOb3A4cOCAateurd9//z3fa/oBAAAAAH8pLznqjz/+0J49eyRJ4eHhRb6XO4fLR7oBAEAhXPhZuc3mujoAACinqlWrprZt2xZbf4RuAADKAmMkuz37lcPNLftF+AYAoNQidAMAUNoZI2VlZf9ps2W/ckK4MZK7O8EbAIBSyu3ymwAAAJfKCdcXjmrbbNnLOeEbAACUSoRuAABKs5xQnd9Its32VygHAAClDqEbAICy4FKhGwAA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AAAAsAihGwAAAAAAixC6AQAAAACwCKEbAAAAAACLELoBACjHunTpojFjxri6DAAArlqEbgAASsCCBQvk4+Oj8+fPO9rS09NVoUIFdenSxWnb+Ph42Ww2JSQklHCVAACguBG6AQAoAV27dlV6erq+//57R9tXX32loKAgbdy4UWfOnHG0r1mzRnXq1FH9+vVdUSoAAChGhG4AAEpAo0aNVLNmTcXHxzva4uPjdfvtt6tu3br69ttvndq7du0qu92u2NhY1a1bVxUrVlSLFi307rvvOvW7Y8cO9ezZU1WqVFFgYKDuueceHTt2LN86Pv74Y/n6+ur1118v9nMEAAC5EboBACghXbt21Zo1axzLa9asUZcuXdS5c2dH++nTp7Vx40Z17dpVsbGxevXVV7VgwQL99NNPeuCBB/TPf/5Ta9eulSSlpKToxhtvVKtWrfT9999r1apVSk5O1l133ZXn8d944w0NGDBAr7/+uu6++27rTxgAAMjD1QUAAHC16Nq1q8aMGaPz58/r9OnT+uGHH9S5c2edO3dOCxYskCRt2LBBmZmZ6tKli5o0aaIvvvhC7dq1kyTVq1dP69ev18KFC9W5c2fNnTtXrVq10hNPPOE4xuLFi1W7dm398ssvatiwoaN93rx5euyxx/Thhx+qc+fOJXviAABcxQjdAACUkC5duigjI0Pfffed/vjjDzVs2FA1atRQ586dNWTIEJ05c0bx8fGqV6+e0tPTderUKd10001OfZw9e1atWrWSJG3btk1r1qxRlSpVch0rISHBEbrfffddHTlyRF9//bWuv/56608UAAA4ELoBACgh4eHhqlWrltasWaM//vjDMeIcHBys2rVr65tvvtGaNWt04403Kj09XVL2PdghISFO/Xh5eUnKnv28V69emjlzZq5j1axZ0/F1q1attGXLFi1evFht2rSRzWaz6hQBAMBFCN0AAJSgrl27Kj4+Xn/88YceeughR3unTp306aefatOmTbr//vvVpEkTeXl5KTExMd/Lwa+77jq99957CgsLk4dH/v+k169fX08//bS6dOkid3d3zZ07t9jPCwAA5I2J1AAAKEFdu3bV+vXrtXXrVqcw3blzZy1cuFBnz55V165d5ePjo7Fjx+qBBx7Q0qVLlZCQoC1btui///2vli5dKkkaMWKETpw4oQEDBui7775TQkKCPvvsMw0ZMkRZWVlOx23YsKHWrFmj9957T2PGjCnJUwYA4KrGSDcAACWoa9euOn36tBo3bqzAwEBHe+fOnZWWluZ4tJgkTZs2TTVq1FBsbKx+/fVX+fn56brrrtOjjz4qKfuy9K+//lqPPPKIunfvrszMTIWGhqpHjx5yc8v9uXqjRo305ZdfOka8n3766ZI5aQAArmI2Y4xxdREAAAAAAJRHXF4OAAAAAIBFCN0AAAAAAFiE0A0AAAAAgEUI3QAAAAAAWITQDQAAAACARQjdAAAAAABYhNANAAAAAIBFCN0AAAAAAFiE0A0AAAAAgEUI3QAAAAAAWITQDQAAAACARQjdAAAAAABY5P8BuOFnE0nBsqwAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "# Assuming you have your dataframe as 'df'\n", "# If not, you can create a sample dataframe like this:\n", "df = pd.DataFrame({\n", " 'week': [1, 1, 1, 2, 2, 2, 3, 3, 3],\n", " 'kl_divergence': [0.1, 0.2, 0.15, 0.3, 0.25, 0.35, 0.4, 0.45, 0.5],\n", " 'off_by_perc': [5, 10, 7, 15, 12, 18, 20, 22, 25]\n", "})\n", "\n", "# Create the main figure and axis\n", "fig, ax1 = plt.subplots(figsize=(10, 6))\n", "\n", "# Create the boxplot using seaborn\n", "sns.boxplot(x='week', y='kl_divergence', data=df, ax=ax1)\n", "\n", "# Set labels and title for the main axis\n", "ax1.set_xlabel('Week')\n", "ax1.set_ylabel('KL Divergence')\n", "ax1.set_title('KL Divergence Boxplot with Off-by Percentage')\n", "\n", "# Create a secondary y-axis\n", "ax2 = ax1.twinx()\n", "\n", "# Plot the off_by_perc values on the secondary y-axis\n", "for i, week in enumerate(df['week'].unique()):\n", " off_by_perc = df[df['week'] == week]['off_by_perc']\n", " ax2.scatter([i] * len(off_by_perc), off_by_perc, color='red', alpha=0.01)\n", "\n", "# Set label for the secondary y-axis\n", "ax2.set_ylabel('Off-by Percentage')\n", "\n", "# Adjust the layout and display the plot\n", "plt.tight_layout()\n", "plt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "hf_dashboards", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 2 }