<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Introduction
This work is inspired by this paper from  Elie and Celine Bursztein and will try to reproduce their findings applying some different ideas.
Step1:  Cards data
Load the collectible cards inside JSON data
Let's start loading the game cards data from Hearthstone JSON and loading it to python using the json library.
Step2:  Card types
Step3:  Card attributes
Step4:  Card mechanics
Step5:  The model
Card analysis will be done based on the following base model equation
Step6:  Vanilla minions modelling
To test the model, let's extract the coefficients for attack and health with only the minions with no text (vanilla minions).
Step7:  Let the pricing begin! Results will be stored in a new price attribute in each card. Also, coeffs are computed taking into account that a card costs
Step8:  With these coeffs, we can define a ratio attribute with the ratio between the real price and cost
Step9:  These results can be bad to anyone with some experience, because Wisp is listed in the first place with a great distance to the second and River Crocolisk (a good vanilla) is on the low end. But this was only a example of how the model works. More complex examples below.
Adding simple mechanics
To enrich the model, let's add simple mechanics to a minion-only matrix.
Processing the cards mechanics
Cards have to be processed to extract the card mechanics (Charge, Stealth, Windfury, Taunt, Divine Shield) from the text, adding the text_mechanics attribute with the complex mechanics. All cards with unknown mechanics are discarded when processed.
Step10:  Let's price this bunch of minions as described before.
Step11:  Results
0 cost minions
Step12:  1 cost minions
Step13:  2 cost minions
Step14:  3 cost minions
Step15:  4 cost minions
Step16:  5 cost minions
Step17:  6 cost minions
Step18:  7+ cost minions
Step19:  Some statistics
Deathrattle minions
Let's see what is the expected damage from a Scarlet Purifier.
Step20:  2-cost minions
Let's see what is the expected minion stats from a Piloted Shredder deathrattle.
<ASSISTANT_TASK:>
Python Code:
from hearthpricer import hearthpricer
import numpy
import os.path
import pandas
Explanation: Introduction
This work is inspired by this paper from  Elie and Celine Bursztein and will try to reproduce their findings applying some different ideas.
End of explanation
all_sets_filename = os.path.join('data', 'AllSets.json')
# Uncomment the following lines to update the date file
#import urllib
#urllib.urlretrieve ('http://hearthstonejson.com/json/AllSets.json', all_sets_filename)
all_collectible_cards = hearthpricer.load_json(all_sets_filename)
print('# of collectible cards:', len(all_collectible_cards))
Explanation: Cards data
Load the collectible cards inside JSON data
Let's start loading the game cards data from Hearthstone JSON and loading it to python using the json library.
End of explanation
print('Card types:', ', '.join(set((x['type'] for x in all_collectible_cards))))
Explanation: Card types
End of explanation
print('Card attributes:', ', '.join(set(sum((list(x.keys()) for x in all_collectible_cards), list()))))
Explanation: Card attributes
End of explanation
print('Card mechanics:', ', '.join(
        set(sum((x['mechanics'] for x in all_collectible_cards if 'mechanics' in x), list()))))
Explanation: Card mechanics
End of explanation
all_collectible_cards_df = pandas.DataFrame(all_collectible_cards)
all_collectible_cards_df.info()
Explanation: The model
Card analysis will be done based on the following base model equation:
$$cost = \sum (attribute_i \cdot coeff_i) + intrinsic$$
The intrinsic value represents the cost of having that card in your deck and also can be viewed as the slot_cost.
Modelling the cards
With the previous model, let's create a matrix with all the information to work with.
End of explanation
vanilla_minions_df = pandas.DataFrame(
    all_collectible_cards_df[(all_collectible_cards_df['type'] == 'Minion') &
                             (all_collectible_cards_df['text'].isnull())])
vanilla_minions_df.info()
Explanation: Vanilla minions modelling
To test the model, let's extract the coefficients for attack and health with only the minions with no text (vanilla minions).
End of explanation
vanilla_columns = ['attack', 'health']
vanilla_coeffs = hearthpricer.pricing(vanilla_minions_df, vanilla_columns, debug=True)
Explanation: Let the pricing begin! Results will be stored in a new price attribute in each card. Also, coeffs are computed taking into account that a card costs: $2 \cdot cost + 1$. Although, price value will be comparable to cost.
End of explanation
intrinsic = vanilla_coeffs[0][0]
vanilla_minions_df['ratio'] = (vanilla_minions_df['price'] -  vanilla_minions_df['cost']) / \
                              (vanilla_minions_df['cost'] - intrinsic)
vanilla_minions_df[['name', 'cost', 'price', 'ratio']].sort('ratio', ascending=False)
Explanation: With these coeffs, we can define a ratio attribute with the ratio between the real price and cost:
$$ratio = \frac{(price - intrinsic) - (cost - intrinsic)}{cost - intrinsic} = \frac{price - cost}{cost - intrinsic}$$
Then sort the results from the best to the worst in terms of ratio.
End of explanation
all_mechanics_cards = hearthpricer.process_mechanics(all_collectible_cards)
print('# of processed cards: {} ({:.2%})'.format(
        len(all_mechanics_cards), 1.0 * len(all_mechanics_cards) / len(all_collectible_cards)))
Explanation: These results can be bad to anyone with some experience, because Wisp is listed in the first place with a great distance to the second and River Crocolisk (a good vanilla) is on the low end. But this was only a example of how the model works. More complex examples below.
Adding simple mechanics
To enrich the model, let's add simple mechanics to a minion-only matrix.
Processing the cards mechanics
Cards have to be processed to extract the card mechanics (Charge, Stealth, Windfury, Taunt, Divine Shield) from the text, adding the text_mechanics attribute with the complex mechanics. All cards with unknown mechanics are discarded when processed.
End of explanation
all_mechanics_cards_df = pandas.DataFrame(all_mechanics_cards)
mechanics_coeffs = hearthpricer.pricing(all_mechanics_cards_df, debug=True)
all_processed_cards = hearthpricer.process_mechanics(all_collectible_cards,
                                                     discard_unknown_mechanics=False)
text_mechanics = dict()
for card in all_processed_cards:
    if 'text_mechanics' in card:
        text_mechanics[card['text_mechanics']] = text_mechanics.get(card['text_mechanics'], 0) + 1
sorted(((y, x) for x, y in text_mechanics.items()), reverse=True)
intrinsic = vanilla_coeffs[0][0]
all_mechanics_cards_df['ratio'] = (all_mechanics_cards_df['price'] -  all_mechanics_cards_df['cost']) / \
                                  (all_mechanics_cards_df['cost'] + intrinsic)
results_df = all_mechanics_cards_df[['playerClass', 'name', 'cost', 'price', 'ratio']].sort(
    'ratio', ascending=False)
Explanation: Let's price this bunch of minions as described before.
End of explanation
results_df[results_df.cost == 0]
Explanation: Results
0 cost minions
End of explanation
results_df[results_df.cost == 1]
Explanation: 1 cost minions
End of explanation
results_df[results_df.cost == 2]
Explanation: 2 cost minions
End of explanation
results_df[results_df.cost == 3]
Explanation: 3 cost minions
End of explanation
results_df[results_df.cost == 4]
Explanation: 4 cost minions
End of explanation
results_df[results_df.cost == 5]
Explanation: 5 cost minions
End of explanation
results_df[results_df.cost == 6]
Explanation: 6 cost minions
End of explanation
results_df[results_df.cost >= 7]
Explanation: 7+ cost minions
End of explanation
minions_df = all_collectible_cards_df[(all_collectible_cards_df['type'] == 'Minion')]
deathrattle_minions_df = minions_df[minions_df.apply(
        lambda x: x['mechanics'] is not numpy.nan and ('Deathrattle' in x['mechanics']), axis=1)]
print('Deathrattle minions population: {} ({:.2%})'.format(
        len(deathrattle_minions_df), 1.0 * len(deathrattle_minions_df) / len(minions_df)))
Explanation: Some statistics
Deathrattle minions
Let's see what is the expected damage from a Scarlet Purifier.
End of explanation
two_cost_minions_df = all_collectible_cards_df[(all_collectible_cards_df['type'] == 'Minion') &
                                               (all_collectible_cards_df['cost'] == 2)]
print('2-cost minion mean attack: {:.2f}'.format(two_cost_minions_df.attack.mean()))
print('2-cost minion mean health: {:.2f}'.format(two_cost_minions_df.health.mean()))
Explanation: 2-cost minions
Let's see what is the expected minion stats from a Piloted Shredder deathrattle.
End of explanation
<END_TASK>