【问题标题】:Python inverse step slicePython逆步切片
【发布时间】:2021-10-08 06:35:15
【问题描述】:

有时,我想从列表 A 中取出每个第 n 个元素并将其放入列表 B,并将所有其他元素放入列表 C。创建列表 B 是基本的 python 切片。有没有一种优雅的方式来创建列表 C?

例如:

A = [0, 1, 2, 3, 4, 5, 6]
B = A[::3] # B = [0, 3, 6]
C = ???    # C = [1, 2, 4, 5]

我能想到的最好的是:

C = [x for x in A if x not in B]

但是当我们从数学上知道应该包含哪些元素时,检查每个元素的成员资格似乎很愚蠢。特别是因为我好奇的场景往往是在机器学习中进行训练/验证/测试拆分,其中列表可能很长。我也对优雅的 numpy 解决方案持开放态度,但我很好奇纯 python 中是否也存在这种解决方案。

【问题讨论】:

  • FWIW,如果您不需要它是确定性的,为什么不改组您的 A 列表,然后进行更简单的拆分?像B, C = A[:training_size], A[training_size:] 这样的东西。这就是我用来创建我的训练/开发/测试集的方式:)
  • C = list(set(A).difference(B)) 然后你得到了C
  • 否则,你应该找到你需要的on this question。 (注意set 的做法,仅在列表不包含重复且顺序无关紧要时才有效)
  • C = [d for i, d in enumerate(A) if i % 3]

标签: python numpy


【解决方案1】:

纯 Python

正如您已经说过的,构建列表 B 很容易。可以使用来自itertoolscompresscycle 构造列表C:

C = list(compress(A, cycle([0, 1, 1])))

顺便说一句,这比简单循环快一个数量级:

from itertools import compress, cycle
from timeit import timeit


def simple_loop(a):
    result = []
    for pos in range(len(a)):
        if pos % 3:
            result.append(a[pos])
    return result


def one_liner(a):
    return compress(a, cycle([0, 1, 1]))


print('timeit simple_loop:')
print(timeit('simple_loop(list(range(1000)))', number=1000, globals=globals()))
print('timeit one_liner:')
print(timeit('one_liner(list(range(1000)))', number=1000, globals=globals()))

# Output:
# timeit simple_loop:
# 0.1398815
# timeit one_liner:
# 0.013991699999999996

Numpy

再次构造列表 B 很容易,构造列表 C 涉及掩码:

import numpy as np

A = np.arange(10)
B = A[::3]
mask = np.ones(A.size, dtype=bool)
mask[::3] = 0
C = A[mask]

【讨论】:

    【解决方案2】:

    这是一个numpy 解决方案。 想法是创建一个索引数组,然后使用numpy.logical_not

    详细版:

    >>> N = 10
    >>> A = np.array(range(N))
    >>> iB = np.zeros(A.shape, dtype=bool)
    >>> iB[::3] = 1
    >>> iB
    array([ True, False, False,  True, False, False,  True, False, False,
            True])
    >>> B = A[iB]
    >>> B
    array([0, 3, 6, 9])
    >>> iC = np.logical_not(iB)
    >>> C = A[iC]
    >>> C
    array([1, 2, 4, 5, 7, 8])
    

    使用 Albo 建议的语法糖的简短版本:

    idx = np.zeros(A.shape, dtype=bool)
    idx[::3] = 1
    B = A[idx]
    C = A[~idx]
    

    【讨论】:

    • ~iBnp.logical_not(iB)的效果一样,如果要保存一行
    • @Albo 不错,我完全忘记了这个语法糖 :)
    【解决方案3】:

    不是基于 numpy 的解决方案 - 只是纯 python

    from typing import List,Any,Tuple
    
    def slicer(lst:List[Any],n:int)  -> Tuple[List[Any],List[Any]]:
      a = []
      b = []
      for i,x in enumerate(lst):
        if i % n == 0:
          a.append(x)
        else:
          b.append(x)
      return a,b
    
    the_list = [0, 1, 2, 3, 4, 5, 6]
    x,y = slicer(the_list,3)
    print(x)
    print(y)
    

    输出

    [0, 3, 6]
    [1, 2, 4, 5]
    

    【讨论】:

      【解决方案4】:

      一对numpy可能性:

      A = np.array(A)
      
      A[np.resize(np.arange(3).astype(bool),A.size)]
      # array([1, 2, 4, 5])
      A.repeat(np.resize(np.arange(3).clip(None,1),A.size))
      # array([1, 2, 4, 5])
      np.delete(A,np.arange(0,A.size,3))
      # array([1, 2, 4, 5])
      A[np.arange(A.size)%3!=0]
      # array([1, 2, 4, 5])
      

      【讨论】:

        猜你喜欢
        • 2011-08-13
        • 1970-01-01
        • 1970-01-01
        • 2021-11-26
        • 1970-01-01
        • 2019-10-25
        • 2015-09-07
        • 2016-12-26
        相关资源
        最近更新 更多