numpy:检查一维数组是否为另一个的子数组

时间:2019-07-12 09:30:18

标签: python numpy

给定两个通用的numpy 1-d数组(无论如何都不保证值),我需要检查一个数组是否为另一个的子数组。

通过强制转换为字符串很容易且很短,但可能不是最有效的:

import numpy as np

def is_sub_arr(a1, a2):
    return str(a2).strip('[]') in str(a1).strip('[]')

arr1 = np.array([9, 1, 3, 2, 7, 2, 7, 2, 8, 5])
arr2 = np.array([3, 2, 7, 2])
arr3 = np.array([1,3,7])

print(is_sub_arr(arr1,arr2))  # True
print(is_sub_arr(arr1,arr3))  # False

是否有一种有效的内置/本机numpy方法来做到这一点?

3 个答案:

答案 0 :(得分:3)

编辑:您还可以使用Numba以更少的内存使处理速度更快(例如1000倍):

import numpy as np
import numba as nb

def is_sub_arr_np(a1, a2):
    l1, = a1.shape
    s1, = a1.strides
    l2, = a2.shape
    a1_win = np.lib.stride_tricks.as_strided(a1, (l1 - l2 + 1, l2), (s1, s1))
    return np.any(np.all(a1_win == a2, axis=1))

@nb.jit(parallel=True)
def is_sub_arr_nb(a1, a2):
    for i in nb.prange(len(a1) - len(a2) + 1):
        for j in range(len(a2)):
            if a1[i + j] != a2[j]:
                break
        else:
            return True
    return False

# Test
np.random.seed(0)
arr1 = np.random.randint(100, size=100_000)
arr2 = np.random.randint(100, size=1_000)
print(is_sub_arr_np(arr1, arr2))
# False
print(is_sub_arr_nb(arr1, arr2))
# False

# Now enforce a match at the end
arr1[-len(arr2):] = arr2
print(is_sub_arr_np(arr1, arr2))
# True
print(is_sub_arr_nb(arr1, arr2))
# True

# Timing
%timeit is_sub_arr_np(arr1, arr2)
# 99.4 ms ± 567 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit is_sub_arr_nb(arr1, arr2)
# 124 µs ± 863 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

不确定这是最有效的答案,但这是一种可能的解决方案:

import numpy as np

def is_sub_arr(a1, a2):
    l1, = a1.shape
    s1, = a1.strides
    l2, = a2.shape
    a1_win = np.lib.stride_tricks.as_strided(a1, (l1 - l2 + 1, l2), (s1, s1))
    return np.any(np.all(a1_win == a2, axis=1))

arr1 = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])
arr2 = np.array([4, 5, 6])
arr3 = np.array([4, 5, 7])
print(is_sub_arr(arr1, arr2))
# True
print(is_sub_arr(arr1, arr3))
# False

答案 1 :(得分:2)

尽管已经有一个可以接受的答案,但我想提出我的快速而肮脏的解决方案:

const bcrypt = require('bcryptjs');

    userSchema.pre('save', async function (next) {
      console.log('just before saving')

      const rounds = 10; // What you want number for round paasword

      const hash = await bcrypt.hash(this.password, rounds);
      this.password = hash;
      next()
    })

这当然仅在数组使用连续内存的情况下有效,因此完整功能为:

memoryview(arr2).tobytes() in memoryview(arr1).tobytes()

这比OP中短而简单的字符串转换要快得多,也比不同阵列大小的(非数字加速)stridecut解决方案要快:

def is_sub_arr_mem(a, sub):
    if a.flags['FORC'] and sub.flags['FORC']: 
        return memoryview(sub).tobytes() in memoryview(a).tobytes()
    return None

(n1和n2分别是arr1和arr2的大小)

答案 2 :(得分:1)

您可以尝试以下操作:

import numpy as np

arr1 = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 4, 5, 6, 20, 67, -1])
arr2 = np.array([4, 5, 6])
arr3 = np.array([4, 5, 7])


def is_sub_arr(original, sub):
    first_match = np.where(original == sub[0])
    if len(first_match) == 0:
        print("no matches")
        return

    else:
        for match in first_match[0]:

            cut_original = original[match:match + len(sub)]
            if np.all(cut_original == sub):
                print("Got a match at index ", match)
            else:
                print("no match")
        return


is_sub_arr(arr1, arr2)
is_sub_arr(arr1, arr3)

首先,我们检查子数组的第一个元素是否在原始数组中,并使用np.where获取其索引。然后,对于每次匹配,我们都将剪切从该索引处开始到该索引处结束的原始数组,再加上子数组的长度,并检查是否与子数组本身匹配。