如何有效地将功能应用于大熊猫数据框的行?

时间:2019-06-11 23:57:38

标签: python pandas

我正在尝试根据当前数据集为模型创建训练数据集。二十一点,每一行都是玩家如何玩牌。

表可能看起来像这样:

|Card1|Card2|Card3|Card4|Card5|PlayerTotal|DealerCard1|Win/Lose
|   7 | 10  |  0  |  0  |  0  |  17       |    10     |  0
|   4 | 3   |  10 |  0  |  0  |  17       |     8     |  1

Id希望仅用玩家手,庄家发牌和赢/输的总和将其分成几行。但是,如果已经玩了2张以上的牌(因此玩家被击中了),那么id希望在每个阶段(因此每次玩家击中之前)都要对该游戏的样本进行多行操作

因此示例将变为:

|PlayerTotal|DealerCard1|Win/Lose
|    17     |     10    |  0
|    7      |     8     |  1
|    17     |     8     |  1

如何有效地做到这一点?

我可以使用pd.apply和一个带有if语句的自定义函数来处理一个小的数据集,但是一旦我使用了整个数据集(〜1毫秒),它的速度就很慢并且占用大量内存。

类似这样的东西:

def extractRounds(x):
    totals = []
    totals.append(x[0:2], x[5], x[6]])

    if x[2] > 0:
        totals.append([sum(x[0:3]), x[5], x[6]])
    else:
        return pd.Series(totals)

    if x[3] > 0:
        totals.append([sum(x[0:4]), x[5], x[6]])
    else:
        return pd.Series(totals)

    if x[4] > 0:
        totals.append([sum(x[0:5]), x[5], x[6]])

    return pd.Series(totals)


b = (a.apply(extractRounds, axis = 1)).stack()

我猜想extractRounds(x)函数不是最有效或效率最高的函数。

所以我想知道我是不是在尝试通过向每行应用一个函数来做到这一点的错误树,还是有更好的方法呢?

让我知道是否不清楚。干杯!

2 个答案:

答案 0 :(得分:1)

您可以使用melt将数据转换为长格式,添加累加和,然后仅排除卡3-5的零卡值。并排除卡1,因为玩家将始终拥有至少2张卡。

以下是您作为数据框的示例:

import pandas as pd
import numpy as np

raw = pd.DataFrame({'Card1': [7, 4],
                    'Card2': [10, 3],
                    'Card3': [0, 10],
                    'Card4': [0, 0],
                    'Card5': [0, 0],
                    'DealerCard1': [10, 8],
                    'PlayerTotal': [17, 17],
                    'Win/Lose': [0, 1]})

raw.index.name = 'Game'

使用melt创建另一个长格式的数据框:

df = (raw.reset_index()
     .melt(value_vars=['Card1', 'Card2', 'Card3', 'Card4', 'Card5'], 
           id_vars=['Game', 'DealerCard1', 'Win/Lose'],
           value_name='CardValue', 
           var_name='Card')
     .sort_values('Game')
     .reset_index(drop=True))

重新创建PlayerTotal列作为累积总和:

df['PlayerTotal'] = df.groupby('Game')['CardValue'].apply(np.cumsum)

然后您可以排除卡1和零卡并选择所需的列:

df.loc[(df['CardValue']!=0) & (df['Card']!='Card1'), ['PlayerTotal', 'DealerCard1', 'Win/Lose']]

这将为您提供:

PlayerTotal DealerCard1 Win/Lose
1   17  10  0
6   7   8   1
7   17  8   1

答案 1 :(得分:0)

您可以使用命令行工具将多余的行添加到csv文件中并进行求和。

假设CSV文件data.csv的前几行是

Card1,Card2,Card3,Card4,Card5,PlayerTotal,DealerCard1,Win/Lose
7,10,0,0,0,17,10,0
4,3,10,0,0,17,8,1

运行以下命令将为我们提供所需的输出

sed 's/\(.*,\)\(.*,\)\([1-9][0-9]*,\)\(.*,.*,.*,.*,.*\)/\1\2\3\4\n\1\20,\4/' data.csv | cut -d ',' -f 1,2,3,7,8 | awk -F ',' 'NR>1 {print $1+$2+$3 "," $4 "," $5}' > data_2.csv

它将创建一个名为data_2.csv的文件,其中包含

17,10,0
17,8,1
7,8,1

--------------------------------

命令说明:

sed 's/\(.*,\)\(.*,\)\([1-9][0-9]*,\)\(.*,.*,.*,.*,.*\)/\1\2\3\4\n\1\20,\4/' data.csv

逐行读取data.csv,如果一行在第三列中的值为0,则添加另一行,其中第三列为0。

| cut -d ',' -f 1,2,3,7,8

读取上一步的数据并将其过滤到第1,2,3,7,8列(这些是我们关心的列)

| awk -F ',' 'NR>1 {print $1+$2+$3 "," $4 "," $5}' > data_2.csv

读取上一步的数据,将前三列相加,并将其与后两列一起写入data_2.csv文件中。