np.random.randint lets us specify different integer ranges. Using that we can generate (128,4) points with one call. Here I'll use a smaller 10:
In [35]: arr = np.random.randint((1000,40,0,60),(1500,80,100,100),(10,4))
In [36]: arr
Out[36]:
array([[1003, 42, 66, 74],
[1421, 42, 54, 66],
[1212, 46, 22, 61],
[1048, 58, 52, 94],
[1487, 50, 20, 76],
[1249, 45, 80, 74],
[1488, 45, 54, 74],
[1105, 69, 53, 67],
[1341, 47, 36, 84],
[1161, 48, 81, 86]])
and adding the index column:
In [37]: arr = np.concatenate((np.arange(10)[:,None],arr),axis=1)
In [38]: arr
Out[38]:
array([[ 0, 1003, 42, 66, 74],
[ 1, 1421, 42, 54, 66],
[ 2, 1212, 46, 22, 61],
[ 3, 1048, 58, 52, 94],
[ 4, 1487, 50, 20, 76],
[ 5, 1249, 45, 80, 74],
[ 6, 1488, 45, 54, 74],
[ 7, 1105, 69, 53, 67],
[ 8, 1341, 47, 36, 84],
[ 9, 1161, 48, 81, 86]])
Doing the same row by row. Note create arr1 as integer dtype:
In [42]: arr1 = np.zeros((10,5),int)
In [43]: for i in range(10):
...: arr1[i,0]=i
...: arr1[i,1:] = np.random.randint((1000,40,0,60),(1500,80,100,100),(1,4))
...:
In [44]: arr1
Out[44]:
array([[ 0, 1014, 62, 15, 77],
[ 1, 1046, 41, 6, 80],
[ 2, 1198, 67, 77, 67],
[ 3, 1306, 76, 25, 86],
[ 4, 1194, 60, 57, 62],
[ 5, 1068, 75, 32, 76],
[ 6, 1468, 74, 39, 67],
[ 7, 1268, 54, 47, 79],
[ 8, 1191, 75, 0, 71],
[ 9, 1164, 42, 27, 90]])
The same thing a float uses scientific notation because values range from 1 to 1000+:
In [45]: arr1.astype(float)
Out[45]:
array([[0.000e+00, 1.014e+03, 6.200e+01, 1.500e+01, 7.700e+01],
[1.000e+00, 1.046e+03, 4.100e+01, 6.000e+00, 8.000e+01],
...
[9.000e+00, 1.164e+03, 4.200e+01, 2.700e+01, 9.000e+01]])