python - numpy 1D অ্যারে: মাস্ক উপাদানগুলি যে n বারের বেশি পুনরাবৃত্তি করে




arrays binning (6)

মত পূর্ণসংখ্যার একটি অ্যারের দেওয়া

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

আমার কাছে এমন উপাদানগুলি মুখোশ করা দরকার যা N বারের চেয়ে বেশি পুনরাবৃত্তি করে। স্পষ্ট করার জন্য: প্রাথমিক লক্ষ্য হ'ল বুলিয়ান মাস্ক অ্যারেটি পুনরুদ্ধার করা, এটি পরে বিন্নের গণনার জন্য ব্যবহার করা।

আমি বরং একটি জটিল সমাধান নিয়ে এসেছি

import numpy as np

bins = np.array([1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5])

N = 3
splits = np.split(bins, np.where(np.diff(bins) != 0)[0]+1)
mask = []
for s in splits:
    if s.shape[0] <= N:
        mask.append(np.ones(s.shape[0]).astype(np.bool_))
    else:
        mask.append(np.append(np.ones(N), np.zeros(s.shape[0]-N)).astype(np.bool_)) 

mask = np.concatenate(mask)

দান যেমন

bins[mask]
Out[90]: array([1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5])

এটি করার কোন সুন্দর উপায় আছে?

সম্পাদনা, # 2

উত্তরের জন্য অনেক ধন্যবাদ! এখানে এমসিফার্টের বেঞ্চমার্ক প্লটের একটি স্লিম সংস্করণ। আমাকে simple_benchmark নির্দেশ করার জন্য ধন্যবাদ। কেবলমাত্র 4 টি দ্রুত বিকল্প দেখানো হচ্ছে:

উপসংহার

ফ্লোরিয়ান এইচ প্রস্তাবিত, পল পাঞ্জার দ্বারা সংশোধিত এই সমস্যাটি সমাধানের এক দুর্দান্ত উপায় বলে মনে হচ্ছে এটি একদম সোজাসুজি এবং numpy । তবে আপনি যদি numba ব্যবহার করে ভাল numba তবে এমসিফের্টের সমাধানটি অন্যটিকে ছাড়িয়ে যায়।

আমি এমসিফার্টের উত্তরটিকে আরও সাধারণ উত্তর হিসাবে সমাধান হিসাবে গ্রহণ করতে বেছে নিয়েছি: এটি ক্রমাগত পুনরাবৃত্তিকারী উপাদানগুলির (অ-অনন্য) ব্লকের সাথে স্বেচ্ছাসেবী অ্যারেগুলি সঠিকভাবে পরিচালনা করে। numba যদি না যায় তবে numba উত্তরও এক নজর দেখার মতো!


সমাধান

আপনি numpy.unique ব্যবহার করতে পারে। অ্যারে bins থেকে ট্র্যাজেট উপাদানগুলি নিষ্কাশনের জন্য ভেরিয়েবল final_mask ব্যবহার করা যেতে পারে।

import numpy as np

bins = np.array([1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5])
repeat_max = 3

unique, counts = np.unique(bins, return_counts=True)
mod_counts = np.array([x if x<=repeat_max else repeat_max for x in counts])
mask = np.arange(bins.size)
#final_values = np.hstack([bins[bins==value][:count] for value, count in zip(unique, mod_counts)])
final_mask = np.hstack([mask[bins==value][:count] for value, count in zip(unique, mod_counts)])
bins[final_mask]

আউটপুট :

array([1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5])

অনেক সুন্দর উপায় numpy unique() -ফাংশন ব্যবহার করা। আপনি আপনার অ্যারেতে অনন্য এন্ট্রি পাবেন এবং কত ঘন ঘন তারা উপস্থিত হয় তা গণনা:

bins = np.array([1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5])
N = 3

unique, index,count = np.unique(bins, return_index=True, return_counts=True)
mask = np.full(bins.shape, True, dtype=bool)
for i,c in zip(index,count):
    if c>N:
        mask[i+N:i+c] = False

bins[mask]

আউটপুট:

array([1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5])

আপনি ইনডেক্সিং দিয়ে এটি করতে পারেন। যে কোনও এন এর জন্য কোডটি হ'ল:

N = 3
bins = np.array([1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5,6])

mask = [True for _ in range(N)] + list(bins[:-N] != bins[N:])
bins[mask]

আউটপুট:

array([1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6]

আপনি কিছুক্ষণ লুপ ব্যবহার করতে পারেন যা অ্যারে এলিমেন্টের অবস্থান N অবস্থানগুলি বর্তমানের সমান কিনা তা পরীক্ষা করে। এই দ্রষ্টব্যটি নোটটি অ্যারের অর্ডার করা হয়েছে বলে ধরে নিন

import numpy as np

bins = [1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5]
N = 3
counter = N

while counter < len(bins):
    drop_condition = (bins[counter] == bins[counter - N])
    if drop_condition:
        bins = np.delete(bins, counter)
    else:
        # move on to next element
        counter += 1

দাবি অস্বীকার: এটি @ ফ্লোরিয়ানএইচ এর ধারণার কেবলমাত্র কার্যকর প্রয়োগ:

def f(a,N):
    mask = np.empty(a.size,bool)
    mask[:N] = True
    np.not_equal(a[N:],a[:-N],out=mask[N:])
    return mask

বৃহত্তর অ্যারেগুলির জন্য এটি একটি বিশাল পার্থক্য করে:

a = np.arange(1000).repeat(np.random.randint(0,10,1000))
N = 3

print(timeit(lambda:f(a,N),number=1000)*1000,"us")
# 5.443050000394578 us

# compare to
print(timeit(lambda:[True for _ in range(N)] + list(bins[:-N] != bins[N:]),number=1000)*1000,"us")
# 76.18969900067896 us

পদ্ধতির # 1: এখানে একটি ভেক্টরাইজড উপায় -

from scipy.ndimage.morphology import binary_dilation

def keep_N_per_group(a, N):
    k = np.ones(N,dtype=bool)
    m = np.r_[True,a[:-1]!=a[1:]]
    return a[binary_dilation(m,k,origin=-(N//2))]

নমুনা রান -

In [42]: a
Out[42]: array([1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5])

In [43]: keep_N_per_group(a, N=3)
Out[43]: array([1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5])

পদ্ধতির # 2: আরও কিছুটা কমপ্যাক্ট সংস্করণ -

def keep_N_per_group_v2(a, N):
    k = np.ones(N,dtype=bool)
    return a[binary_dilation(np.ediff1d(a,to_begin=a[0])!=0,k,origin=-(N//2))]

পদ্ধতির # 3: গোষ্ঠীভুক্ত গণনা এবং np.repeat (যদিও আমাদের মুখোশটি দেবে না) -

def keep_N_per_group_v3(a, N):
    m = np.r_[True,a[:-1]!=a[1:],True]
    idx = np.flatnonzero(m)
    c = np.diff(idx)
    return np.repeat(a[idx[:-1]],np.minimum(c,N))

পদ্ধতির # 4: একটি view-based পদ্ধতি সহ -

from skimage.util import view_as_windows

def keep_N_per_group_v4(a, N):
    m = np.r_[True,a[:-1]!=a[1:]]
    w = view_as_windows(m,N)
    idx = np.flatnonzero(m)
    v = idx<len(w)
    w[idx[v]] = 1
    if v.all()==0:
        m[idx[v.argmin()]:] = 1
    return a[m]

পদ্ধতির # 5: flatnonzero থেকে flatnonzero ছাড়াই একটি view-based পদ্ধতি flatnonzero -

def keep_N_per_group_v5(a, N):
    m = np.r_[True,a[:-1]!=a[1:]]
    w = view_as_windows(m,N)
    last_idx = len(a)-m[::-1].argmax()-1
    w[m[:-N+1]] = 1
    m[last_idx:last_idx+N] = 1
    return a[m]






binning