【问题标题】:Multi-dimensional slicing a list of strings with numpy用 numpy 对字符串列表进行多维切片
【发布时间】:2015-10-01 22:25:48
【问题描述】:

假设我有以下内容:

my_list = np.array(["abc", "def", "ghi"])

我想得到:

np.array(["ef", "hi"])

我试过了:

my_list[1:,1:]

然后我得到:

IndexError: too many indices for array

Numpy 是否支持对字符串进行切片?

【问题讨论】:

    标签: python arrays numpy multidimensional-array slice


    【解决方案1】:

    不,你不能那样做。因为 numpy np.array(["abc", "def", "ghi"]) 是一维字符串数组,因此不能使用二维切片。

    您可以将数组定义为二维数组或字符,或者简单地使用列表推导进行切片,

    In [4]: np.asarray([el[1:] for el in my_list[1:]])
    Out[4]: 
    array(['ef', 'hi'], dtype='|S2')
    

    【讨论】:

    • 从 numpy 1.23.0 开始可以
    【解决方案2】:

    您的字符串数组将数据存储为连续的字符块,使用 'S3' dtype 将其划分为长度为 3 的字符串。

    In [116]: my_list
    Out[116]: 
    array(['abc', 'def', 'ghi'], 
          dtype='|S3')
    

    S1,S2 dtype 将每个元素视为 2 个字符串,每个字符串有 1 个和 2 个字符:

    In [115]: my_list.view('S1,S2')
    Out[115]: 
    array([('a', 'bc'), ('d', 'ef'), ('g', 'hi')], 
         dtype=[('f0', 'S1'), ('f1', 'S2')])
    

    选择第二个字段以获取包含所需字符的数组:

    In [114]: my_list.view('S1,S2')[1:]['f1']
    Out[114]: 
    array(['ef', 'hi'], 
          dtype='|S2')
    

    我对@9​​87654325@ 的第一次尝试是将数组拆分为单字节字符串,并使用生成的二维数组:

    In [48]: my_2dstrings = my_list.view(dtype='|S1').reshape(3,-1)
    
    In [49]: my_2dstrings
    Out[49]: 
    array([['a', 'b', 'c'],
           ['d', 'e', 'f'],
           ['g', 'h', 'i']], 
          dtype='|S1')
    

    然后可以在两个维度上对这个数组进行切片。我使用flatten 删除了维度,并强制进行了复制(以获取新的连续缓冲区)。

    In [50]: my_2dstrings[1:,1:].flatten().view(dtype='|S2')
    Out[50]: 
    array(['ef', 'hi'], 
          dtype='|S2')
    

    如果字符串已经在数组中(而不是列表),那么这种方法比列表理解方法快得多。

    wflynny 测试的 1000 x 64 列表的一些计时

    In [98]: timeit [s[1:] for s in my_list_64[1:]]
    10000 loops, best of 3: 173 us per loop   # mine's slower computer
    
    In [99]: timeit np.array(my_list_64).view('S1').reshape(64,-1)[1:,1:].flatten().view('S63')
    1000 loops, best of 3: 213 us per loop
    
    In [100]: %%timeit arr =np.array(my_list_64)
       .....: arr.view('S1').reshape(64,-1)[1:,1:].flatten().view('S63')   .....: 
    10000 loops, best of 3: 23.2 us per loop
    

    从列表中创建数组很慢,但一旦创建,view 方法会快得多。


    查看我之前在np.char 上的注释的编辑历史记录。

    【讨论】:

      【解决方案3】:

      根据 Joe Kington here 的说法,python 非常擅长字符串操作,并且生成器/列表推导对于字符串操作来说既快速又灵活。除非您稍后需要在管道中使用numpy,否则我会强烈反对。

      [s[1:] for s in my_list[1:]]
      

      In [1]: from string import ascii_lowercase
      In [2]: from random import randint, choice
      In [3]: my_list_rand = [''.join([choice(ascii_lowercase) 
                                       for _ in range(randint(2, 64))])
                              for i in range(1000)]
      In [4]: my_list_64 = [''.join([choice(ascii_lowercase) for _ in range(64)])
                            for i in range(1000)]
      
      In [5]: %timeit [s[1:] for s in my_list_rand[1:]]
      10000 loops, best of 3: 47.6 µs per loop
      In [6]: %timeit [s[1:] for s in my_list_64[1:]]
      10000 loops, best of 3: 45.3 µs per loop
      

      使用numpy 只会增加开销。

      【讨论】:

        【解决方案4】:

        从 numpy 1.23.0 开始,我添加了一种机制来更改非连续数组的视图的 dtype。这意味着您可以将数组视为单个字符,按您喜欢的方式对其进行切片,然后将其重新组合在一起。正如@hpaulj 的回答清楚表明的那样,在此之前需要一份副本。

        >>> my_list = np.array(["abc", "def", "ghi"])
        >>> my_list[:, None].view('U1')[1:, 1:].view('U2').squeeze()
        array(['ef', 'hi'])
        

        我正在研究另一层抽象,特别是用于名为np.slice_ 的字符串数组(目前在PR #20694 中进行中,但代码是可运行的)。如果这应该被接受,你将能够做到

        >>> np.char.slice_(my_list[1:], 1)
        array(['ef', 'hi'])
        

        【讨论】:

          【解决方案5】:

          您的切片语法不正确。你只需要my_list[1:] 就可以得到你需要的东西。如果你想将元素复制两次到一个列表中,你可以这样做something = mylist[1:].extend(mylist[1:])

          【讨论】:

          • 请注意,我说的是 Numpy 中的多维切片,它允许使用我使用的语法。另外,my_list[1:] 让我得到['def', 'ghi'],而不是['ef', 'hi']
          猜你喜欢
          • 1970-01-01
          • 2014-07-08
          • 1970-01-01
          • 1970-01-01
          • 2021-11-20
          • 2018-11-07
          • 2019-03-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多