有没有办法在熊猫数据框上并行化此循环?

时间:2019-05-19 18:34:11

标签: python pandas

我的数据框中有8列,值的范围可以从1到99。我试图创建其他列,例如'1_observed','2_observed','3_observed'...'99_observed' ,具体取决于该观察结果中是否出现了这些数字。

我正在运行的代码可以工作,但是由于我在一个循环中运行一个循环,所以速度很慢。

for index in df[observed_nos].index:
    for num in range(1,100):
        if num in df[observed_nos].iloc[index].values:
            df[f'{num}_observed'][index] = '1'
        else:
            df[f'{num}_observed'][index] = '0'

我对熊猫没有丰富的经验,有没有办法使它运行更快/并行化呢?

编辑: 示例数据帧如下:

data = [[12,14,33,45,22,33,86,56],
        [78,12,52,1,99,22,4,19],
        [15,11,7,23,30,19,63,71],
        [2,14,52,36,17,95,8,39],
        [1,4,31,42,72,23,67,15],
        [92,28,32,52,77,19,55,10],
        [42,16,64,25,92,11,26,36],
        [12,21,38,17,90,32,41,74],
       ]
df = pd.DataFrame(data, columns =['N1','N2','N3','N4','N5','N6','N7','N8'])

这将导致以下df

.   N1  N2  N3  N4  N5  N6  N7  N8
0   12  14  33  45  22  33  86  56
1   78  12  52  1   99  22  4   19
2   15  11  7   23  30  19  63  71
3   2   14  52  36  17  95  8   39
4   1   4   31  42  72  23  67  15
5   92  28  32  52  77  19  55  10
6   42  16  64  25  92  11  26  36
7   12  21  38  17  90  32  41  74

我想要得到的输出如下:

    N1  N2  N3  N4  N5  N6  N7  N8  1_  2_  3_  4_  5_  6_  7_  8_  9_
0   12  14  33  45  22  33  86  56  0   0   0   0   0   0   0   0   0
1   78  12  52  1   99  22  4   19  1   0   0   1   0   0   0   0   0
2   15  11  7   23  30  19  63  71  0   0   0   0   0   0   1   0   0
3   2   14  52  36  17  95  8   39  0   1   0   0   0   0   0   1   0
4   1   4   31  42  72  23  67  15  1   0   0   1   0   0   0   0   0
5   92  28  32  52  77  19  55  10  0   0   0   0   0   0   0   0   0
6   42  16  64  25  92  11  26  36  0   0   0   0   0   0   0   0   0
7   12  21  38  17  90  32  41  74  0   0   0   0   0   0   0   0   0

(我已将上面的示例删节了,仅检查数字1-9的出现,以便于查看)

2 个答案:

答案 0 :(得分:1)

我和熊猫玩了一会儿,发现了另一个可能适合您的解决方案。尽管它没有提供0和1,但是提供了Trua和False(您可能需要修改数据以满足您的需要)。

此外,您可能想检查一下这段代码实际上是否比您的代码快:

rand = np.random.RandomState(42)
items = rand.randint(1, 100, 800).reshape((100, 8))

df = pd.DataFrame(items)

for n in range(1, 100):
    df[f'{n}_observed'] = df[df == n].any(axis=1)
print(df)

希望这个建议对您有帮助!

答案 1 :(得分:1)

如果数字为正数,则可以将它们视为2D映射网格上的索引。因此,使用布尔网格数组,将给定值用作列索引,对于输入数据帧的每一行,使用相同的行索引。现在,使用这些行索引和列索引,在其中分配True值。当以int数组查看时,该网格也将是您的最终数组。因此,实现看起来像这样-

def presence_df(df, start=1, stop=99, str_postfix='_'):
    c = df.to_numpy()
    n = len(c)
    id_ar = np.zeros((n,stop+1), dtype=bool)
    id_ar[np.arange(n)[:,None],c] = 1
    df1 = pd.DataFrame(id_ar[:,start:stop+1].view('i1'))
    df1.columns = [str(i) + str_postfix for i in range(start,stop+1)]
    df_out = pd.concat([df,df1],axis=1)
    return df_out

样品运行-

In [41]: np.random.seed(0)
    ...: df = pd.DataFrame(np.random.randint(1,10,(8,10)))

In [42]: presence_df(df,start=1, stop=9)
Out[42]: 
   0  1  2  3  4  5  6  7  8  9  1_  2_  3_  4_  5_  6_  7_  8_  9_
0  6  1  4  4  8  4  6  3  5  8   1   0   1   1   1   1   0   1   0
1  7  9  9  2  7  8  8  9  2  6   0   1   0   0   0   1   1   1   1
2  9  5  4  1  4  6  1  3  4  9   1   0   1   1   1   1   0   0   1
3  2  4  4  4  8  1  2  1  5  8   1   1   0   1   1   0   0   1   0
4  4  3  8  3  1  1  5  6  6  7   1   0   1   1   1   1   1   1   0
5  9  5  2  5  9  2  2  8  4  7   0   1   0   1   1   0   1   1   1
6  8  3  1  4  6  5  5  7  5  5   1   0   1   1   1   1   1   1   0
7  4  5  5  9  5  4  8  6  6  1   1   0   0   1   1   1   0   1   1

给定样本数据和较大样本的时间-

In [17]: data = [[12,14,33,45,22,33,86,56],
    ...:         [78,12,52,1,99,22,4,19],
    ...:         [15,11,7,23,30,19,63,71],
    ...:         [2,14,52,36,17,95,8,39],
    ...:         [1,4,31,42,72,23,67,15],
    ...:         [92,28,32,52,77,19,55,10],
    ...:         [42,16,64,25,92,11,26,36],
    ...:         [12,21,38,17,90,32,41,74],
    ...:        ]
    ...: df = pd.DataFrame(data, columns =['N1','N2','N3','N4','N5','N6','N7','N8'])

In [18]: %timeit presence_df(df)
1000 loops, best of 3: 575 µs per loop

In [19]: df = pd.DataFrame(np.random.randint(1,100,(1000,1000)))

In [20]: %timeit presence_df(df)
100 loops, best of 3: 8.86 ms per loop