Mam ramkę danych z każdym wierszem o wartości listy.
id list_of_value
0 ['a','b','c']
1 ['d','b','c']
2 ['a','b','c']
3 ['a','b','c']
muszę obliczyć wynik dla jednego wiersza i dla wszystkich innych wierszy
Na przykład:
Step 1: Take value of id 0: ['a','b','c'],
Step 2: find the intersection between id 0 and id 1 ,
resultant = ['b','c']
Step 3: Score Calculation => resultant.size / id.size
powtórz krok 2,3 między id 0 i id 1,2,3, podobnie dla wszystkich id.
i utwórz ramkę danych N x N; tak jak to:
- 0 1 2 3
0 1 0.6 1 1
1 1 1 1 1
2 1 1 1 1
3 1 1 1 1
W tej chwili mój kod ma tylko jeden dla pętli:
def scoreCalc(x,queryTData):
#mathematical calculation
commonTData = np.intersect1d(np.array(x),queryTData)
return commonTData.size/queryTData.size
ids = list(df['feed_id'])
dfSim = pd.DataFrame()
for indexQFID in range(len(ids)):
queryTData = np.array(df.loc[df['id'] == ids[indexQFID]]['list_of_value'].values.tolist())
dfSim[segmentDfFeedIds[indexQFID]] = segmentDf['list_of_value'].apply(scoreCalc,args=(queryTData,))
Czy jest na to lepszy sposób? czy mogę po prostu napisać jedną funkcję Apply zamiast wykonywać iterację dla pętli. czy mogę to zrobić szybciej?
list_of_value
?list_of_value
. Mam na myśli w sumie we wszystkich rzędach.Odpowiedzi:
Jeśli dane nie są zbyt duże, możesz użyć
get_dummies
do zakodowania wartości i pomnożenia macierzy:Wynik:
Aktualizacja : Oto krótkie wyjaśnienie kodu. Główną ideą jest przekształcenie podanych list w kodowanie „na gorąco”:
Kiedy już to uzyskamy, rozmiar przecięcia dwóch rzędów, powiedzmy,
0
i1
jest po prostu ich iloczynem kropkowym, ponieważ znak należy do obu wierszy wtedy i tylko wtedy, gdy jest reprezentowany przez1
oba.Mając to na uwadze, pierwsze użycie
aby przekształcić każdą komórkę w serię i połączyć wszystkie te serie. Wynik:
Teraz używamy
pd.get_dummies
tej serii, aby przekształcić ją w ramkę danych zakodowaną na gorąco:Jak widać, każda wartość ma własny wiersz. Ponieważ chcemy połączyć te należące do tego samego oryginalnego wiersza do jednego wiersza, możemy po prostu zsumować je według oryginalnego indeksu. A zatem
daje kod danych binarnych, który chcemy. Następna linia
jest tak jak twoja logika:
s.dot(s.T)
oblicza iloczyn kropkowy według wierszy, a następnie.div(s.sum(1))
dzieli liczby przez wiersze.źródło
12k x 12k
ramką danych. Powinno być dobrze, jeśli masz około kilkuset unikalnych wartości.Spróbuj tego
Wynik
Możesz to również zrobić w następujący sposób
źródło
Użyj funkcji zagnieżdżania listy na liście zestawów
s_list
. W ramach zrozumienia listy użyjintersection
operacji, aby sprawdzić nakładanie się i uzyskać długość każdego wyniku. Na koniec skonstruuj ramkę danych i podziel ją przez długość każdej listydf.list_of_value
Jeśli na każdej liście znajdują się zduplikowane wartości, należy użyć
collections.Counter
zamiastset
. Zmieniłem przykładowe dane id = 0 na['a','a','c']
i id = 1 na['d','b','a']
źródło
Zaktualizowano
Ponieważ proponowanych jest wiele kandydujących rozwiązań, dobrym pomysłem wydaje się analiza czasowa. Wygenerowałem kilka losowych danych z 12 tys. Wierszy zgodnie z żądaniem OP, zachowując 3 elementy na zestaw, ale zwiększając rozmiar alfabetu dostępnego do zapełniania zbiorów. Można to dostosować do rzeczywistych danych.
Daj mi znać, jeśli masz rozwiązanie, które chcesz przetestować lub zaktualizować.
Ustawiać
Aktualny zwycięzca
Zawodnicy
Oryginalny post ze szczegółami rozwiązania
Można to zrobić
pandas
za pomocą samozłączenia.Jak wskazały inne odpowiedzi, pierwszym krokiem jest rozpakowanie danych w dłuższą formę.
Z tej tabeli można obliczyć liczbę poszczególnych identyfikatorów.
A potem następuje samozłączenie, które dzieje się w
value
kolumnie. To paruje identyfikatory raz dla każdej przecinającej się wartości, więc sparowane identyfikatory można policzyć, aby uzyskać rozmiary przecięcia.Te dwa elementy można następnie połączyć i obliczyć wynik.
Jeśli wolisz formę macierzy, jest to możliwe dzięki
pivot
. Będzie to o wiele większa reprezentacja, jeśli dane będą rzadkie.źródło
Takie rozwiązanie będzie działać skutecznie z dowolnego rozmiaru danych i jakiejkolwiek wartości w swojej
list
powiedzieć, jegostr
lubint
lub w inny sposób, a także dbanie o powtarzających się wartości, jeśli takie istnieją.W tym przypadku zrozumienie listy działa lepiej, ponieważ nie trzeba ładować atrybutu dołączania listy i wywoływać go jako funkcję przy każdej iteracji. Innymi słowy, zrozumienie listy działa szybciej, ponieważ zawieszanie i wznawianie ramki funkcji lub wielu funkcji w innych przypadkach jest wolniejsze niż tworzenie listy na żądanie.
Używanie rozumienia listy zamiast pętli, która nie tworzy listy, nonsensowne gromadzenie listy nic nie znaczących wartości, a następnie wyrzucanie listy, jest często wolniejsze z powodu narzutu związanego z tworzeniem i rozszerzaniem listy.
Wynik:
Czas egzekucji:
źródło
Możesz przekonwertować listę na zestaw i użyć funkcji przecięcia, aby sprawdzić nakładanie się:
(użyto tylko 1 funkcji zastosuj zgodnie z zapytaniem :-))
źródło
Użyłbym,
product
aby uzyskać wszystkie kombinacje. Następnie możemy sprawdzić za pomocąnumpy.isin
inumpy.mean
:Próbka czasu
źródło
Powinny być szybkie, weź również pod uwagę duplikat na liście
źródło
Tak! Szukamy tutaj produktu kartezjańskiego, który jest podany w tej odpowiedzi. Można to osiągnąć bez pętli for lub zrozumienia listy
Dodajmy nową powtarzaną wartość do naszej ramki danych,
df
aby wyglądała następująco:Następnie scal się ze sobą
Tak wygląda scalona ramka:
Następnie stosujemy pożądaną funkcję do każdego wiersza za pomocą
axis=1
Przekształcanie tego, aby uzyskać wartości w żądanym formacie
Mam nadzieję że to pomoże :)
źródło