【问题标题】:Step left rows values until not null in python在python中向左行值直到不为空
【发布时间】:2019-03-21 06:06:17
【问题描述】:

我必须处理大量数据。每行都以 1 或 0 开头。我需要一个数据框,其中每行都以 1 开头,所以我必须将所有行的值向左移动,直到第一个值为 1。

例如:

0 1 0 0 1 0 0
1 0 0 0 0 1 1
0 0 0 1 0 0 1
0 0 0 0 0 1 1

结果必须是这样的:

1 0 0 1 0 0 0
1 0 0 0 0 1 1
1 0 0 1 0 0 0
1 1 0 0 0 0 0

我不想使用 for、while 等,因为我需要一些更快的 pandas 或 numpy 方法。

你对这个问题有想法吗?

【问题讨论】:

  • 你是循环移位还是填充0
  • @pault 想要标记为骗子?刚刚注意到我的解决方案基本上是那里公认的答案。
  • @user3483203 我不知道 - 这取决于所需的转变是否是循环的,不是吗?
  • @pault 循环移位无关紧要,因为当 1 位于索引 0 中时,您将始终停止,这意味着 1 永远不会回绕。无论哪种方式,另一个答案都没有谈论如何确定偏移量,所以我认为保持打开状态很好

标签: python pandas performance numpy dataframe


【解决方案1】:

您可以使用cummax 来掩盖所有需要移动的位置为NaNsorted

df[df.cummax(1).ne(0)].apply(lambda x : sorted(x,key=pd.isnull),1).fillna(0).astype(int)
Out[310]: 
   1  2  3  4  5  6  7
0  1  0  0  1  0  0  0
1  1  0  0  0  0  1  1
2  1  0  0  1  0  0  0
3  1  1  0  0  0  0  0

或者我们使用 Divakar 编写的函数justify(比应用排序快得多)

pd.DataFrame(justify(df[df.cummax(1).ne(0)].values, invalid_val=np.nan, axis=1, side='left')).fillna(0).astype(int)
Out[314]: 
   0  1  2  3  4  5  6
0  1  0  0  1  0  0  0
1  1  0  0  0  0  1  1
2  1  0  0  1  0  0  0
3  1  1  0  0  0  0  0

【讨论】:

  • 伙计,Divakar 是个麻木的神,该死的
【解决方案2】:

您可以在这里使用numpy.ogrid

a = df.values
s = a.argmax(1) * - 1
m, n = a.shape
r, c = np.ogrid[:m, :n]
s[s < 0] += n
c = c - s[:, None]
a[r, c]

array([[1, 0, 0, 1, 0, 0, 0],
       [1, 0, 0, 0, 0, 1, 1],
       [1, 0, 0, 1, 0, 0, 0],
       [1, 1, 0, 0, 0, 0, 0]], dtype=int64)

时间

In [35]: df = pd.DataFrame(np.random.randint(0, 2, (1000, 1000)))

In [36]: %timeit pd.DataFrame(justify(df[df.cummax(1).ne(0)].values, invalid_val=np.nan, axis=1, side='left')).fillna(0).a
    ...: stype(int)
116 ms ± 640 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [37]: %%timeit
    ...: a = df.values
    ...: s = a.argmax(1) * - 1
    ...: m, n = a.shape
    ...: r, c = np.ogrid[:m, :n]
    ...: s[s < 0] += n
    ...: c = c - s[:, None]
    ...: pd.DataFrame(a[r, c])
    ...:
    ...:
11.3 ms ± 18.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

【讨论】:

    【解决方案3】:

    为了提高性能,您可以使用numba。一个基本的循环,但考虑到 JIT 编译和在 C 级使用更基本的对象是有效的:

    from numba import njit
    
    @njit
    def shifter(A):
        res = np.zeros(A.shape)
        for i in range(res.shape[0]):
            start, end = 0, 0
            for j in range(res.shape[1]):
                if A[i, j] != 0:
                    start = j
                    break
            res[i, :res.shape[1]-start] = A[i, start:]
        return res
    

    性能基准测试

    def jpp(df):
        return pd.DataFrame(shifter(df.values).astype(int))
    
    def user348(df):
        a = df.values
        s = a.argmax(1) * - 1
        m, n = a.shape
        r, c = np.ogrid[:m, :n]
        s[s < 0] += n
        c = c - s[:, None]
        return pd.DataFrame(a[r, c])    
    
    np.random.seed(0)
    df = pd.DataFrame(np.random.randint(0, 2, (1000, 1000)))
    
    assert np.array_equal(jpp(df).values, user348(df).values)
    
    %timeit jpp(df)      # 9.2 ms per loop
    %timeit user348(df)  # 18.5 ms per loop
    

    【讨论】:

      【解决方案4】:

      这是一个stride_tricks 解决方案,速度很快,因为它可以进行切片复制。

      def pp(x):
          n, m = x.shape
          am = x.argmax(-1)
          mam = am.max()
          xx = np.empty((n, m + mam), x.dtype)
          xx[:, :m] = x
          xx[:, m:] = 0
          xx = np.lib.stride_tricks.as_strided(xx, (n, mam+1, m), (*xx.strides, xx.strides[-1]))
          return xx[np.arange(x.shape[0]), am]
      

      它用所需数量的零填充输入,然后使用as_strided 创建一个滑动窗口视图。这是使用花哨的索引来解决的,但是因为最后一个维度没有被索引,所以行的复制得到了优化和快速。

      有多快?对于与 numba 相当的足够大的输入:

      x = np.random.randint(0, 2, (10000, 10))
      
      from timeit import timeit
      
      shifter(x) # that should compile it, right?
      
      print(timeit(lambda:shifter(x).astype(x.dtype), number=1000))
      print(timeit(lambda:pp(x), number=1000))
      

      样本输出:

      0.8630472810036736
      0.7336142909916816
      

      【讨论】:

      • 是的,我看到这个小的 +/- 与 numba 的不同形状。我需要学习np.lib.stride_tricks,尽管我对the docs 中的免责声明有些犹豫。
      • @jpp 滑动窗口视图在skimage.util中也作为一个现成的功能存在,减少了射脚的危险。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-04
      相关资源
      最近更新 更多