用扑克牌演示 Python 数据分析

  • 2019 年 10 月 11 日
  • 筆記

1. 序言

扑克牌是我们常见一种娱乐工具,玩法千变万化,为了提高学习 Python 知识的趣味性,我构建了一个扑克牌的数据框,将用它来演示一些 Python 数据分析的功能。

畅想一下,假设利用人工智能的算法,让机器人学会各种扑克牌的玩法,比如说,德州扑克、桥牌、斗地主等等,把机器人训练成玩扑克牌游戏的高手,最终能否实现无人能敌?就像 Alpha Go 战胜人类围棋冠军一样。

说明一下,理解下面的文章,需要你先学习一些 Python 语言的基础知识,我先是看了《利用 Python 进行数据分析》这本书,并把学到的知识应用到了实际的工作中。为了让文章尽可能更加通俗易懂,我下面用一个扑克牌游戏来进行演示。

首先,安装好 Python 的运行环境,建议安装最新版本的 Anaconda,其中默认包含了数据分析所需的大部分库。

为了方便演示和分析,我安装了最新版本的 Jupyter Lab,在命令行输入:jupyter lab,就可以在浏览器中打开和新建 Notebook 文件。

2. 构建一副扑克牌的数据框

在 Jupyter Notebook 中输入以下代码,其中有比较详细的解释说明,如果有不懂或发现不对的地方,欢迎在文章下面留言。

# 导入相关 Python 库  import numpy as np  import pandas as pd  import datetime  from datetime import datetime  import matplotlib  import matplotlib.pyplot as plt  import seaborn as sns    # 设置最大行列数  pd.set_option('max_rows', 20)  pd.set_option('max_columns', 10)    # 显示两位小数  pd.set_option('display.float_format', lambda x: '%.2f' % x)    # 在 Jupyter Notebook 中显示图形  %matplotlib inline    # 构建扑克牌的数据框  nums = list(range(2, 11))  faces = DataFrame(['A'] + nums + ['J', 'Q', 'K'])  values = DataFrame([np.arange(1, 14)] * 4).T    # 定义列名  faces.columns = ['牌面']  values.columns = ['红心♥️', '黑桃♠️', '梅花♣️', '方块♦️']    # 合并两个数据框  poker = pd.merge(faces, values,                   left_index=True, right_index=True)    # 定义大小王  poker.loc[13] = ['小王'] + [np.NaN]*4  poker.loc[14] = ['大王'] + [np.NaN]*4    poker

运行结果如下:

3. 描述性数据分析

我们用一行代码,对扑克牌数据框做一个简单的数据分析:

# 描述性数据分析  poker.describe()

运行结果如下:

从上面的结果可以看出,大小王的缺失值 nan 没有统计在内,每种花色有 13 张扑克牌,平均值为 7,标准差为 3.89,最小值为 1,最大值为 13。其中 25% 代表第一个四分位数,50% 代表第二个四分位数,其实就是中位数,75% 代表第三个四分位数。

这里稍微解释一下四分位数的概念,就是在数据排序后,处于某个位置上的值,比如说,中位数的位置在 1/2 的地方,去掉大小王之后,每种花色有 13 张牌,7 刚好处于正中间,它的前面和后面都有 6 张牌。

好好理解四分位数,这对我们做数据分析非常重要。

4. 数据可视化

我以前写过一篇《Python编程实践(2):数据可视化》,其中用的画图库是 pyecharts,在 Python 语言下,数据可视化的库有很多,是一个比较热门的领域,如果你有兴趣,可以试着去探索更多数据可视化的生态系统。

本文将使用的数据可视化库是 matplotlib 和 seaborn,具体代码如下:

# 先设置字体格式,防止中文乱码  sns.set_style("whitegrid",                {"font.sans-serif": ['simhei', 'Arial']})  matplotlib.rcParams.update({'font.size': 16})    # 绘制箱形图,设置标题和标签的字体大小  poker.plot.box(title='扑克牌点数的箱形图')    # 显示网格线并设置样式  plt.grid(linestyle='--', alpha=0.5)

运行得到的下面这个图形,它把四分位数比较直观地展现了出来,让人对数据的整体分布状况有个整体的把握,如果其中有异常值,那么能够快速地发现。

接下来,我们进一步探索,看看数据的相关性和分布情况:

# 绘制成对的散点图矩阵,在对角线上显示密度估计值  sns.pairplot(poker.iloc[:13],               diag_kind='kde',               markers='+',               height=2,               plot_kws=dict(edgecolor="b"),               diag_kws=dict(shade=True))

运行结果如下:

5. 一个诈金花小游戏

诈金花,又叫三张牌,是一种比较流行的多人纸牌游戏,具有独特的规则,需要考验玩家的胆略和智慧。

从表面上看,诈金花是一种自己可以掌握自己命运的游戏。如果你的牌不好,那么你可以跑,但是别人的牌可能比你更不好,你可能因此懊恼不已。相反,你可能以为自己的牌很好,但是别人的牌可能更好。就像古龙小说《七杀手》中描述的那样,有一个叫杜七的武林高手,一只手长了七根手指,他出现后不久杀了几个人,但转眼就被人灭了,这就叫人外有人,天外有天。

我用 Python 写了一段代码,实现了简单的发牌功能。假设有两个人,去掉扑克牌中的大小王后,每个人随机发 3 张牌,将两个人拿到的牌分别保存到 Excel 文件中,具体代码如下:

# 去掉大小王  poker_no_joker = poker.iloc[:13, 1:].unstack().reset_index()    # 把数据框变成只有 1 列  poker_no_joker = pd.DataFrame(poker_no_joker)    # 设置列名  poker_no_joker.columns = ['花色', '索引', '点数']    # 删除不用的列  del poker_no_joker['索引']    # 总共 52 张牌的索引  poker_index = np.r_[0:52]  person_num = 2  poker_num = 3  poker_choice = None    # 循环发牌  for i in range(person_num):      for j in range(poker_num):          # 随机选 1 张牌          num = np.random.choice(poker_index, 1)            # 刚开始没有牌          if poker_choice is None:              poker_choice = poker_no_joker.iloc[num]          else:              poker_choice = poker_choice.append(poker_no_joker.iloc[num])            # 排除已发过的牌          poker_index = poker_index[poker_index != num[0]]    # 第 1、3、5 张牌给第一个人  person1 = poker_choice.iloc[list(np.arange(0, 6, 2))]    # 第 2、4、6 张牌给第二个人  person2 = poker_choice.iloc[list(np.arange(1, 6, 2))]    # 将两个人拿到的牌分别保存到 Excel 文件中  dt_now = datetime.now().strftime('%Y-%m-%d %H%M%S')  person1.to_excel('第一个人的三张牌-' + dt_now + '.xlsx')  person2.to_excel('第二个人的三张牌-' + dt_now + '.xlsx')

6. 总结

这篇文章,我们试着用娱乐的精神,和大家一起学习 Python,首先,我们构建了一副扑克牌的数据框。然后,我们对其做了一个简单的描述性数据分析。接着,我们对数据进行可视化。最后,我们用了一个诈金花的小游戏,演示了 Python 的一些功能。