As pointed out in the comments, Numba introduces some compilation overhead the first time the function is called (for a particular datatype signature). Whether that should be included in the benchmark is difficult to answer based on the limited information you've shared.
The Numpy functions supported by Numba are convenient and robust, but you can often gain a little extra performance by implementing a specific function for your application.
The parallel=True doesn't do anything as shown by the warning.
Using np.clip you could perhaps gain a little by using the out= keyword if you're willing to modify the input (in place).
Overall I get the best performance using numba.vectorize, as is often the case in my experience.
from numba import njit, vectorize
import numpy as np
def clip1(x, l, u):
return x.clip(l, u)
@njit(fastmath=True)
def clip2(x, l, u):
return x.clip(l, u)
@njit(fastmath=True)
def clip3(x, l, u):
return np.clip(x, l, u, out=x)
@vectorize
def clip4(x, l, u):
return max(min(x, u), l)
On my machine, with a warm-up (excluding compilation), this results in:
clip1: 7.19 µs ± 546 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
clip2: 2.88 µs ± 35.9 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
clip3: 2.54 µs ± 177 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
clip4: 1.2 µs ± 39.3 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
For the record, on my side (Apple M3) the fastest implementation is this one:
@nb.jit(fastmath=True)
def clip(x, l, u):
return np.maximum(np.minimum(x, u), l)
You can simply use np.clip as mozway suggested.
The clip function
First, we import the required libraries.
import numpy as np
import matplotlib.pyplot as plt
And then, simply define the clip function that takes the minimum and the maximum values from the user's input.
def clip(array):
min_val, max_val = [
float(input(i))
for i in ["Minimum value: ", "Maximum value: "]
]
return np.clip(array, min_val, max_val)
Output
We will test our clip function on a sample array.
>>> a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> clip(a)
Minimum value: 3
Maximum value: 8
array([3, 3, 3, 4, 5, 6, 7, 8, 8, 8])
Plotting
We will define an arbitrary array with np.random.randint.
a = np.random.randint(0, 100, 100)
x_values = np.arange(len(a))
Finally, we clip and plot the two arrays as follows.
fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize=(9, 3),
tight_layout=True, dpi=144)
ax1.plot(x, a)
ax1.set_title("Unclipped Array")
ax2.plot(x, clip(a))
ax2.set_title("Clipped Array")
plt.show()
Minimum value: 25
Maximum value: 75

The above plot is our final result.
Based on what your "clipping" should do, here's some idea with "native python" i.e no imports (can be done otherwise using e.g numpy or pandas.Series):
#Remove all elements outside [mi,ma]
a = [1,2,3,4,5,6,7,8,9,10]
mi = 3 #min
ma = 7 #max
list(filter(lambda x: mi<x<ma,a)) # [4,5,6]
#Set elements greater than 7 to 7 and all elements less than 3 to three
def clip_to_min_max(x,mi,max):
if x<mi: #Number is less than "mi" set it to "mi"
return mi
if x>ma: #Number is greater han "max", set it to "ma"
return mx
return x #It is between "mi" and "ma" - do nothing
[clip_to_min_max(x,3,7) for x in a] #[3,3,3,4,5,6,7,7,7,7]