Chciałbym losowo usunąć ułamek elementów z listy bez zmiany kolejności listy.

Powiedzmy, że miałem pewne dane i chciałem usunąć 1/4 z nich:

data = [1,2,3,4,5,6,7,8,9,10]
n    = len(data) / 4

Myślę, że potrzebuję pętli, aby przejść przez dane i usunąć losowy element "n" razy? Więc coś takiego:

for i in xrange(n):
    random = np.randint(1,len(data))
    del data[random]

Moje pytanie brzmi: jest to najbardziej "Pythonic" sposób na to? Moja lista będzie długa ~ 5000 elementów i chcę to zrobić wiele razy z różnymi wartościami "n".

Dzięki!

5
rh1990 3 lipiec 2017, 14:00

4 odpowiedzi

Najlepsza odpowiedź

Możesz użyć Random.Sample Lubię to:

import random

a = [1,2,3,4,5,6,7,8,9,10]

no_elements_to_delete = len(a) // 4
no_elements_to_keep = len(a) - no_elements_to_delete
b = set(random.sample(a, no_elements_to_keep))  # the `if i in b` on the next line would benefit from b being a set for large lists
b = [i for i in a if i in b]  # you need this to restore the order
print(len(a))  # 10
print(b)       # [1, 2, 3, 4, 5, 8, 9, 10]
print(len(b))  # 8

Dwa notatki na powyższym.

  1. Nie modyfikujesz oryginalnej listy na miejscu, ale możesz.
  2. W rzeczywistości nie usuwanie elementów, ale raczej utrzymywania elementów, ale to samo (musisz dostosować wskaźniki)
  3. Wadą jest rozumienie listy, które przywraca kolejność elementów

Ponieważ @Koalo mówi w komentarzach, które powyższe nie będą działać prawidłowo, jeśli elementy na liście oryginalnej nie są unikalne . Mógłbym łatwo naprawić to, ale moja odpowiedź byłaby identyczna z tym, który został wysłany przez @ JohnColeman. Więc jeśli to może być tak, użyj jego jego zamiast tego.

4
Ma0 3 lipiec 2017, 11:30

Proponuję używać numpy indeksowania jak w

import numpy as np
data = np.array([1,2,3,4,5,6,7,8,9,10])
n = len(data)/4
indices = sorted(np.random.choice(len(data),len(data)-n,replace=False))
result = data[indices]
0
koalo 3 lipiec 2017, 11:18

Czy porządek jest znaczący? Jeśli nie, możesz zrobić coś takiego:

shuffle(data)
data=data[:len(data)-n]
0
Binyamin Even 3 lipiec 2017, 11:17

Sekwencyjne usunięcie to zły pomysł od momentu usunięcia na liście jest O(n). Zamiast tego zrobić coś takiego:

def delete_rand_items(items,n):
    to_delete = set(random.sample(range(len(items)),n))
    return [x for i,x in enumerate(items) if not i in to_delete]
6
John Coleman 3 lipiec 2017, 11:08