Skip to main content

Poker Analysis using Python (numpy and pandas)

In this I did an analysis on poker. I made my own dataset by randomly selecting 7 cards from the dataset. It does not gives the actual results. It is more of a simulation of actual data.


I have used jupyter-notebook for this you can use any IDE according to your liking.

Start by importing the modules.

import pandas as pd
import numpy as np
import random
import time


Then I created a function calculate_hand which takes hand as a list and returns a boolean list with the corresponding values [royal_flush, straight_flush, four_of_a_kind, full_house, flush, straight, three_of_a_kind, two_pairs, pair, highcard]. If the hand has three cards of same value three_of_a_kind will be True, also pair will be True because if a hand has three of a kind, it also has a pair. Highcard always return the high card.

ALL_CARDS_NUM = {'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14}
ALL_CARDS_NUM_TO_TEXT = {1:'A', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10:'T', 11:'J', 12:'Q', 13:'K', 14:'A'}
FACE_CARDS = {'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14}

def to_number(card):
    if card[0].isnumeric():
        return int(card[0])
    else:
        return FACE_CARDS[card[0]]
   
   
def is_straight(num):
    all_straights = []
    seqcount = np.zeros(7, dtype=np.int)
   
    for i in range(1,len(num)):
        if ALL_CARDS_NUM[num[i-1]] - ALL_CARDS_NUM[num[i]] == -1:
            seqcount[i] = seqcount[i-1] + 1 
        elif ALL_CARDS_NUM[num[i-1]] == ALL_CARDS_NUM[num[i]]:
            seqcount[i] = seqcount[i-1]

    for x in np.argwhere(seqcount >= 4):
        return (True, seqcount)
   
    try:
        if num[-1] == 'A' and seqcount[num.index('5')] >=3:
            return (True, seqcount)
    except:
        pass
   
    return (False, seqcount)


def count_cards(num):
    ptf = {}
    temp_count = np.zeros(13)
   
    for x in num:
        temp_count[ALL_CARDS_NUM[x]%13 - 1] += 1
   
    for x in np.argwhere(temp_count > 1):
        ptf[ALL_CARDS_NUM_TO_TEXT[x[0] + 1]] = int(temp_count[x[0]])
       
    return ptf

def count_suites(suite):
    suites_array = {'S': [],
                   'C': [],
                   'H': [],
                   'D': []}
       
    for i in range(len(suite)):
        suites_array[suite[i]].append(i)

    for key, value in suites_array.items():
        if len(value) >= 5:
            return value

    return []

def calculate_hand(temp_hand, show = False):
    hand = temp_hand.copy()
    hand.sort(key=to_number)
    num = []
    suite = []
   
    hand_value = {}
   
    for x in hand:       
        num.append(x[0])
        suite.append(x[1])
   
   
   
    #straight
    straight, seq = is_straight(num)
    pairs = count_cards(num)
    flushes = count_suites(suite)
   
    #straignt flush
    straight_flush = False
    straight_flush_sequence = []
   
    if straight and flushes:
        for i in range(len(flushes)-4):
            flush_seq = [num[z] for z in flushes[i:i+5]]
            temp, _ = is_straight(flush_seq)
           
            if temp:
                straight_flush = True
                straight_flush_sequence = flush_seq
   
    #royal flush
    royal_flush = False
   
    if straight_flush:
        if straight_flush_sequence[-1] == 'A':
            royal_flush = True
       
    #pairs
    pair_value = np.asarray([value for _,value in pairs.items()], dtype=np.int)

    #four of a kind
    four_of_a_kind = False
    if np.argwhere(pair_value >= 4).size != 0:
        four_of_a_kind = True
       
   
    #three of a kind
    three_of_a_kind = False
    if np.argwhere(pair_value >= 3).size != 0:
        three_of_a_kind = True

   
    #full house
    full_house = False
    if three_of_a_kind and np.argwhere(pair_value >= 2).size >= 2:
        full_house = True
       
    #flush
    flush = False
    if flushes:
        flush = True
   
    #two pairs
    two_pairs = False
   
    if np.argwhere(pair_value >= 2).size >= 2:
        two_pairs = True
       
    #pairs
    pair = bool(pairs)
   
    #high card
    highcard = num[-1]

    if show:
        print(num, suite)
        print("royal flush:\t\t",royal_flush)
        print("straight flush:\t\t", straight_flush)
        print("four of a kind:\t\t", four_of_a_kind)
        print("full house:\t\t", full_house)
        print("flush:\t\t\t", flush)
        print("straight:\t\t", straight)
        print("three of a kind:\t", three_of_a_kind)
        print("Two pairs:\t\t", two_pairs)
        print("Pair:\t\t\t", pair)
        print("High card: \t\t",highcard)
   
   
    return [royal_flush, straight_flush, four_of_a_kind, full_house, flush, straight, three_of_a_kind, two_pairs, pair, highcard]


The other functions here are the helping functions.

Now you can create your dataset:

card_num = ['A','2','3','4','5','6','7','8','9','T','J','Q','K']
suite = ['S','C','H','D']
main = []

def randCard():
    choosen_num = random.choice(card_num)
    choosen_suite = random.choice(suite)
   
    return str(choosen_num) + choosen_suite

start = time.time()

for i in range(1000000):
    temp = []
    for j in range(7):

        choosen_card = randCard()

        while (choosen_card in temp):
            choosen_card = randCard()

        temp.append(choosen_card)
       
    hand_val = calculate_hand(temp)
   
    try:
        top_val = hand_val.index(True)
    except:
        top_val = 9
   
    temp += hand_val
    temp.append(top_val)
   
    if i%100000 == 0:
        print("Time Elapsed",i,"=", time.time() - start)
   
    main.append(temp)

print("\n\nTotal Time Elapsed =", time.time() - start) 


The dataset is stored in the main variable now convert into a pandas dataframe.

x = pd.DataFrame(data=main, columns=["YC1", "YC2", "F1", "F2", "F3", "R", "T", "royal_flush", "straight_flush", "four_of_a_kind", "full_house", "flush", "straight", "three_of_a_kind", "two_pairs", "pair", "highcard", "top_val" ])

x.head()


Now you can either save your data set in csv format or create new dataset every time you decide to use this.

x.to_csv(file_name,index=False)
 
To read the csv file you can use read_csv function
 
x = pd.read_csv(file_name) 

Now we can start analysing the data. 
 
top_hand_value_dict = {
    0: "royal_flush", 
    1: "straight_flush", 
    2: "four_of_a_kind", 
    3: "full_house", 
    4: "flush", 
    5: "straight",
    6: "three_of_a_kind", 
    7: "two_pairs", 
    8: "pair", 
    9: "highcard" 
}

def calculate_hand_value(data, x1, x2):
    
    print("\nIndividuaized values\n")
    hands = data.loc[((data["YC1"] == x1) & (data["YC2"] == x2)) | ((data["YC1"] == x2) & (data["YC2"] == x1))]
    len_hand = len(hands)
    
    print("Length of the sample: ", len_hand, "\n")

    
    for i in range(9):
        perc_val = 100 * hands[top_hand_value_dict[i]].sum()/len_hand
        print(top_hand_value_dict[i] + ":\t", round( perc_val , 3))
    
    z = hands.loc[:, hands.columns[7:len(hands.columns)-1]].sum(axis = 1)
    print("\nNothing:\t\t" , 100 * len(z.loc[z == 0])/len(z) )
        
def calculate_top_values(data, x1, x2):
    
    print("\nTop Values:\n")
    hands = data.loc[((data["YC1"] == x1) & (data["YC2"] == x2)) | ((data["YC1"] == x2) & (data["YC2"] == x1))]
    len_hand = len(hands)
    
    print("Total number of rows:", len_hand)
    
    l = hands['top_val'].value_counts()
    
    for key, value in dict(l).items():
        print(top_hand_value_dict[key] + ":\t", round( (value/len_hand)*100, 3))
     
calculate_hand_value check the possibility of the value of the hand. x1 and x2 are the cards. 
calculate_top_values check the top value of the hand. x1 and x2 are the cards. 
calculate_hand_value(x, "8S", "8D")
calculate_top_values(x, "QH", "JS")

Use above functions to check outputs.
 
def hand_losing(data, YC1, YC2, delt_card):
    
    print("\nChances of loosing this hand: ")
    
    hand = [YC1, YC2] + delt_card
    
    len_delt_card = len(delt_card)
        
    delt_card_hands = data.loc[ data.iloc[:,0:7].isin(delt_card).sum(axis="columns") >= len_delt_card]

    if delt_card_hands.empty:
        print("Sorry no values present!!! Increase the rows database")
    else:
        
        len_hand = len(delt_card_hands)
    
        hand_val = calculate_hand(hand)

        try:
            top_val = hand_val.index(True)
        except:
            top_val = 9
            
        len_lost =  len(delt_card_hands.loc[data['top_val'] < top_val])
        print("Chances of losing by cards value: ", len_lost)
        
        hand_high_card = ALL_CARDS_NUM[hand_val[9]]

        len_lost_high_val = len(
            delt_card_hands.loc[
                (data['top_val'] == top_val) &
                (data['highcard'].apply(lambda i: ALL_CARDS_NUM[i]) > hand_high_card)
            ]
        )
        
        print("Chances of losing by highcard: ", len_lost_high_val)
        
        unpredicted = len(
            delt_card_hands.loc[
                (data['top_val'] == top_val) &
                (data['highcard'] == hand_val[9])
            ]
        )
        
        print("Unpredicted data: ", unpredicted)
        
        
        print("Chances of losing (w unpredicted): ",
              len_lost + len_lost_high_val, "/",len_hand)
        print("Percentage of losing (w unpredicted): ", 
              round(100 * (len_lost + len_lost_high_val)/len_hand,3))
        
        print("Chances of losing (w/o unpredicted): ", 
              len_lost + len_lost_high_val, "/",(len_hand - unpredicted))
        print("Percentage of losing (w/o unpredicted): ", 
              round(100 * (len_lost + len_lost_high_val)/(len_hand - unpredicted),3))
hand_losing gives the number of times your hand can loose given the dataset.
 
hand_losing(x, "8S", "8D", ["8C", "TS", "TH", "9S", "8H"])
 
You can change the number of cards in the list for turn,flop and river.
 
Hope you liked this. 
 

Comments