【问题标题】:Numpy String Partitioning: Perform Multiple SplitsNumpy 字符串分区:执行多个拆分
【发布时间】:2019-07-23 07:58:50
【问题描述】:

我有一个字符串数组,每个字符串包含一个或多个单词。我想在分隔符(在我的情况下为空白)上拆分/分区数组,拆分次数与包含最多分隔符的元素中的分隔符一样多。 numpy.char.partition 但是只执行一次拆分,无论分隔符出现的频率如何:

我有:

>>> a = np.array(['word', 'two words', 'and three words'])
>>> np.char.partition(a, ' ')

>>> array([['word', '', ''],
       ['two', ' ', 'words'],
       ['and', ' ', 'three words']], dtype='<U8')

我想要:

>>> array([['word', '', '', '', ''],
       ['two', ' ', 'words', '', ''],
       ['and', ' ', 'three', ' ', 'words']], dtype='<U8')

【问题讨论】:

    标签: python string numpy split


    【解决方案1】:

    方法#1

    那些partition 函数似乎并未针对所有事件进行分区。为了解决我们的情况,我们可以使用np.char.split 来获取拆分字符串,然后使用masking,array-assignment,就像这样 -

    def partitions(a, sep):
        # Split based on sep
        s = np.char.split(a,sep)
    
        # Get concatenated split strings
        cs = np.concatenate(s)
    
        # Get params
        N = len(a)
        l = np.array(list(map(len,s)))
        el = 2*l-1
        ncols = el.max()
    
        out = np.zeros((N,ncols),dtype=cs.dtype)
    
        # Setup valid mask that starts at fist col until the end for each row
        mask = el[:,None] > np.arange(el.max())
    
        # Assign sepeter into valid ones
        out[mask] = sep
    
        # Setup valid mask that has True at postions where words are to be assigned
        mask[:,1::2] = 0
    
        # Assign words
        out[mask] = cs
        return out
    

    示例运行 -

    In [32]: a = np.array(['word', 'two words', 'and three words'])
    
    In [33]: partitions(a, sep=' ')
    Out[33]: 
    array([['word', '', '', '', ''],
           ['two', ' ', 'words', '', ''],
           ['and', ' ', 'three', ' ', 'words']], dtype='<U5')
    
    In [44]: partitions(a, sep='ord')
    Out[44]: 
    array([['w', 'ord', ''],
           ['two w', 'ord', 's'],
           ['and three w', 'ord', 's']], dtype='<U11')
    

    方法#2

    这是另一个带循环的,以节省内存 -

    def partitions_loopy(a, sep):
        # Get params
        N = len(a)
        l = np.char.count(a, sep)+1
        ncols = 2*l.max()-1
        out = np.zeros((N,ncols),dtype=a.dtype)
        for i,(a_i,L) in enumerate(zip(a,l)):
            ss = a_i.split(sep)
            out[i,1:2*L-1:2] = sep
            out[i,:2*L:2] = ss
        return out
    

    【讨论】:

      【解决方案2】:

      我想出了我自己的使用np.char.partition 的递归解决方案。但是,在计时时,它的性能会降低。时间类似于@Divakar 的单次拆分解决方案,但随后会乘以必要的拆分次数。

      def partitions(a, sep):
          if np.any(np.char.count(a, sep) >= 1):
              a2 = np.char.partition(a, sep)
              return np.concatenate([a2[:, 0:2], partitions(a2[:, 2], sep)], axis=1)
          return a.reshape(-1, 1)
      

      【讨论】:

      • 你也可以测试一下我的Approach #2 吗?谢谢!想知道它是否比 #1 好,如果是,那么差多少。
      • @Divakar 的 Approach #2 的执行时间在我的示例向量(大小 3)上约为 Approach #1 的 80% ,但在大小为 300 的向量上约为 200%:a = np.array(['word', 'two', 'and three words'] * 100)。如果拆分次数增加,则趋势相同(4 次拆分而不是 2 次拆分约为 200%)。然而,我的递归 partitions 然后很快就失去了优势,执行时间是 Approach #1 的 20 倍。
      【解决方案3】:

      基于函数的方法很棒,但似乎太复杂了。您可以通过使用数据结构转换和 re.split 在一行代码中来解决这个问题。

      a = np.array(['word', 'two words', 'and three words'])
      
      #Use the re.split to get partitions then transform to dataframe, fillna, transform back!
      
      np.array(pd.DataFrame([re.split('( )', i) for i in a]).fillna(''))
      
      #You can change the '( )' to '(\W)' if you want it to separate on all non-word characters!
      
      array([['word', '', '', '', ''],
             ['two', ' ', 'words', '', ''],
             ['and', ' ', 'three', ' ', 'words']], dtype=object)
      

      【讨论】:

      • 优雅的解决方案:)。时序:结果类似于@Divakar 的 Approach #2 用于大型数组(即比 Approach #1 慢约 2 倍)。然而,对于小型的,创建 DataFrame 的开销很大。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-30
      • 2018-02-27
      • 2013-10-19
      • 2019-08-21
      相关资源
      最近更新 更多