np.linspace returns evenly spaced numbers between two endpoints, and you tell it how many you want rather than how big the gaps are:
np.linspace(start, stop, num=50)
np.linspace(0, 10, 5) # [ 0. 2.5 5. 7.5 10. ]
# ^ stop is INCLUDED
That inclusive stop is the difference from np.arange, which leaves it out. It’s also why linspace is the safer of the two.
Output below is from real runs on Python 3.12.5, NumPy 2.5.3.
Using np.linspace in Python
Three arguments cover almost every use. The third one is a count, which is the part people get wrong:
import numpy as np
print(np.linspace(0, 10, 5)) # 5 values, and 10 IS included
print(np.linspace(0, 1, 11)) # 11 values, step 0.1
# the count is the third argument, not the step
print("\ndefault number of values:", np.linspace(0, 1).size)
Output:
[ 0. 2.5 5. 7.5 10. ]
[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
default number of values: 50
num out and you get 50 values, not 50 steps.np.linspace(0, 1, 11) gives eleven values because the count includes both ends. Ask for 10 and the spacing becomes 0.111 instead of 0.1.
A useful habit: the number of gaps is always one less than the number of values.
The np.linspace formula
The spacing is fixed by the arguments, and retstep=True hands it back so you don’t have to work it out:
import numpy as np
start, stop, num = 0, 10, 5
values, step = np.linspace(start, stop, num, retstep=True)
print("values:", values)
print("step from retstep:", step)
# that is exactly what the formula gives
print("\n(stop - start) / (num - 1) =", (stop - start) / (num - 1))
# with endpoint=False the divisor changes
values2, step2 = np.linspace(start, stop, num, endpoint=False, retstep=True)
print("\nendpoint=False values:", values2)
print("step:", step2, "which is (stop - start) / num =", (stop - start) / num)
Output:
values: [ 0. 2.5 5. 7.5 10. ]
step from retstep: 2.5
(stop - start) / (num - 1) = 2.5
endpoint=False values: [0. 2. 4. 6. 8.]
step: 2.0 which is (stop - start) / num = 2.0
retstep confirms the formula, with and without the endpoint.| Setting | Formula for the step |
|---|---|
endpoint=True (default) | (stop - start) / (num - 1) |
endpoint=False | (stop - start) / num |
The divisor changes because dropping the endpoint removes one value but keeps the same span, so each gap has to be slightly larger.
np.linspace vs np.arange
They answer two different questions. linspace answers “how many values”, arange answers “how big a step”:
import numpy as np
# linspace: "give me N values" -- the stop is included
print("linspace(0, 10, 5) :", np.linspace(0, 10, 5))
# arange: "step by this much" -- the stop is excluded
print("arange(0, 10, 2.5) :", np.arange(0, 10, 2.5))
print()
print("linspace dtype:", np.linspace(0, 10, 5).dtype)
print("arange dtype:", np.arange(0, 10, 2).dtype, "<- integers stay integers")
Output:
linspace(0, 10, 5) : [ 0. 2.5 5. 7.5 10. ]
arange(0, 10, 2.5) : [0. 2.5 5. 7.5]
linspace dtype: float64
arange dtype: int64 <- integers stay integers
np.linspace | np.arange | |
|---|---|---|
| You specify | The number of values | The step size |
| Stop value | Included | Excluded |
| Default dtype | Always float | Follows the inputs |
| Safe with floats | Yes | No, see below |
| Best for | Plot axes, interpolation | Integer counters, indices |
For whole-number sequences arange is the natural choice and keeps the integer dtype. There is more on it in the arange function guide.
Why is np.arange unreliable with float steps?
This is the reason to prefer linspace, and it is stranger than the usual explanation suggests:
import numpy as np
# Same step. Same width of interval. Different number of elements.
a = np.arange(0, 0.3, 0.1)
b = np.arange(1, 1.3, 0.1)
print("arange(0, 0.3, 0.1) ->", a, " length", len(a))
print("arange(1, 1.3, 0.1) ->", b, "length", len(b), "<- 1.3 should NOT be here")
print()
print("arange computes ceil((stop - start) / step) and rounding decides the count:")
print(" (0.3 - 0) / 0.1 =", repr((0.3 - 0) / 0.1))
print(" (1.3 - 1) / 0.1 =", repr((1.3 - 1) / 0.1))
print()
print("linspace cannot do this, because you state the count yourself:")
print(" linspace(1, 1.3, 3) ->", np.linspace(1, 1.3, 3))
Output:
arange(0, 0.3, 0.1) -> [0. 0.1 0.2] length 3
arange(1, 1.3, 0.1) -> [1. 1.1 1.2 1.3] length 4 <- 1.3 should NOT be here
arange computes ceil((stop - start) / step) and rounding decides the count:
(0.3 - 0) / 0.1 = 2.9999999999999996
(1.3 - 1) / 0.1 = 3.0000000000000004
linspace cannot do this, because you state the count yourself:
linspace(1, 1.3, 3) -> [1. 1.15 1.3 ]
np.arange(0, 0.3, 0.1) behaves. np.arange(1, 1.3, 0.1) returns four values and includes 1.3, which it was supposed to exclude.
The cause is in the printed division. (1.3 - 1) / 0.1 does not come out as exactly 3 in binary floating point, so the rounding lands the wrong side of the boundary.
linspace cannot make this mistake. You state the count, so the count is what you get.
np.linspace endpoints and edge cases
Both ends are exact, which matters when the result feeds an interpolation or an axis limit:
import numpy as np
# linspace hits both ends exactly, which matters for plotting and for interpolation
grid = np.linspace(0, 1, 11)
print("first:", repr(grid[0]))
print("last :", repr(grid[-1]))
print("last is exactly 1.0?", grid[-1] == 1.0)
print()
# edge cases worth knowing
print("num=1 ->", np.linspace(0, 10, 1), "(just the start)")
print("num=0 ->", np.linspace(0, 10, 0), "(empty)")
print("backwards ->", np.linspace(10, 0, 5))
Output:
first: np.float64(0.0)
last : np.float64(1.0)
last is exactly 1.0? True
num=1 -> [0.] (just the start)
num=0 -> [] (empty)
backwards -> [10. 7.5 5. 2.5 0. ]
num=1returns just the start value, not the stop.num=0returns an empty array rather than raising.- A start larger than the stop counts downward, no special argument needed.
- A negative
numraisesValueError.
The exact endpoint is why linspace suits axis work. If you’re setting an axis range, a grid that stops at 0.9999999 will quietly clip your last point.
Setting the linspace dtype
linspace always computes in floating point, then casts if you ask it to:
import numpy as np
print("default :", np.linspace(0, 10, 5).dtype)
print("dtype=int :", np.linspace(0, 10, 5, dtype=int), np.linspace(0, 10, 5, dtype=int).dtype)
print("dtype=float32:", np.linspace(0, 1, 5, dtype=np.float32).dtype)
print()
# integer casting TRUNCATES, it does not round
print("float values :", np.linspace(0, 10, 4))
print("as int :", np.linspace(0, 10, 4, dtype=int), "<- 3.33 became 3, 6.67 became 6")
Output:
default : float64
dtype=int : [ 0 2 5 7 10] int64
dtype=float32: float32
float values : [ 0. 3.33333333 6.66666667 10. ]
as int : [ 0 3 6 10] <- 3.33 became 3, 6.67 became 6
dtype=int truncates: 3.33 becomes 3 and 6.67 becomes 6.That truncation catches people out. np.linspace(0, 10, 4, dtype=int) gives [0, 3, 6, 10], where the gaps are no longer even.
If you want evenly spaced integers, use np.arange with an integer step, or round explicitly with np.round before casting.
Using linspace with Matplotlib
Generating a smooth x axis is the single most common use of the function:
import numpy as np
import matplotlib.pyplot as plt
# this is what linspace is for: a smooth x axis
x_coarse = np.linspace(0, 2 * np.pi, 8)
x_smooth = np.linspace(0, 2 * np.pi, 300)
plt.figure(figsize=(8, 4.5))
plt.plot(x_coarse, np.sin(x_coarse), "o-", color="#c0392b", label="num=8")
plt.plot(x_smooth, np.sin(x_smooth), color="#0b6bcb", lw=2, label="num=300")
plt.title("linspace controls how smooth a curve looks")
plt.xlabel("x"); plt.ylabel("sin(x)")
plt.legend(); plt.grid(alpha=.3); plt.tight_layout()
plt.show()
print("8 points :", np.round(x_coarse, 3))
print("300 points: first three", np.round(x_smooth[:3], 4), "...")
Output:
8 points : [0. 0.898 1.795 2.693 3.59 4.488 5.386 6.283]
300 points: first three [0. 0.021 0.042] ...
The rule of thumb is a few hundred points for a smooth line. Beyond about a thousand you’re adding file size, not detail.
Use the same linspace array for both x and the function of x, so they can never fall out of step. The same applies when you draw a best fit line over scattered data.
Two-dimensional linspace with the axis argument
Pass arrays as start and stop and you get one sequence per pair:
import numpy as np
# start and stop can be arrays, giving one row or column per pair
starts = np.array([0, 100])
stops = np.array([1, 200])
print("axis=0 (default):")
print(np.linspace(starts, stops, 5))
print("\naxis=1:")
print(np.linspace(starts, stops, 5, axis=1))
Output:
axis=0 (default):
[[ 0. 100. ]
[ 0.25 125. ]
[ 0.5 150. ]
[ 0.75 175. ]
[ 1. 200. ]]
axis=1:
[[ 0. 0.25 0.5 0.75 1. ]
[100. 125. 150. 175. 200. ]]
axis=0 stacks the sequences as rows, axis=1 as columns. This builds a coordinate grid in one call, with no reshaping.
Common np.linspace mistakes
| Symptom | Cause | Fix |
|---|---|---|
| One more value than expected | num counts values, not gaps | Ask for n, get n |
| Got 50 values | num was left out | Always pass it |
| Spacing is not what you wanted | Confused count with step | Use retstep=True to check |
| Integers are unevenly spaced | dtype=int truncates | Round before casting |
| Stop value is missing | You used arange | Use linspace |
If you work with NumPy ranges a lot, these go well with this page:
- The arange function in Python
- NumPy empty arrays
- NumPy data types
- Create an array from 1 to N
- Find unique values in a NumPy array
- Draw a best fit line with Matplotlib
Frequently asked questions
What does np.linspace do?
It returns evenly spaced numbers over an interval, with the stop value included by default. Full details are in the numpy.linspace reference.
What is the linspace formula?
The step is (stop - start) / (num - 1) by default. With endpoint=False it becomes (stop - start) / num.
What is the difference between linspace and arange?
linspace takes the number of values and includes the stop. arange takes a step size and excludes the stop.
Does np.linspace include the endpoint?
Yes, unless you pass endpoint=False. That is the main practical difference from arange.
How many values does np.linspace return by default?
50, because num defaults to 50. It is easy to miss, since the argument is usually supplied.
How do I get the step size from linspace?
Pass retstep=True and it returns a tuple of the array and the step.
Why is np.arange giving me an extra element?
Floating-point division decides the element count, and it can round the wrong way. np.arange(1, 1.3, 0.1) returns four values including 1.3. Use linspace for float ranges.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn