ค้นหาแถวที่ไม่ซ้ำกันใน numpy.array


199

numpy.arrayฉันต้องไปหาแถวที่ไม่ซ้ำกันใน

ตัวอย่างเช่น:

>>> a # I have
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
>>> new_a # I want to get to
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])

ฉันรู้ว่าฉันสามารถสร้างชุดและวนรอบอาร์เรย์ แต่ฉันกำลังมองหาnumpyโซลูชันบริสุทธิ์ที่มีประสิทธิภาพ ฉันเชื่อว่ามีวิธีตั้งค่าชนิดข้อมูลเป็นโมฆะจากนั้นฉันสามารถใช้numpy.uniqueแต่ฉันไม่สามารถหาวิธีทำให้มันใช้งานได้


11
นุ่นมีวิธี dataframe.drop_duplicates () ดูstackoverflow.com/questions/12322779/pandas-unique-dataframeและpandas.pydata.org/pandas-docs/dev/generated/…
codeape

ขอบคุณ แต่ฉันไม่สามารถใช้แพนด้า
Akavall


1
@ Andy Hayden แม้จะมีชื่อ แต่ก็ไม่ได้ซ้ำกับคำถามนี้ ลิงค์ของ codeape นั้นซ้ำกัน
ไหว Yip Tung

5
คุณลักษณะนี้กำลังมาที่ 1.13: github.com/numpy/numpy/pull/7742
Eric

คำตอบ:


115

ตั้งแต่ NumPy 1.13 เราสามารถเลือกแกนเพื่อเลือกค่าเฉพาะในอาร์เรย์ N-dim ใด ๆ ในการรับแถวที่ไม่ซ้ำใครสามารถทำได้:

unique_rows = np.unique(original_array, axis=0)


12
ระวังด้วยฟังก์ชั่นนี้ np.unique(list_cor, axis=0)ทำให้คุณได้รับอาร์เรย์กับแถวที่ซ้ำกันเอาออก ; มันไม่ได้กรองอาร์เรย์องค์ประกอบที่ไม่ซ้ำกันในอาร์เรย์เดิม ดูที่นี่เช่น ..
แบรดซาโลมอน

โปรดทราบว่าหากคุณต้องการแถวที่ไม่ซ้ำกันโดยไม่สนใจลำดับของค่าในแถวคุณสามารถเรียงลำดับอาร์เรย์เดิมในคอลัมน์ได้โดยตรงก่อน:original_array.sort(axis=1)
mececoeur

140

อีกวิธีหนึ่งที่เป็นไปได้

np.vstack({tuple(row) for row in a})

20
+1 นี่ชัดเจนสั้นและไพเราะ การแก้ปัญหาประเภทนี้ควรมีความสำคัญมากกว่าคำตอบที่ซับซ้อนและได้รับคะแนนโหวตสูงกว่าสำหรับคำถามนี้ IMO
Bill Cheatham

3
ยอดเยี่ยม วงเล็บปีกกาหรือฟังก์ชั่น set () ทำเคล็ดลับ
เทียนเหอเขา

2
@Greg von Winckel คุณสามารถแนะนำสิ่งที่ไม่ใช่สิ่งที่ไม่เปลี่ยนคำสั่ง
Laschet Jain

ใช่ แต่ไม่ใช่คำสั่งเดียว: x = []; [x.append (tuple (r)) สำหรับ r ใน a หาก tuple (r) ไม่ได้อยู่ใน x]; a_unique = array (x);
Greg von Winckel

1
หากต้องการหลีกเลี่ยง FutureWarning ให้แปลงชุดเป็นรายการเช่น: np.vstack(list({tuple(row) for row in AIPbiased[i, :, :]})) FutureWarning: อาร์เรย์ไปยังสแต็กจะต้องผ่านเป็นประเภท "ลำดับ" เช่นรายการหรือทูเปิล การสนับสนุน iterables ที่ไม่ต่อเนื่องเช่นเครื่องกำเนิดไฟฟ้าถูกคัดค้าน ณ NumPy 1.16 และจะเพิ่มข้อผิดพลาดในอนาคต
leermeester

111

ตัวเลือกอื่นสำหรับการใช้อาร์เรย์ที่มีโครงสร้างคือการใช้มุมมองของvoidชนิดที่รวมแถวทั้งหมดไว้ในรายการเดียว:

a = np.array([[1, 1, 1, 0, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [1, 1, 1, 0, 0, 0],
              [1, 1, 1, 1, 1, 0]])

b = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
_, idx = np.unique(b, return_index=True)

unique_a = a[idx]

>>> unique_a
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

เพิ่มแก้ไขnp.ascontiguousarrayตามคำแนะนำของ @ seberg วิธีนี้จะทำให้วิธีการทำงานช้าลงหากอาร์เรย์ไม่ได้อยู่ติดกัน

แก้ไข ข้างต้นสามารถเร่งขึ้นเล็กน้อยอาจมีค่าใช้จ่ายของความชัดเจนโดยทำ:

unique_a = np.unique(b).view(a.dtype).reshape(-1, a.shape[1])

นอกจากนี้อย่างน้อยในระบบของฉันประสิทธิภาพก็ควรที่จะเท่าเทียมกันหรือดีกว่าวิธี lexsort:

a = np.random.randint(2, size=(10000, 6))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
100 loops, best of 3: 3.17 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
100 loops, best of 3: 5.93 ms per loop

a = np.random.randint(2, size=(10000, 100))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
10 loops, best of 3: 29.9 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
10 loops, best of 3: 116 ms per loop

3
ขอบคุณมาก. นี่คือคำตอบที่ผมกำลังมองหาคุณสามารถอธิบายสิ่งที่เกิดขึ้นในขั้นตอนนี้b = a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))?
Akavall

3
@Akavall มันสร้างมุมมองข้อมูลของคุณด้วยnp.voidชนิดข้อมูลขนาดจำนวนไบต์ในแถวเต็ม มันคล้ายกันสองสิ่งที่คุณจะได้รับถ้าคุณมีอาร์เรย์np.uint8และดูเป็นnp.uint16s ซึ่งรวมทุกสองคอลัมน์เป็นหนึ่งเดียว แต่มีความยืดหยุ่นมากกว่า
Jaime

3
@ Jaime คุณสามารถเพิ่มnp.ascontiguousarrayหรือคล้ายกันเพื่อให้ปลอดภัยโดยทั่วไป (ฉันรู้ว่ามันจำเป็นต้องเข้มงวดมากขึ้นเล็กน้อย แต่ ... ) แถวจะต้องต่อเนื่องกันเพื่อให้มุมมองทำงานได้ตามที่คาดไว้
seberg

2
@ConstantineEvans มันเป็นการเพิ่มเมื่อเร็ว ๆ นี้: ใน numpy 1.6 พยายามเรียกใช้np.uniqueอาร์เรย์ของการnp.voidส่งคืนข้อผิดพลาดที่เกี่ยวข้องกับการผสานที่ไม่ได้ใช้งานสำหรับประเภทนั้น มันทำงานได้ดีใน 1.7 แม้ว่า
Jaime

9
เป็นที่น่าสังเกตว่าถ้าวิธีนี้ใช้สำหรับตัวเลขจุดลอยตัวจะมีการจับที่-0.จะไม่เปรียบเทียบเท่ากับ+0.ในขณะที่การเปรียบเทียบองค์ประกอบต่อองค์ประกอบจะมี-0.==+0.(ตามที่ระบุโดยมาตรฐานลอย ieee) ดูstackoverflow.com/questions/26782038/…
tom10

29

หากคุณต้องการหลีกเลี่ยงค่าใช้จ่ายหน่วยความจำของการแปลงเป็นชุดของ tuples หรือโครงสร้างข้อมูลอื่นที่คล้ายคลึงกันคุณสามารถใช้ประโยชน์จากอาร์เรย์ที่มีโครงสร้างของ numpy ได้

เคล็ดลับคือการดูอาร์เรย์ดั้งเดิมของคุณเป็นอาร์เรย์ที่มีโครงสร้างโดยที่แต่ละรายการสอดคล้องกับแถวของอาร์เรย์เดิม สิ่งนี้ไม่ได้ทำสำเนาและค่อนข้างมีประสิทธิภาพ

เป็นตัวอย่างรวดเร็ว:

import numpy as np

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])

ncols = data.shape[1]
dtype = data.dtype.descr * ncols
struct = data.view(dtype)

uniq = np.unique(struct)
uniq = uniq.view(data.dtype).reshape(-1, ncols)
print uniq

เพื่อให้เข้าใจถึงสิ่งที่เกิดขึ้นให้ดูที่ผลลัพธ์ของคนกลาง

เมื่อเราดูสิ่งต่าง ๆ เป็นอาร์เรย์ที่มีโครงสร้างองค์ประกอบแต่ละอย่างในอาร์เรย์จะเป็นแถวในอาร์เรย์เดิม (โดยทั่วไปมันเป็นโครงสร้างข้อมูลที่คล้ายกับรายการของ tuples)

In [71]: struct
Out[71]:
array([[(1, 1, 1, 0, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(1, 1, 1, 0, 0, 0)],
       [(1, 1, 1, 1, 1, 0)]],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

In [72]: struct[0]
Out[72]:
array([(1, 1, 1, 0, 0, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

เมื่อเรารันnumpy.uniqueเราจะได้อาร์เรย์ที่มีโครงสร้างกลับมา:

In [73]: np.unique(struct)
Out[73]:
array([(0, 1, 1, 1, 0, 0), (1, 1, 1, 0, 0, 0), (1, 1, 1, 1, 1, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

จากนั้นเราต้องดูเป็นอาร์เรย์ "ปกติ" ( _เก็บผลลัพธ์ของการคำนวณครั้งล่าสุดipythonซึ่งเป็นสาเหตุที่คุณเห็น_.view...):

In [74]: _.view(data.dtype)
Out[74]: array([0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0])

จากนั้นเปลี่ยนรูปร่างกลับไปเป็นอาร์เรย์ 2D ( -1เป็นตัวยึดตำแหน่งที่บอกให้ numpy คำนวณจำนวนแถวที่ถูกต้องให้จำนวนคอลัมน์):

In [75]: _.reshape(-1, ncols)
Out[75]:
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

เห็นได้ชัดว่าถ้าคุณต้องการที่จะกระชับมากขึ้นคุณสามารถเขียนมันเป็น:

import numpy as np

def unique_rows(data):
    uniq = np.unique(data.view(data.dtype.descr * data.shape[1]))
    return uniq.view(data.dtype).reshape(-1, data.shape[1])

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])
print unique_rows(data)

ซึ่งผลลัพธ์ใน:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]

ดูเหมือนว่านี่จะช้ามากเกือบจะช้าเท่ากับการใช้สิ่งอันดับ การเรียงลำดับอาร์เรย์ที่มีโครงสร้างเช่นนี้จะช้า
cge

3
@cge - ลองใช้กับอาร์เรย์ขนาดใหญ่กว่า ใช่การเรียงลำดับอาร์เรย์ numpy ช้ากว่าการเรียงลำดับรายการ ความเร็วไม่ใช่ข้อพิจารณาหลักในกรณีส่วนใหญ่ที่คุณใช้งาน ndarrays มันคือการใช้หน่วยความจำ รายการของสิ่งอันดับจะใช้หน่วยความจำมากกว่าโซลูชันนี้อย่างมากมาย แม้ว่าคุณจะมีหน่วยความจำเพียงพอ แต่ด้วยอาเรย์ที่มีขนาดใหญ่พอสมควรการแปลงเป็นรายการของทูเปิลจะมีค่าใช้จ่ายมากกว่าความได้เปรียบด้านความเร็ว
Joe Kington

@cge - อ่าฉันไม่ได้สังเกตว่าคุณใช้lexsortอยู่ ฉันคิดว่าคุณหมายถึงใช้รายการของสิ่งอันดับ ใช่lexsortอาจเป็นตัวเลือกที่ดีกว่าในกรณีนี้ ฉันลืมมันไปแล้วกระโดดไปที่ทางออกที่ซับซ้อนเกินไป
Joe Kington

20

np.uniqueเมื่อฉันเรียกใช้มันจะnp.random.random(100).reshape(10,10)ส่งคืนองค์ประกอบแต่ละอันที่ไม่ซ้ำใคร แต่คุณต้องการแถวที่ไม่ซ้ำดังนั้นก่อนอื่นคุณต้องใส่มันลงใน tuples:

array = #your numpy array of lists
new_array = [tuple(row) for row in array]
uniques = np.unique(new_array)

นั่นเป็นวิธีเดียวที่ฉันเห็นคุณเปลี่ยนประเภทเพื่อทำสิ่งที่คุณต้องการและฉันไม่แน่ใจว่าการทำซ้ำรายการเพื่อเปลี่ยนเป็น tuples ไม่เป็นไรกับ "ไม่วนซ้ำผ่าน"


5
+1 นี่ชัดเจนสั้นและไพเราะ การแก้ปัญหาประเภทนี้ควรมีความสำคัญมากกว่าคำตอบที่ซับซ้อนและได้รับคะแนนโหวตสูงกว่าสำหรับคำถามนี้ IMO
Bill Cheatham

ฉันชอบสิ่งนี้มากกว่าโซลูชันที่ได้รับการยอมรับ ความเร็วไม่ใช่ปัญหาสำหรับฉันเพราะฉันอาจมีเพียง< 100แถวต่อการเรียกเท่านั้น สิ่งนี้อธิบายได้อย่างชัดเจนถึงประสิทธิภาพการทำงานที่ไม่ซ้ำใครเหนือแถว
rayryeng

4
สิ่งนี้ใช้ไม่ได้กับข้อมูลของฉันuniquesมีองค์ประกอบที่ไม่ซ้ำใคร อาจเป็นไปได้ว่าฉันเข้าใจรูปร่างที่คาดหวังของarray- คุณอาจจะแม่นยำมากขึ้นที่นี่?
FooBar

@ ryan-saxe ฉันชอบที่นี่คือ pythonic แต่นี่ไม่ใช่วิธีที่ดีเพราะแถวที่กลับไปuniquesถูกเรียงลำดับ (และแตกต่างจากแถวในarray) B = np.array([[1,2],[2,1]]); A = np.unique([tuple(row) for row in B]); print(A) = array([[1, 2],[1, 2]])
jmlarson

16

np.unique ทำงานโดยการเรียงลำดับอาร์เรย์ที่แบนแล้วดูว่าแต่ละรายการเท่ากับก่อนหน้านี้หรือไม่ สิ่งนี้สามารถทำได้ด้วยตนเองโดยไม่ต้องแบน:

ind = np.lexsort(a.T)
a[ind[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]]

วิธีนี้ไม่ได้ใช้สิ่งอันดับและควรจะเร็วกว่าและง่ายกว่าวิธีอื่น ๆ

หมายเหตุ: รุ่นก่อนหน้านี้ไม่ได้มีตัวบ่งชี้หลังจาก [ซึ่งหมายความว่ามีการใช้ดัชนีที่ไม่ถูกต้อง นอกจากนี้โจคิงตันยังชี้ให้เห็นอย่างชัดเจนว่าสิ่งนี้จะสร้างสำเนากลางที่หลากหลาย วิธีการต่อไปนี้ทำให้น้อยลงโดยการทำสำเนาเรียงแล้วใช้มุมมองของมัน:

b = a[np.lexsort(a.T)]
b[np.concatenate(([True], np.any(b[1:] != b[:-1],axis=1)))]

นี่คือเร็วกว่าและใช้หน่วยความจำน้อยลง

นอกจากนี้หากคุณต้องการค้นหาแถวที่ไม่ซ้ำกันใน ndarray โดยไม่คำนึงถึงจำนวนมิติในอาร์เรย์สิ่งต่อไปนี้จะทำงาน:

b = a[lexsort(a.reshape((a.shape[0],-1)).T)];
b[np.concatenate(([True], np.any(b[1:]!=b[:-1],axis=tuple(range(1,a.ndim)))))]

ประเด็นที่น่าสนใจที่เหลืออยู่ก็คือถ้าคุณต้องการที่จะจัดเรียง / ไม่ซ้ำกันตามแกนที่กำหนดเองของอาเรย์ที่มีขนาดตามอำเภอใจซึ่งเป็นสิ่งที่ยากขึ้น

แก้ไข:

เพื่อแสดงความแตกต่างของความเร็วฉันใช้การทดสอบสองสามครั้งใน ipython ของสามวิธีที่แตกต่างกันซึ่งอธิบายไว้ในคำตอบ ด้วยความถูกต้องของคุณ a ไม่มีความแตกต่างมากนักแม้ว่าเวอร์ชั่นนี้จะเร็วกว่า:

In [87]: %timeit unique(a.view(dtype)).view('<i8')
10000 loops, best of 3: 48.4 us per loop

In [88]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True], np.any(a[ind[1:]]!= a[ind[:-1]], axis=1)))]
10000 loops, best of 3: 37.6 us per loop

In [89]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10000 loops, best of 3: 41.6 us per loop

ด้วยขนาดที่ใหญ่กว่า a รุ่นนี้จบลงเร็วกว่ามาก:

In [96]: a = np.random.randint(0,2,size=(10000,6))

In [97]: %timeit unique(a.view(dtype)).view('<i8')
10 loops, best of 3: 24.4 ms per loop

In [98]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10 loops, best of 3: 28.2 ms per loop

In [99]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!= a[ind[:-1]],axis=1)))]
100 loops, best of 3: 3.25 ms per loop

ดีมาก! ในบันทึกย่อด้านข้างมันทำสำเนาหลายชุด (เช่นa[ind[1:]]เป็นสำเนา ฯลฯ ) ในทางกลับกันโซลูชันของคุณโดยทั่วไปจะเร็วกว่า 2-3 เท่าจนกว่าจะหมดหน่วยความจำ
Joe Kington

จุดดี. ตามที่ปรากฏความพยายามของฉันที่จะคัดลอกคนกลางโดยใช้เพียงดัชนีทำให้วิธีการของฉันใช้หน่วยความจำมากขึ้นและจบลงช้ากว่าเพียงแค่ทำสำเนาเรียงลำดับของอาร์เรย์เนื่องจาก a_sorted [1:] ไม่ใช่สำเนาของ a_sorted .
cge

คืออะไรdtypeในการกำหนดเวลาของคุณหรือไม่ ฉันคิดว่าคุณเข้าใจผิด ในระบบของฉันเรียกตามที่อธิบายไว้ในคำตอบของฉันคือเร็วขึ้นเล็กน้อยกว่าการใช้อย่างใดอย่างหนึ่งของสองรสชาติของคุณnp.unique np.lexsortและเร็วกว่าประมาณ 5 เท่าถ้าอาร์เรย์เพื่อค้นหาสิ่ง(10000, 100)แปลกใหม่มีรูปร่าง แม้ว่าคุณตัดสินใจที่จะปรับใช้สิ่งที่np.uniqueจะลดเวลาการดำเนินการ (เล็กน้อย) บางส่วนการยุบทุกแถวในวัตถุเดียวจะทำการเปรียบเทียบได้เร็วกว่าการเรียกnp.anyการเปรียบเทียบคอลัมน์โดยเฉพาะอย่างยิ่งสำหรับการนับคอลัมน์ที่สูงขึ้น
Jaime

@cge: คุณอาจหมายถึง 'np.any' แทนมาตรฐาน 'ใด ๆ ' ซึ่งไม่ได้ใช้อาร์กิวเมนต์คำหลัก
M. Toya

@ Jaime - ฉันเชื่อว่าdtypeเป็นเพียงa.dtypeคือชนิดข้อมูลของข้อมูลที่กำลังดูเช่นเดียวกับที่ทำโดย Joe Kington ในคำตอบของเขา หากมีหลายคอลัมน์อีกวิธีหนึ่ง (ไม่สมบูรณ์!) วิธีที่จะทำให้สิ่งต่าง ๆ ใช้งานได้อย่างรวดเร็วlexsortคือการจัดเรียงคอลัมน์เพียงไม่กี่แห่ง นี่เป็นข้อมูลเฉพาะเมื่อต้องการทราบว่าคอลัมน์ใดมีความแปรปรวนเพียงพอในการจัดเรียงอย่างสมบูรณ์ เช่นa.shape = (60000, 500)- การเรียงลำดับใน 3 ind = np.lexsort((a[:, 2], a[:, 1], a[:, 0]))คอลัมน์แรก: ประหยัดเวลาค่อนข้างมาก แต่ข้อจำกัดความรับผิดชอบอีกครั้ง: มันอาจจะไม่จับทุกกรณี - มันขึ้นอยู่กับข้อมูล
n1k31t4


9

ฉันได้เปรียบเทียบทางเลือกที่แนะนำสำหรับความเร็วและพบว่าน่าแปลกใจที่uniqueโซลูชันดูเป็นโมฆะนั้นเร็วกว่าเนทีฟดั้งเดิมuniqueของaxisอาร์กิวเมนต์ด้วย หากคุณกำลังมองหาความเร็วคุณจะต้องการ

numpy.unique(
    a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
    ).view(a.dtype).reshape(-1, a.shape[1])

ป้อนคำอธิบายรูปภาพที่นี่


รหัสในการทำซ้ำพล็อต:

import numpy
import perfplot


def unique_void_view(a):
    return numpy.unique(
        a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
        ).view(a.dtype).reshape(-1, a.shape[1])


def lexsort(a):
    ind = numpy.lexsort(a.T)
    return a[ind[
        numpy.concatenate((
            [True], numpy.any(a[ind[1:]] != a[ind[:-1]], axis=1)
            ))
        ]]


def vstack(a):
    return numpy.vstack({tuple(row) for row in a})


def unique_axis(a):
    return numpy.unique(a, axis=0)


perfplot.show(
    setup=lambda n: numpy.random.randint(2, size=(n, 20)),
    kernels=[unique_void_view, lexsort, vstack, unique_axis],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    xlabel='len(a)',
    equality_check=None
    )

1
คำตอบที่ดีมากจุดหนึ่งเล็ก ๆ น้อย ๆ : vstack_dictไม่เคยใช้ Dict vstatck_setที่วงเล็บปีกกาเป็นความเข้าใจชุดและดังนั้นพฤติกรรมของมันเกือบจะเหมือนกับ เนื่องจากvstack_dictบรรทัดประสิทธิภาพขาดหายไปสำหรับกราฟเทียวมาๆดูเหมือนว่ามันจะถูกปกคลุมด้วยvstack_setกราฟประสิทธิภาพเนื่องจากมันคล้ายกันมาก!
Akavall

ขอบคุณสำหรับการตอบกลับ. ฉันได้ปรับปรุงพล็อตให้รวมหนึ่งvstackตัวแปร
Nico Schlömer

8

ฉันไม่ชอบคำตอบใด ๆ เหล่านี้เพราะไม่มีใครจัดการกับอาร์เรย์จุดลอยในพีชคณิตเชิงเส้นหรือปริภูมิเวกเตอร์ที่ซึ่งสองแถวเป็น“ เท่าเทียมกัน” หมายถึง“ ภายในบาง 𝜀” คำตอบเดียวที่มีเกณฑ์ความอดทนhttps://stackoverflow.com/a/26867764/500207จึงใช้เกณฑ์นี้เป็นทั้งองค์ประกอบที่ชาญฉลาดและทศนิยมความแม่นยำซึ่งใช้งานได้ในบางกรณี แต่ไม่เหมือนในเชิงคณิตศาสตร์ทั่วไป ระยะทางเวกเตอร์ที่แท้จริง

นี่คือรุ่นของฉัน:

from scipy.spatial.distance import squareform, pdist

def uniqueRows(arr, thresh=0.0, metric='euclidean'):
    "Returns subset of rows that are unique, in terms of Euclidean distance"
    distances = squareform(pdist(arr, metric=metric))
    idxset = {tuple(np.nonzero(v)[0]) for v in distances <= thresh}
    return arr[[x[0] for x in idxset]]

# With this, unique columns are super-easy:
def uniqueColumns(arr, *args, **kwargs):
    return uniqueRows(arr.T, *args, **kwargs)

ฟังก์ชั่นโดเมนสาธารณะข้างต้นใช้scipy.spatial.distance.pdistในการค้นหาระยะทางแบบยุคลิด (กำหนดเองได้) ระหว่างแถวแต่ละคู่ จากนั้นจะเปรียบเทียบแต่ละระยะทางกับคนthreshเก่าเพื่อค้นหาแถวที่อยู่ภายในthreshซึ่งกันและกันและส่งกลับเพียงหนึ่งแถวจากแต่ละแถวthreshกลุ่ม

ระยะทางmetricไม่จำเป็นต้องเป็นแบบยุคลิด - pdistสามารถคำนวณระยะทางต่างๆรวมถึงcityblock(แมนฮัตตัน - บรรทัดฐาน) และcosine (มุมระหว่างเวกเตอร์)

หากthresh=0(ค่าเริ่มต้น) แถวนั้นจะต้องเป็นบิตแน่นอนเพื่อให้ถือว่า "ไม่ซ้ำกัน" ค่าที่ดีอื่น ๆ สำหรับการthreshใช้งานขนาดความแม่นยำของเครื่องจักรเช่นthresh=np.spacing(1)*1e3.


คำตอบที่ดีที่สุด ขอบคุณ มันเป็นคำตอบทั่วไปที่ถูกเขียนมากที่สุด (ทางคณิตศาสตร์) มันพิจารณาเมทริกซ์เป็นชุดของจุดข้อมูลหรือตัวอย่างในช่องว่าง N- มิติและค้นหาคอลเลกชันของจุดที่เหมือนกันหรือคล้ายกัน จุดเหล่านี้สามารถซ้อนทับจุดข้อมูลหรือพื้นที่ใกล้เคียง ในตอนท้ายชุดของจุดเดียวกันหรือคล้ายกันจะถูกแทนที่ด้วยจุดใด ๆ (ในคำตอบข้างต้นโดยจุดแรก) เป็นของชุดเดียวกัน สิ่งนี้ช่วยลดความซ้ำซ้อนจาก cloud point
Sanchit

@Sanchit aha เป็นจุดที่ดีแทนที่จะเลือกจุด“ แรก” (จริงๆแล้วมันอาจจะสุ่มได้อย่างมีประสิทธิภาพเนื่องจากมันขึ้นอยู่กับวิธีที่ Python เก็บคะแนนใน a set) ในฐานะตัวแทนของแต่ละthreshพื้นที่ใกล้เคียงขนาดฟังก์ชั่นอาจช่วยให้ ผู้ใช้เพื่อระบุวิธีเลือกจุดนั้นเช่นใช้ "ค่ามัธยฐาน" หรือจุดที่ใกล้เคียงกับเซนทรอยด์มากที่สุด
Ahmed Fasih

แน่ใจ ไม่ต้องสงสัยเลย ฉันเพิ่งพูดถึงประเด็นแรกเนื่องจากนี่คือสิ่งที่โปรแกรมของคุณกำลังทำอยู่ซึ่งสมบูรณ์ดี
Sanchit

เพียงแค่การแก้ไขฉันผิดที่กล่าวข้างต้นว่าแถวที่จะหยิบสำหรับแต่ละthresh-cluster setจะสุ่มเพราะธรรมชาติไม่เรียงลำดับของ แน่นอนว่ามันเป็นความคิดสร้างสรรค์ในส่วนของฉันsetร้านค้า tuples ของดัชนีที่อยู่ในพื้นที่threshใกล้เคียงดังนั้นสิ่งนี้findRows จะกลับมาจริงสำหรับแต่ละthreshกลุ่มในแถวแรก
Ahmed Fasih

3

ทำไมไม่ใช้drop_duplicatesจากแพนด้า:

>>> timeit pd.DataFrame(image.reshape(-1,3)).drop_duplicates().values
1 loops, best of 3: 3.08 s per loop

>>> timeit np.vstack({tuple(r) for r in image.reshape(-1,3)})
1 loops, best of 3: 51 s per loop

ฉันรักคำตอบนี้จริงๆ แน่นอนว่ามันไม่ได้ใช้ numpy โดยตรง แต่สำหรับฉันมันเป็นสิ่งที่ง่ายที่สุดที่จะเข้าใจในขณะที่กำลังรวดเร็ว
noctilux

3

numpy_indexedแพคเกจ (Disclaimer: ผมผู้เขียน) wraps วิธีการโพสต์โดยไจในที่ดีและอินเตอร์เฟซที่ผ่านการทดสอบพร้อมคุณสมบัติอื่น ๆ อีกมากมาย:

import numpy_indexed as npi
new_a = npi.unique(a)  # unique elements over axis=0 (rows) by default

1

np.unique ทำงานให้กับรายการของ tuples:

>>> np.unique([(1, 1), (2, 2), (3, 3), (4, 4), (2, 2)])
Out[9]: 
array([[1, 1],
       [2, 2],
       [3, 3],
       [4, 4]])

ด้วยรายการของรายการมันจะเพิ่ม TypeError: unhashable type: 'list'


ดูเหมือนจะไม่ทำงานกับฉัน แต่ละ tuple เป็นสองสตริงแทนตัวเลขลอยสองตัว
mjp

ใช้งานไม่ได้ แต่จะส่งคืนรายการองค์ประกอบที่ไม่ใช่สิ่งอันดับ
Mohanad Kaleia

1

จากคำตอบในหน้านี้ฉันได้เขียนฟังก์ชั่นที่จำลองความสามารถของunique(input,'rows')ฟังก์ชั่นMATLAB พร้อมคุณสมบัติเพิ่มเติมเพื่อยอมรับความอดทนสำหรับการตรวจสอบความเป็นเอกลักษณ์ นอกจากนี้ยังมีผลตอบแทนดัชนีดังกล่าวว่าและc = data[ia,:] data = c[ic,:]กรุณารายงานว่าคุณเห็นความแตกต่างหรือข้อผิดพลาดใด ๆ

def unique_rows(data, prec=5):
    import numpy as np
    d_r = np.fix(data * 10 ** prec) / 10 ** prec + 0.0
    b = np.ascontiguousarray(d_r).view(np.dtype((np.void, d_r.dtype.itemsize * d_r.shape[1])))
    _, ia = np.unique(b, return_index=True)
    _, ic = np.unique(b, return_inverse=True)
    return np.unique(b).view(d_r.dtype).reshape(-1, d_r.shape[1]), ia, ic

1

นอกเหนือจาก @Jaime คำตอบที่ดีเยี่ยมวิธีการยุบแถวหนึ่งคือการใช้a.strides[0](สมมติว่าaเป็น C-ต่อเนื่องกัน) a.dtype.itemsize*a.shape[0]ซึ่งเท่ากับ นอกจากนี้void(n)dtype((void,n))เป็นทางลัดสำหรับ ในที่สุดเราก็มาถึงเวอร์ชั่นสั้นที่สุดนี้:

a[unique(a.view(void(a.strides[0])),1)[1]]

สำหรับ

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]

0

เพื่อวัตถุประสงค์ทั่วไปเช่น 3D หรืออาร์เรย์หลายมิติซ้อนกันที่สูงขึ้นลองสิ่งนี้

import numpy as np

def unique_nested_arrays(ar):
    origin_shape = ar.shape
    origin_dtype = ar.dtype
    ar = ar.reshape(origin_shape[0], np.prod(origin_shape[1:]))
    ar = np.ascontiguousarray(ar)
    unique_ar = np.unique(ar.view([('', origin_dtype)]*np.prod(origin_shape[1:])))
    return unique_ar.view(origin_dtype).reshape((unique_ar.shape[0], ) + origin_shape[1:])

ซึ่งเป็นไปตามชุดข้อมูล 2D ของคุณ:

a = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
unique_nested_arrays(a)

ให้:

array([[0, 1, 1, 1, 0, 0],
   [1, 1, 1, 0, 0, 0],
   [1, 1, 1, 1, 1, 0]])

แต่ยังมีอาร์เรย์ 3 มิติเช่น:

b = np.array([[[1, 1, 1], [0, 1, 1]],
              [[0, 1, 1], [1, 1, 1]],
              [[1, 1, 1], [0, 1, 1]],
              [[1, 1, 1], [1, 1, 1]]])
unique_nested_arrays(b)

ให้:

array([[[0, 1, 1], [1, 1, 1]],
   [[1, 1, 1], [0, 1, 1]],
   [[1, 1, 1], [1, 1, 1]]])

การใช้unique return_indexas Jaime ควรทำให้returnบรรทัดสุดท้ายนั้นเรียบง่ายขึ้น เพียงจัดทำดัชนีเดิมarบนแกนด้านขวา
hpaulj

0

คำตอบเหล่านี้ไม่เหมาะกับฉัน ฉันสมมติว่าแถวที่ไม่ซ้ำกันของฉันมีสตริงและไม่ใช่ตัวเลข อย่างไรก็ตามคำตอบจากกระทู้อื่นนี้ใช้งานได้:

ที่มา: https://stackoverflow.com/a/38461043/5402386

คุณสามารถใช้วิธีการรายการ. count () และ. ดัชนี ()

coor = np.array([[10, 10], [12, 9], [10, 5], [12, 9]])
coor_tuple = [tuple(x) for x in coor]
unique_coor = sorted(set(coor_tuple), key=lambda x: coor_tuple.index(x))
unique_count = [coor_tuple.count(x) for x in unique_coor]
unique_index = [coor_tuple.index(x) for x in unique_coor]

0

เราสามารถเปลี่ยนอาร์เรย์ numpy ที่เป็นตัวเลข mxn ให้เป็น mx 1 numpy string array ได้โปรดลองใช้ฟังก์ชั่นต่อไปนี้มันมีcount , inverse_idxและอื่น ๆ เช่น numpy.unique:

import numpy as np

def uniqueRow(a):
    #This function turn m x n numpy array into m x 1 numpy array storing 
    #string, and so the np.unique can be used

    #Input: an m x n numpy array (a)
    #Output unique m' x n numpy array (unique), inverse_indx, and counts 

    s = np.chararray((a.shape[0],1))
    s[:] = '-'

    b = (a).astype(np.str)

    s2 = np.expand_dims(b[:,0],axis=1) + s + np.expand_dims(b[:,1],axis=1)

    n = a.shape[1] - 2    

    for i in range(0,n):
         s2 = s2 + s + np.expand_dims(b[:,i+2],axis=1)

    s3, idx, inv_, c = np.unique(s2,return_index = True,  return_inverse = True, return_counts = True)

    return a[idx], inv_, c

ตัวอย่าง:

A = np.array([[ 3.17   9.502  3.291],
  [ 9.984  2.773  6.852],
  [ 1.172  8.885  4.258],
  [ 9.73   7.518  3.227],
  [ 8.113  9.563  9.117],
  [ 9.984  2.773  6.852],
  [ 9.73   7.518  3.227]])

B, inv_, c = uniqueRow(A)

Results:

B:
[[ 1.172  8.885  4.258]
[ 3.17   9.502  3.291]
[ 8.113  9.563  9.117]
[ 9.73   7.518  3.227]
[ 9.984  2.773  6.852]]

inv_:
[3 4 1 0 2 4 0]

c:
[2 1 1 1 2]

-1

ให้รับเมทริกซ์ numpy ทั้งหมดเป็นรายการจากนั้นลบรายการที่ซ้ำกันออกจากรายการนี้และในที่สุดก็คืนค่ารายการที่ไม่ซ้ำกันของเรากลับสู่เมทริกซ์ numpy:

matrix_as_list=data.tolist() 
matrix_as_list:
[[1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 0, 0], [1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 0]]

uniq_list=list()
uniq_list.append(matrix_as_list[0])

[uniq_list.append(item) for item in matrix_as_list if item not in uniq_list]

unique_matrix=np.array(uniq_list)
unique_matrix:
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])

-3

ทางออกที่ตรงไปตรงมาที่สุดคือการทำให้แถวเป็นรายการเดียวโดยทำให้เป็นสตริง แต่ละแถวนั้นสามารถเปรียบเทียบได้โดยรวมสำหรับความเป็นเอกลักษณ์โดยใช้ numpy วิธีการแก้ปัญหานี้เป็นแบบทั่วไปที่คุณเพียงแค่ต้องก่อร่างใหม่และโยกย้ายอาร์เรย์ของคุณสำหรับการรวมกันอื่น ๆ นี่คือวิธีแก้ไขปัญหาที่มีให้

import numpy as np

original = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

uniques, index = np.unique([str(i) for i in original], return_index=True)
cleaned = original[index]
print(cleaned)    

จะให้:

 array([[0, 1, 1, 1, 0, 0],
        [1, 1, 1, 0, 0, 0],
        [1, 1, 1, 1, 1, 0]])

ส่งรางวัลโนเบลของฉันทางไปรษณีย์


ไม่มีประสิทธิภาพและเกิดข้อผิดพลาดอย่างเช่นตัวเลือกการพิมพ์ที่แตกต่างกัน ตัวเลือกอื่น ๆ เป็นที่ต้องการอย่างชัดเจน
Michael

-3
import numpy as np
original = np.array([[1, 1, 1, 0, 0, 0],
                     [0, 1, 1, 1, 0, 0],
                     [0, 1, 1, 1, 0, 0],
                     [1, 1, 1, 0, 0, 0],
                     [1, 1, 1, 1, 1, 0]])
# create a view that the subarray as tuple and return unique indeies.
_, unique_index = np.unique(original.view(original.dtype.descr * original.shape[1]),
                            return_index=True)
# get unique set
print(original[unique_index])
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.