代码之家  ›  专栏  ›  技术社区  ›  KOB

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

  •  1
  • KOB  · 技术社区  · 6 年前

    我有一个非常大的数据框,其中每个元素都填充一个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
    

    有没有一个更快的方法来实现这一点使用熊猫或裸体?

    2 回复  |  直到 6 年前
        1
  •  1
  •   andrew_reece    6 年前

    首先,用 sample() :

    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 可以通过乘法设置 subset 与原作相反 df ,并使用1或0作为 fill_value :

    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
    
        2
  •  2
  •   fuglede    6 年前

    一种方法是

    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úu reece的(其他优雅的)解决方案运行时间大约为15.6毫秒。