Interactive Plots
[1]:
"""
Update Parameters Here
"""
COLLECTION_NAME = "MutantCats"
COLLECTION_TITLE = "Mutant Cats"
"""
Annotations to the sales graph can be added with the ANNOTATIONS list
{
"label": "annotation text",
"date": "2022-02-05 17:00:00"
},
"""
ANNOTATIONS = [
{"label": "Reveal Date", "date": "2021-10-11 04:30:14"},
{"label": "Mutant Gorillaz auction start", "date": "2021-11-01 18:58:00"},
]
[2]:
"""
data:25/02/2022
"""
# LOAD AND SETUP DATA
import pandas as pd
import plotly
import plotly.express as px
import numpy as np
import datetime
plotly.offline.init_notebook_mode()
from honestnft_utils import config
# Define rarity rank brackets
RARITY_RANKS = [50, 200, 400, 1000] # Must be of length 4
RARITY_RANKS_COLOURS = [
"red",
"yellow",
"orange",
"green",
"blue",
] # Must be of length 5
# The API used to get sales data doesn't return the token used in the tx
# It's assumed the sale was in ETH, so collections that had sales with other tokens will display abnormal values in the sale price
# For this cases use MAX_PRICE to filter out those transactions
MAX_PRICE = 0 # value in ETH, Leave as 0 for no filter
# PLOT AESTHETIC PARAMETERS
DOT_SIZE = 8
DOT_OPACITY = 0.5
# Load Raw attributes and Rarity Data
try:
RAW_ATTRIBUTES = pd.read_csv(f"{config.ATTRIBUTES_FOLDER}/{COLLECTION_NAME}.csv")
except:
print(f"Unable to find Attributes data for {COLLECTION_NAME}")
raise ValueError(
f"Raw attributes not available in '{config.ATTRIBUTES_FOLDER}/{COLLECTION_NAME}.csv'"
)
# Load Rarity Data
try:
RARITY_DB = pd.read_csv(f"{config.RARITY_FOLDER}/{COLLECTION_NAME}_raritytools.csv")
except:
print(f"Unable to load rarity data for {COLLECTION_NAME}")
raise ValueError(
f"Rarity data not available in '{config.RARITY_FOLDER}/{COLLECTION_NAME}_raritytools.csv'"
)
# Load sales data
try:
SALES_DATA = pd.read_csv(f"{config.SALES_DATA_FOLDER}/{COLLECTION_NAME}.csv")
except:
print(f"Unable to load sales data for {COLLECTION_NAME}")
raise ValueError(
f"Sales Data not available in '{config.SALES_DATA_FOLDER}/{COLLECTION_NAME}.csv'"
)
# dummy dataframe to add rarity rank to RAW_ATTRIBUTES
simple_rarity_db = RARITY_DB[["TOKEN_ID", "RARITY_SCORE", "Rank"]]
# add rarity rank to RAW_ATTRIBUTES
RAW_ATTRIBUTES = pd.merge(RAW_ATTRIBUTES, simple_rarity_db, on="TOKEN_ID")
RAW_ATTRIBUTES = RAW_ATTRIBUTES.astype(object).replace(
np.nan, "None"
) # Necessary to remove NaN values for the categorical graph
# add rarity rank to SALES_DATA
SALES_DATA = SALES_DATA.reset_index(drop=True)
RARITY_DB = RARITY_DB.reset_index(drop=True)
SALES_DATA = SALES_DATA.merge(RARITY_DB, left_on="TOKEN_ID", right_on="TOKEN_ID")
SALES_DATA["priceETH"] = SALES_DATA["price"].astype(float) / 1e18
SALES_DATA["RARITY_SCORE"] = SALES_DATA["RARITY_SCORE"].astype(float)
# Filter Sales Data for abnormal values
if MAX_PRICE != 0:
SALES_DATA = SALES_DATA.loc[SALES_DATA["priceETH"] < MAX_PRICE]
# Create strings for rarity ranks categories
RARITY_RANKS_CAT = [
f"≤{RARITY_RANKS[0]}",
f"≤{RARITY_RANKS[1]}",
f"≤{RARITY_RANKS[2]}",
f"≤{RARITY_RANKS[3]}",
f">{RARITY_RANKS[3]}",
]
# Color ranges
SALES_DATA["rankCategory"] = RARITY_RANKS_CAT[4]
RARITY_DB["rankCategory"] = RARITY_RANKS_CAT[4]
for i in reversed(range(len(RARITY_RANKS))):
SALES_DATA.loc[
SALES_DATA["Rank"] <= RARITY_RANKS[i], "rankCategory"
] = RARITY_RANKS_CAT[i]
RARITY_DB.loc[
RARITY_DB["Rank"] <= RARITY_RANKS[i], "rankCategory"
] = RARITY_RANKS_CAT[i]
[3]:
#
# SALE DATE vs PRICE IN ETH
#
fig = px.scatter(
SALES_DATA,
x="saleDate",
y="priceETH",
hover_data=["TOKEN_ID", "priceETH", "Rank"],
color="rankCategory",
color_discrete_sequence=RARITY_RANKS_COLOURS,
category_orders={"rankCategory": RARITY_RANKS_CAT},
title=f"<b>{COLLECTION_TITLE}</b> - Sales date vs Price<br><sup>(Low rank is more rare)</sup>",
)
fig.update_traces(
marker=dict(size=DOT_SIZE, line=dict(width=1, color="DarkSlateGrey")),
selector=dict(mode="markers"),
opacity=DOT_OPACITY,
)
fig.update_layout(
legend_title_text="Rarity Rank", xaxis_title="Sale Date", yaxis_title="ETH price"
)
if len(ANNOTATIONS) > 0:
for note in ANNOTATIONS:
fig.add_vline(
# need to convert REVEAL_DATE to epoch timestamp due to a bug in plolty add_vline
x=datetime.datetime.strptime(note["date"], "%Y-%m-%d %H:%M:%S").timestamp()
* 1000,
line_dash="dot",
line_width=3,
annotation_text=note["label"],
annotation_font_size=12,
)
fig.show()
[4]:
#
# TOKEN ID vs RARITY RANK (Similar to rarity.ipynb)
#
fig_rarity = px.scatter(
RARITY_DB,
x="TOKEN_ID",
y="Rank",
color="rankCategory",
color_discrete_sequence=RARITY_RANKS_COLOURS,
category_orders={"rankCategory": RARITY_RANKS_CAT},
title=f"<b>{COLLECTION_TITLE}</b> - Token Id vs Rarity Rank<br><sup>(Low rank is more rare)</sup>",
)
fig_rarity.update_traces(
marker=dict(size=DOT_SIZE, line=dict(width=1, color="DarkSlateGrey")),
selector=dict(mode="markers"),
opacity=DOT_OPACITY,
)
fig_rarity.update_layout(
legend_title_text="Rarity Rank", xaxis_title="Token ID", yaxis_title="Rarity Rank"
)
fig_rarity.show()
[5]:
#
# TOKEN ID vs RARITY RANK (Numerical Rank with custom color scale)
#
fig_rarity = px.scatter(
RARITY_DB,
x="TOKEN_ID",
y="Rank",
hover_data=["Rank"],
color="Rank",
color_continuous_scale=[
[0.0, "rgb(165,0,38)"],
[0.03, "rgb(215,48,39)"],
[0.06, "rgb(244,109,67)"],
[0.09, "rgb(253,174,97)"],
[0.12, "rgb(254,224,144)"],
[0.15, "rgb(224,243,248)"],
[1.0, "rgb(49,54,149)"],
],
title=f"<b>{COLLECTION_TITLE}</b> - Token Id vs Rarity Rank<br><sup>(Low rank is more rare)</sup>",
)
fig_rarity.update_traces(
marker=dict(size=DOT_SIZE, line=dict(width=1, color="DarkSlateGrey")),
selector=dict(mode="markers"),
opacity=DOT_OPACITY,
)
fig_rarity.update_layout(
legend_title_text="Rarity Rank", xaxis_title="Token ID", yaxis_title="Rarity Rank"
)
fig_rarity.show()
[6]:
# Print available traits in this collection
TRAITS = list(
set(list(RAW_ATTRIBUTES.columns))
- set(["TOKEN_ID", "TOKEN_NAME", "RARITY_SCORE", "Rank"])
)
print("Available traits in this collection:")
print(TRAITS)
Available traits in this collection:
['SpecialBackground', 'CrownedMutant', 'SpecialEyewear', 'SpecialClothing', 'SpecialAccessories', 'Unnamed: 0', 'SpecialHeadwear', 'Mutant', 'XrayBackground', 'Clothing', 'Background', 'XrayMutant', 'AbstractBackground', 'CrownedAccessories', 'HoloBackground', 'Headwear', 'HoloMutant', 'Accessories', 'SpecialMutant', 'AbstractMutants', 'CrownedBackground']
[7]:
#
# TOKEN ID vs RARITY RANK BY TRAIT
#
# Select trait to use as colour category
TRAIT = "AbstractBackground"
fig_categorical = px.scatter(
RAW_ATTRIBUTES,
x="TOKEN_ID",
y="Rank",
color=f"{TRAIT}",
title=f"<b>{COLLECTION_TITLE}</b> - Token Id vs Rarity Rank - Color by <b>{TRAIT}</b><br><sup><i>(Low rank is more rare)</i></sup>",
)
fig_categorical.update_traces(
marker=dict(size=DOT_SIZE, line=dict(width=1, color="DarkSlateGrey")),
selector=dict(mode="markers"),
opacity=DOT_OPACITY,
)
fig_categorical.update_layout(
legend_title_text="Rarity Rank", xaxis_title="Token ID", yaxis_title="Rarity Rank"
)
fig_categorical.show()
[8]:
"""
WARNING:
This script creates 1 plot for each trait in the collection
"""
#
# TOKEN ID vs RARITY RANK BY TRAIT
#
# Get all trait categories in the collection
TRAITS = list(
set(list(RAW_ATTRIBUTES.columns))
- set(["TOKEN_ID", "TOKEN_NAME", "RARITY_SCORE", "Rank"])
)
for TRAIT in TRAITS:
fig_categorical = px.scatter(
RAW_ATTRIBUTES,
x="TOKEN_ID",
y="Rank",
color=f"{TRAIT}",
title=f"<b>{COLLECTION_TITLE}</b> - Token Id vs Rarity Rank - Color by <b>{TRAIT}</b><br><sup><i>(Low rank is more rare)</i></sup>",
)
fig_categorical.update_traces(
marker=dict(size=DOT_SIZE, line=dict(width=1, color="DarkSlateGrey")),
selector=dict(mode="markers"),
opacity=DOT_OPACITY,
)
fig_categorical.update_layout(
legend_title_text="Rarity Rank",
xaxis_title="Token Id",
yaxis_title="Rarity Rank",
)
fig_categorical.show()
TODO: Add notes to graph with link to Convex Labs twitter and HonestNFT Github