更快,对熊猫友好的方式来完成此算法?

时间:2018-08-01 06:18:02

标签: python pandas numpy

我有一个非常大的DataFrame,其中每个元素都填充有1-5个整数,如果没有该元素的数据,则为0。我想为其创建两个调整后的副本:

  • train将是一个副本,其中每行随机将20%的非零元素设置为0
  • test将是一个副本,其中除了这些相同的20%元素外,所有其他元素均设置为0

以下是示例:

ORIGINAL
   0  1  2  3  4  5  6  7  8  9
0  3  0  1  1  3  5  3  5  4  2
1  4  2  3  2  3  3  4  4  1  2
2  2  4  2  5  4  4  0  0  4  2

TRAIN
   0  1  2  3  4  5  6  7  8  9
0  3  0  0  1  3  5  3  5  4  2
1  4  2  3  0  3  3  4  4  0  2
2  2  4  2  5  4  4  0  0  4  0

TEST
   0  1  2  3  4  5  6  7  8  9
0  0  0  1  0  0  0  0  0  0  0
1  0  0  0  2  0  0  0  0  1  0
2  0  0  0  0  0  0  0  0  0  2

这是我当前的蛮力算法,可以完成工作,但是速度太慢:

train, test = original.copy(), original.copy()
for i in range(original.shape[0]):
    print("{} / {}".format(i + 1, original.shape[0]))
    row = original.iloc[i]                      # Select row
    nonZeroIndices = np.where(row > 0)[0]       # Find all non-zero indices
    numTest = int(len(nonZeroIndices) * 0.2)    # Calculate 20% of this amount
    rand = np.random.choice(nonZeroIndices, numTest, replace=False)  # Select a rancom 20% of non-zero indices

    for j in range(original.shape[1]):
        if j in rand:
            train.iloc[i, j] = 0
        else:
            test.iloc[i, j] = 0

是否可以使用Pandas或Numpy来实现这一目标?

2 个答案:

答案 0 :(得分:2)

一种方法是

def make_train_test(df):
    train, test = df.copy(), df.copy()
    for i, row in df.iterrows():
        non_zero = np.where(row > 0)[0]
        num_test = int(len(non_zero) * 0.2)
        rand = np.random.choice(non_zero, num_test, replace=False)
        row_train = train.iloc[i, :]
        row_test = test.iloc[i, :]
        row_train[rand] = 0
        row_test[~row_test.index.isin(rand)] = 0
    return train, test

在我的测试中,此过程运行约4.85毫秒,原始解决方案运行约9.07毫秒,andrew_reece(否则为优雅)的解决方案运行15.6毫秒。

答案 1 :(得分:1)

首先,使用sample()创建非零值的20%子集:

subset = df.apply(lambda x: x[x.ne(0)].sample(frac=.2, random_state=42), axis=1)

subset
     1    2    5    8
0  NaN  1.0  NaN  4.0
1  2.0  NaN  NaN  1.0
2  4.0  NaN  4.0  NaN

现在可以通过将train与原始test乘以subsetdf并将fill_value使用1或0来设置{

train = df.apply(lambda x: x.multiply(subset.iloc[x.name].isnull(), fill_value=1), axis=1)

train
   0  1  2  3  4  5  6  7  8  9
0  3  0  0  1  3  5  3  5  0  2
1  4  0  3  2  3  3  4  4  0  2
2  2  0  2  5  4  0  0  0  4  2

test = df.apply(lambda x: x.multiply(subset.iloc[x.name].notnull(), fill_value=0), axis=1)

test
   0  1  2  3  4  5  6  7  8  9
0  0  0  1  0  0  0  0  0  4  0
1  0  2  0  0  0  0  0  0  1  0
2  0  4  0  0  0  4  0  0  0  0

数据:

df
   0  1  2  3  4  5  6  7  8  9
0  3  0  1  1  3  5  3  5  4  2
1  4  2  3  2  3  3  4  4  1  2
2  2  4  2  5  4  4  0  0  4  2