有没有办法创建一个 pandas 数据框,其行是整数,这些整数会增加直到每行达到某个值?

Is there a way to create a pandas dataframe whose rows are integers which increase until a certain value is reached in each row?

例如,假设我有一个整数数组 [5, 3, 7, 6, 4]。我希望找到一种有效的方法来创建一个 pandas 数据框,如下所示:

这应该是数据框中的第一行包含数字 1 到 5,第二行应该包含数字 1 到 3,依此类推。

有没有不用循环实现的方法?

设置 df:

df = pd.DataFrame([[1,2,3,4,5,6,7]]*5)

逐行过滤最大值:

max_row_val = [5, 3, 7, 6, 4]
df.ge(max_row_val, axis=0)

       0      1      2      3      4      5      6
0  False  False  False  False  False   True   True
1  False  False  False   True   True   True   True
2  False  False  False  False  False  False  False
3  False  False  False  False  False  False   True
4  False  False  False  False   True   True   True

并且只需在 True

将 df 设置为 nan
df[df.ge(max_row_val, axis=0)] = np.nan
   0  1  2    3    4    5    6
0  1  2  3  4.0  5.0  NaN  NaN
1  1  2  3  NaN  NaN  NaN  NaN
2  1  2  3  4.0  5.0  6.0  7.0
3  1  2  3  4.0  5.0  6.0  NaN
4  1  2  3  4.0  NaN  NaN  NaN

对于这个大小的 df,在我的机器上每个循环实现了 0.0003582255399999667s。

您可以使用 .apply() 方法来完成。 但是,如果您需要在大数据帧上执行此操作,性能将非常糟糕。 solution with apply methode

具有range功能的简单单行

pd.DataFrame(range(1, x+1) for x in [5, 3, 7, 6, 4])

输出

     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

使用 numpy 和屏蔽数组提高性能:

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))

我们首先形成 n,即 1..max(a) 重复 a 的长度,然后找到 m 掩盖 np.NaN 的适当位置。然后,将掩码数组传递给框架构造函数,

获得

     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

时间:

对于给定的设置:

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

对于大数组:

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