اذهب إلى المحتوى
  • 0

أخذ المصفوفات الفرعية من مصفوفة صغيرة مع خطوة / حجم في numpy

Fahmy Mostafa

السؤال

لدي مصفوفة بسيطة التالي:

x = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])

أريد أن أقوم بتحويلها إلى مصفوفة بحجم 5*3 كالتالي:

numpy.array([[1, 2, 3, 4, 5] ,[4, 5, 6 ,7, 8], [7, 8, 9, 10, 11]])

حاولت أن أستخدم حلقة for للقيام بهذا الأمر لكن الخطوات لم تكن واضحة بالنسبة لي، كيف يمكنني تطبيق هذا الأمر؟

رابط هذا التعليق
شارك على الشبكات الإجتماعية

Recommended Posts

  • 1

لتشكيل مصفوفة جديدة بطريقة تسمح لك بتحديد الخطوة وال shape، تمنحك Numpy طريقتين رئيسيتين لبناء منظار جديد "new view" لمخزن الذاكرة المؤقت "memory buffer" ، وذلك عن طريق تحديد سمات المصفوفة الأساسية مثل الخطوات "strides" بشكل مباشرة، هاتين الطريقتين هما: as_strided و ndarray.
بشكل عام كلاهما يعتبران طرق خطيرة للاستخدام داخل الكود لأنه قد تنتج (((الكثير))) من الأخطاء أثناء استخدامه حتى ولو كنت مبرمجاً محترفاً، لذا لاينصح أبداً بهما إلا للضرورة القصوى. مثلاً في بعض المسائل الصعبة التي قد يسهل حلها عند التلاعب بشكل المصفوفة بهذه الطرق.

قبل البدء ماهي ال strides؟ هو مقدار الخطوة، أو هو  عدد البايتات اللازمة للانتقال إلى العنصر الثاني من المصفوفة.(ويشار له أيضاً بال axis)
طبعاً في حالة المصوفوفة الثنائية نحدد الخطوة الأفقية والخطوة العمودية فمثلاً(16,4) تعني:

array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

# جهازي بخزن كل قيمة ب4 بايت
هذا يعني أنه للانتقال من العنصر 1 إلى 2 نحتاج إلى 4 بايت وللانتقال من بداية البعد الأول [ 0,  1,  2,  3] في المصفوفة  إلى البعد الثاني [ 4,  5,  6,  7] نحتاج إلى  خطوة مقدارها 16 أي 4*4 أي عدد الأعمدة بحجم كل خطوة
  • as_strided :

في اللمثال التالي نقوم بإنشاء view باستخدام هذه الطريقة بشكل مكافئ تماماً ل reshape، ثم سأقوم بإنشاء مايكافئ x.reshape

# أنشأنا عرضًا جديدًا لمخزن الذاكرة 
#x.reshape(3, 4) مايلي يكافئ 
import numpy as np
from numpy.lib.stride_tricks import as_strided
x = np.arange(12) #array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
print(x.strides) # أي نحتاج 8 بايتات للانتقال في الذاكرة من العنصر1 إلى 2
as_strided(x, shape=(3, 4), strides=(16, 4))# 16=x.strides*4
'''array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])'''
#x.reshape(3, 4) مايلي يكافئ 
as_strided(x, shape=(3, 4), strides=(4, 16))
# لاحظ كيف أنه من خلال التلاعب بالخطوات يمكنني الحصول على ما أريد

القدرات والفائدة الأساسية من as_strided  تتجاوز ذلك، فالاستخدام الشائع منها هي ال “sliding window” وهو ما أشرت إليه بسؤالك "إنشاء مصفوفة من x بأبعاد (5,3)" إذاً ماهو ال strides المناسب للقيام بذلك؟
أولاً بالنسبة للأسطر، فالانتقال من عنصر لعنصر يتطلب x.strides أي 4 بايت على جهازي.
ثانياً القفز على الأعمدة ( أي للنزول ) كم نحتاج؟ لو ان القيم في الأعمدة متتالية لكنا نحتاج فقط 4 بايت، مثلاً:

array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6]])
# ولو كان بالشكل التالي سنحتاج 4*2
array([[0, 1, 2, 3, 4],
       [2, 3, 4, 5, 6],
       [4, 5, 6, 7, 8]])
ولو كان بالشكل التالي لحتجنا 4*3
array([[ 0,  1,  2,  3,  4],
       [ 3,  4,  5,  6,  7],
       [ 6,  7,  8,  9, 10]])
#لمعرفتها x.strides حجم الخطوة يختلف من جهاز لآخر اسخدم التعليمة 
x = np.arange(1,12)
as_strided(x, shape=(3, 5), strides=(x.strides*3, x.strides))
"""
array([[ 1,  2,  3,  4,  5],
       [ 4,  5,  6,  7,  8],
       [ 7,  8,  9, 10, 11]])
"""

إن الشيء الرائع فيها هو إنه يمكنك تشكيل مصفوفة أكبر حجماً من المصفوفة x ذاتها بدوم أن يتم استهلاك ذاكرة إضافية، أي أننا هنا احتجنا لتمثيل x في الذاكرة 11*4 =44 bytes ثم شكلنا مصفوفة جديدة منها بأبعاد 4,3 أي قد تظن أن الحجم التخزيني سيكون 4*3*4 لكن أنت مخطئ، سيكون 44 بايت أيضاً!! وذلك لأنها تتشارك البيانات.
الآن يمكنك تشكيل تابع يعطيك الخرج بالحالة العامة، حيث نمرر له المصفوفة والطول والعرض:

def strided_app(a, L, S ):
    nrows = ((a.size-L)//S)+1
    n = a.strides[0]
    x=np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n))
    return x
strided_app(x,5,3)
"""
array([[ 0,  1,  2,  3,  4],
       [ 3,  4,  5,  6,  7],
       [ 6,  7,  8,  9, 10]])
"""
  • ndarray: يمكنك استخدامها بطريقة مشابهة كالتالي:
np.ndarray(buffer=x.data, shape=(3, 5), strides=(12,4), dtype=int)

كما يمكنك استخدام طرق عادية لتشكيل المطلوب باستخدام مفهوم ال broadcasting:

def broadcasting_app(a, L, S ):
    nrows = ((a.size-L)//S)+1
	x=a[S*np.arange(nrows)[:,None] + np.arange(L)]
    return x

 

رابط هذا التعليق
شارك على الشبكات الإجتماعية

  • 0

يمكن استخدام الدالة reshape لإعادة تشكيل المصفوفة:

تحجيم المصفوفة للحجم المطلوب، ثم تغيير الأبعاد

import numpy as np

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
n = 3
m = 5
x = np.resize(x, n*m)
x = x.reshape(n, m)

print(x)

حيث يمثل الوسيط الأول عدد الأسطر و الثاني عدد الأعمدة وهو المطلوب

رابط هذا التعليق
شارك على الشبكات الإجتماعية

  • 0

يمكنك فعل ذلك عن طريق Broadcasting ويمكنك فعل ذلك كالتالي

def broadcasting_app(a, L, S ):
    numOfRows = ((a.size-L)//S)+1
    return a[S*np.arange(numOfRows)[:,None] + np.arange(L)]

حيث ان المعامل a هو المصفوفة التي سوف تمررها والمعامل L هو عدد الأعمدة والمعامل S هو عدد الأسطر

رابط هذا التعليق
شارك على الشبكات الإجتماعية

  • 0

الطريقة الأولى والتي تعتبر أكثر كفائة بإستخدام NumPy strides كالآتي

def strided_app(a, L, S ):  
    nrows = ((a.size-L)//S)+1
    n = a.strides[0]
    return np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n))

والطريقة الأخرى عبر إستخدام broadcasting

def broadcasting_app(a, L, S ): 
    nrows = ((a.size-L)//S)+1
    return a[S*np.arange(nrows)[:,None] + np.arange(L)]

 

رابط هذا التعليق
شارك على الشبكات الإجتماعية

  • 0

ما هو سبب إختيار المصفوفة [4, 5, 6 ,7, 8] ضمن المصفوفات الفرعية؟ العناصر 4و5و7و8 مشتركة بين المصفوفتين الأخريتين،، و لتجد المصفوفات الفرعية المكونة للمصفوفة الرئيسية بإستخدام reshape تحتاج أن يكون عدد الأعمدة * عدد الصفوف = طول المصفوفة و بهذا ستولد المصفوفات المكونة للمصفوفة الرئيسية بدون تكرار في القيم، أما إن كان عدد الأعمدة * عدد الصفوف لا يساوي طول المصفوفة فالدالة ستولد خطأ.

ما يمكنني إقتراحه في هذه الحالة هو إستخدام دالة permutation بتحديد القوائم ذات الطول 5 (عدد الأعمدة).

import itertools
import numpy as np

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
arr = np.array(list(itertools.permutations(set(x),5)))

final = []
semifinal = []
for i in arr:
  semifinal.append(np.array(sorted(i)))

final = np.unique(np.array(semifinal),axis=0)

بعد إيجاد كل التباديل الممكنة، نقوم بترتيب المصفوفات الداخلية ليسهل عملية إختيار المصفوفات غير المكررة بإستخدام الدالة unique بذلك نكون قد حصلنا على جميع المصفوفات ذات الطول 5 و التي يمكن إيجادها من المصفوفة x.

 

 

رابط هذا التعليق
شارك على الشبكات الإجتماعية

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...