Mam listę zawierającą 1 000 000 elementów (liczb) o nazwie x i chciałbym policzyć, ile z nich jest równych lub większych [0.5, 0.55, 0.60, ..., 1]. Czy można to zrobić bez pętli for?

W tej chwili mam następujący kod, który działa dla określonej wartości przedziału [0,5, ... 1], powiedzmy 0,5 i przypisuje go do zmiennej count

count=len([i for i in x if i >= 0.5])

EDYCJA: Zasadniczo to, czego chcę uniknąć, to robienie tego ... jeśli to możliwe?

obs=[]
alpha = [0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.95,1]

for a in alpha:
    count= len([i for i in x if i >= a])
    obs.append(count)

Z góry dziękuję. Najlepiej, Mikael

2
Mikael Olai Milhøj 20 listopad 2019, 21:00
1
Nie sądzę, że możesz to zrobić bez pętli. Nawet jeśli masz tendencję do używania dowolnego pakietu, który robi to za Ciebie, nadal będzie przemierzał listę za kulisami, używając jakiegoś mechanizmu pętli.
 – 
unixia
20 listopad 2019, 21:14
@unixia. Ale czy byłaby to pętla for?
 – 
Mad Physicist
20 listopad 2019, 21:39
Hmm nie mogę o tym powiedzieć.
 – 
unixia
20 listopad 2019, 21:39
Czy Twoja lista jest zawsze posortowana?
 – 
Mad Physicist
20 listopad 2019, 21:40
Czy chcesz histogramu?
 – 
Brad Campbell
20 listopad 2019, 22:49

5 odpowiedzi

Nie sądzę, że jest to możliwe bez pętli, ale możesz posortować tablicę x, a następnie użyć modułu bisect (doc), aby zlokalizować punkt wstawiania (indeks).

Na przykład:

x = [0.341, 0.423, 0.678, 0.999, 0.523, 0.751, 0.7]
    
alpha = [0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.95,1]

x = sorted(x)

import bisect

obs = [len(x) - bisect.bisect_left(x, a) for a in alpha]

print(obs)

Wydrukuje:

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

Uwaga:

sorted() ma złożoność n log(n) i bisect_left() log(n)

1
Community 20 czerwiec 2020, 12:12
Załóżmy, że len(alfa) to m. Oznacza to, że patrzymy na O((n + m) log n). Oryginał OP to O(m*n). Dla m << n wygrywa OP, ale dla m ~= n wygrywasz. To samo dotyczy mojej odpowiedzi, która jest po prostu krótsza z powodu numpy.
 – 
Mad Physicist
20 listopad 2019, 21:58

Możesz użyć indeksowania numpy i boolowskiego:

>>> import numpy as np
>>> a = np.array(list(range(100)))
>>> a[a>=50].size
50
0
Brad Campbell 20 listopad 2019, 21:05
Dzięki za odpowiedź. Ale moje pytanie brzmi, co powinienem zrobić, jeśli chcę znaleźć rozmiar >=50, >=55, ... i tak dalej (gdzie mam 50,55, ... zapisane jako lista)
 – 
Mikael Olai Milhøj
20 listopad 2019, 21:24

Nawet jeśli nie używasz pętli for, używają ich wewnętrzne metody. Ale skutecznie je iteruje.

Możesz użyć poniższej funkcji bez pętli for od końca.

x = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
l = list(filter(lambda _: _ > .5 , x))
print(l)
0
Durai 20 listopad 2019, 21:20
Dziękuję. Nigdy wcześniej nie widziałem _. Co to jest? (przepraszam, nowy w Pythonie, jak już się domyśliłeś!)
 – 
Mikael Olai Milhøj
20 listopad 2019, 21:22
_ to prawidłowa nazwa parametru, ale jest zwykle używana do ignorowanych parametrów, nie jest zalecane, jeśli faktycznie używasz jej wartości (patrz Do czego służy pojedyncza zmienna podkreślenia „_” w Pythonie?).
 – 
jdehesa
20 listopad 2019, 21:35

Na podstawie komentarzy możesz używać numpy, więc użyj np.searchsorted, aby po prostu wstawić alpha do posortowanej wersji x. Indeksy będą twoimi obliczeniami.

Jeśli nie masz nic przeciwko sortowaniu x na miejscu:

x.sort()
counts = x.size - np.searchsorted(x, alpha)

Jeśli nie,

counts = x.size - np.searchsorted(np.sort(x), alpha)

Te liczby zakładają, że chcesz x < alpha. Aby uzyskać <= dodaj słowo kluczowe side='right':

np.searchsorted(x, alpha, side='right')

PS

Jest kilka poważnych problemów z linią

count = len([i for i in x if i >= 0.5])

Przede wszystkim tworzysz listę wszystkich pasujących elementów, zamiast je liczyć. Aby je policzyć,

count = sum(1 for i in x if i >= threshold)

Teraz problem polega na tym, że wykonujesz liniowe przejście przez całą tablicę dla każdej alfy, co nie jest konieczne.

Jak skomentowałem pod odpowiedzią @Andreja Kesely, załóżmy, że mamy N = len(x) i M = len(alpha). Twoja implementacja jest O(M * N) złożona czasowo, a sortowanie daje O((M + N) log N). W przypadku M << N (małego alpha) Twoja złożoność wynosi około O(N), co przewyższa O(N log N). Ale w przypadku M ~= N Twój zbliża się do O(N^2) w porównaniu z moim O(N log N).

0
Mad Physicist 20 listopad 2019, 22:05

EDYCJA: Jeśli używasz już NumPy, możesz po prostu zrobić to:

import numpy as np

# Make random data
np.random.seed(0)
x = np.random.binomial(n=20, p=0.5, size=1000000) / 20
bins = np.arange(0.55, 1.01, 0.05)
# One extra value for the upper bound of last bin
bins = np.append(bins, max(bins.max(), x.max()) + 1)
h, _ = np.histogram(x, bins)
result = np.cumsum(h)
print(result)
# [280645 354806 391658 406410 411048 412152 412356 412377 412378 412378]

Jeśli masz do czynienia z dużymi tablicami liczb, możesz rozważyć użycie NumPy. Ale jeśli używasz prostych list Pythona, możesz to zrobić na przykład tak:

def how_many_bigger(nums, mins):
    # List of counts for each minimum
    counts = [0] * len(mins)
    # For each number
    for n in nums:
        # For each minimum
        for i, m in enumerate(mins):
            # Add 1 to the count if the number is greater than the current minimum
            if n >= m:
                counts[i] += 1
    return counts

# Test
import random
# Make random data
random.seed(0)
nums = [random.random() for _ in range(1_000_000)]
# Make minimums
mins = [i / 100. for i in range(55, 101, 5)]
print(mins)
# [0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0]
count = how_many_bigger(nums, mins)
print(count)
# [449771, 399555, 349543, 299687, 249605, 199774, 149945, 99928, 49670, 0]
0
jdehesa 21 listopad 2019, 12:52
Cześć, mój x jest podany jako x=np.random.binomial(n=20, p=0.5,size=1000000)
 – 
Mikael Olai Milhøj
20 listopad 2019, 21:32
Wartości zwracane przez tę funkcję są prawie zawsze większe niż 1.
 – 
jdehesa
20 listopad 2019, 22:06
Przepraszamy, zapomniałem dołączyć /20
 – 
Mikael Olai Milhøj
20 listopad 2019, 23:20
Zaktualizowano dla tych losowych danych.
 – 
jdehesa
21 listopad 2019, 12:54