With numpy and masked arrays for performance:
a = [5, 3, 7, 6, 4]
n = np.repeat(np.arange(1, max(a)+1)[None, :], len(a), axis=0)
m = n > np.array(a)[:, None]
df = pd.DataFrame(np.ma.array(n, mask=m))
where we first form n that is 1..max(a) repeated by length of a and then find m that masks the appropriate places for np.NaN. Then, masked array is passed to the frame constructor,
to get
0 1 2 3 4 5 6
0 1.0 2.0 3.0 4.0 5.0 NaN NaN
1 1.0 2.0 3.0 NaN NaN NaN NaN
2 1.0 2.0 3.0 4.0 5.0 6.0 7.0
3 1.0 2.0 3.0 4.0 5.0 6.0 NaN
4 1.0 2.0 3.0 4.0 NaN NaN NaN
timings:
For the given setup:
a = [5, 3, 7, 6, 4]
# @Vishnudev's solution
%timeit pd.DataFrame(range(1, x+1) for x in a)
553 µs ± 25.2 µs per loop
# @Tom Mclean's solution (a bit modified for generalization)
%%timeit
df = pd.DataFrame([list(range(1, max(a)+1))]*len(a))
df[df.ge(a, axis=0)] = np.nan
2.14 ms ± 43.9 µs per loop
# This solution
%%timeit
n = np.repeat(np.arange(1, max(a)+1)[None, :], len(a), axis=0)
m = n > np.array(a)[:, None]
pd.DataFrame(np.ma.array(n, mask=m))
139 µs ± 2.22 µs per loop
For a large array:
a = np.random.randint(3, 10_000, size=5_000)
# @Vishnudev solution
%timeit pd.DataFrame(range(1, x+1) for x in a)
8.12 s ± 76 ms per loop
# @Tom Mclean's solution (a bit modified for generalization)
%%timeit
df = pd.DataFrame([list(range(1, max(a)+1))]*len(a))
df[df.ge(a, axis=0)] = np.nan
15 s ± 199 ms per loop
# This solution
%%timeit
n = np.repeat(np.arange(1, max(a)+1)[None, :], len(a), axis=0)
m = n > np.array(a)[:, None]
pd.DataFrame(np.ma.array(n, mask=m))
583 ms ± 16.1 ms per loop