Strona główna Polish Python Coders Group
   Strona główna   Pomoc Zaloguj się Rejestracja  
Witamy, Gość. Zaloguj się lub zarejestruj.
Czy dotarł do Ciebie email aktywacyjny?

Zaloguj się podając nazwę użytkownika, hasło i długość sesji

Aktualności:
Szukaj Szukaj
Strony: [1]   Do dołu
Drukuj
Wątek: Jak w pandas zastąpić dużą drabinę if/else?  (Przeczytany 112 razy)
« : 09:13 17/05/19 »
isoshi Offline
Hello World!

Zobacz profil
*

Reputacja: 0
Wiadomości: 2


Cześć, analizuje sobie w pracy dane w pandas. Mianowicie mam tabele z allelami osobników... wygląda to następująco:

Dwie kolumny na których pracuje nazywają się A1_Top i A2_Top. W każdym wierszu te kolumny są powiązane i muszę sobie zastąpić ich wartości w zależności od aktualnej wartości co widać w kodzie niżej.

Moje pytanie zasadnicze jest takie: jak w pandas zrobić to optymalniej, ładniej, szybciej?
W R mogę sobie zmieniać wartości pracując na skladowych samego DF, bez żadnych ifów elsów jak tu:
Kod:
  df$A_Top[df$ab=="AG"]="AB"
  df$A_Top[df$ab=="AC"]="AB"
  df$A_Top[df$ab=="CG"]="AB"
  df$A_Top[df$ab=="AT"]="AB"
  df$A_Top[df$ab=="AA"]="AA"

Moje rozwiązanie w Pandas wygląda tak:
Kod
def refomatATCGToAB(df):
   x = 0
   for i in range(lenFor):
       if a['A1_TOP'].iloc[i] == 'A' and a['A2_TOP'].iloc[i] =='C':
           a['A1_TOP'].iloc[i] = 'A'
           a['A2_TOP'].iloc[i] ='B'
       elif a['A1_TOP'].iloc[i] == 'A' and a['A2_TOP'].iloc[i] =='G':
           a['A1_TOP'].iloc[i] = 'A'
           a['A2_TOP'].iloc[i] ='B'
       elif a['A1_TOP'].iloc[i] == 'A' and a['A2_TOP'].iloc[i] =='T':
           a['A1_TOP'].iloc[i] = 'A'
           a['A2_TOP'].iloc[i] ='B'
       elif a['A1_TOP'].iloc[i] == 'C' and a['A2_TOP'].iloc[i] =='G':
           a['A1_TOP'].iloc[i] = 'A'
           a['A2_TOP'].iloc[i] ='B'
       elif a['A1_TOP'].iloc[i] == 'A' and a['A2_TOP'].iloc[i] =='A':
           a['A1_TOP'].iloc[i] = 'A'
           a['A2_TOP'].iloc[i] ='A'
       elif a['A1_TOP'].iloc[i] == 'C' and a['A2_TOP'].iloc[i] =='C':
           a['A1_TOP'].iloc[i] = 'B'
           a['A2_TOP'].iloc[i] ='B'
       elif a['A1_TOP'].iloc[i] == 'G' and a['A2_TOP'].iloc[i] =='G':
           a['A1_TOP'].iloc[i] = 'B'
           a['A2_TOP'].iloc[i] ='B'
       elif a['A1_TOP'].iloc[i] == 'T' and a['A2_TOP'].iloc[i] =='T':
           a['A1_TOP'].iloc[i] = 'B'
           a['A2_TOP'].iloc[i] ='B'
       else:
           print(x,". Something is wrong in line: ", i)
           x+=1
 

Wszystko działa elegancko, ale nie da się ukryć, że jest to rozwiązanie nieeleganckie i czuje, że nie używam potencjału pełnego dla pandas. A, i czytałem wiele wątków, jest tam rozwiązanie problemów, ale... dla pojedynczego warunku. Kompletnie nie wychodzi mi to w moim przypadku. Jakieś rady, linki, przykłady?

Pozdrawiam Uśmiech
Zapisane
« Odpowiedz #1 : 12:00 17/05/19 »
DJangoL Offline
Professional Python User

Zobacz profil
***

Reputacja: 30
Wiadomości: 423


Ponieważ nietrudno zauważyć, że kod się powtarza, potrzebujesz kolejnej pętli.
Masz pojedynczą pętle, a podwójna dała by tu radę + wszystkie etykiety A C G T ,..... w jakiejś tupli. Możesz iterować po tupli składajacej się z czteroelementowych tupli:

reformat_tab = (('A', 'C', 'A', 'B'), ('A', 'G', 'A', 'B'), ('A', 'T', 'A', 'B'), ('C', 'G', 'A', 'B'), ('A', 'A', 'A', 'A'), ('C', 'C', 'B', 'B'), ('G', 'G', 'B', 'B'), ('T', 'T', 'B', 'B'))

To jedna z możłiwości, są oczywiście możliwe inne rozwiązania

--

Można zrobić to na słowniku i będzie nawet trochę ładniej / krótszy zapis:

reformat_dict = {}
reformat_dict[('A', 'C')] = ('A', 'B')
itd. itd.

i uprości się druga pętla.

--

Zastanawiam się do czego jest na końcu kod:

x+=1

Zliczasz tak  błędy?
Zapisane
« Odpowiedz #2 : 14:11 17/05/19 »
isoshi Offline
Hello World!

Zobacz profil
*

Reputacja: 0
Wiadomości: 2


Dziękuję za sugestie. Będę próbował ogarnąć w sobotę jakieś ogarnięcie tego kodu (ta pętla jest mocno wolna). Na razie nie wiem jak zaimplementować Twoje pomysły, ale metodą prób i błędów spróbuje.

Tak, x służy do zliczania błędów. Jak się kończy program, to generowany jest plik z ilością błędów i komunikatem. Bo to musi być walidowane itp. Wiem, że można to pewnie zrobić za pomocą exceptions, ale na razie nie mam czasu próbować tego zrobić w ten sposób Chichot Teraz musze jedynie wyniki wyciągnąć, w chwili spokoju będe optymalizował Chichot

Dzięki bardzo za odpowiedź jeszcze raz
Zapisane
« Odpowiedz #3 : 20:19 18/05/19 »
DJangoL Offline
Professional Python User

Zobacz profil
***

Reputacja: 30
Wiadomości: 423


Ja tak się zastanawiam czy tu można w ogóle cokolwiek przyspieszyć. Skrócić, zapsiać ładniej kod owszem, ale optymalizować pod kątem wydajności? Uśmiech Chyba nie bardzo jest co, aczkolwiek nie znam pandas.
Zapisane
« Odpowiedz #4 : 01:33 19/05/19 »
Guaz Offline
Professional Python User

Zobacz profil
***

Reputacja: 64
Płeć: Mężczyzna
Wiadomości: 481


Zawsze możesz to zrobić na zasadzie pythonowego 'switch-case':
Kod
def refomatATCGToAB(df):
   x = 0
   A1 = "A1_TOP"
   A2 = "A2_TOP"
   switch = { ('A', 'C'): ('A', 'B'),
('A', 'G'): ('A', 'B'),
('A', 'A'): ('A', 'A'),
('G', 'G'): ('B', 'B') }
#~ Itd.
for idx in range(lenFor):
try:
a[A1].iloc[idx], a[A2].iloc[idx] = switch.get((a[A1].iloc[idx], a[A2].iloc[idx]))
except KeyError:
           print(x,". Something is wrong in line: ", idx)
x += 1
 
Minus jest taki, że try/except jest wolny, przez co całość może być wolniejsza.

Więc możnaby dodać argumenty domyślne do get'a zakładając że to nad czym pracujemy może zmienić wartość gdy coś jest złe w danej linii...

Według mnie jednak: najlepsze, najczytelniejsze i najoptymalniejsze wyjście, to dodać zmienną pomocniczą zamiast kilkukrotnie przeszukiwać słownik:
Kod
def refomatATCGToAB(df):
   x = 0
   A1 = "A1_TOP"
   A2 = "A2_TOP"
   switch = {  ('A', 'C'): ('A', 'B'),
               ('A', 'G'): ('A', 'B'),
               ('A', 'A'): ('A', 'A'),
               ('G', 'G'): ('B', 'B')  }
               #~ Itd.
   for idx in range(lenFor):
       temp = switch.get((a[A1].iloc[idx], a[A2].iloc[idx]), None)
       if temp:
           a[A1].iloc[idx], a[A2].iloc[idx] = temp
       else:
           print(x,". Something is wrong in line: ", idx)
           x += 1
 
Nie podałem go jako pierwszego, dlatego że czasem obiekty są tak duże, że nie możemy sobie pozwolić na zmienna pomocniczą, w dodatku to też może powodować problem gdy przekaże nam jedynie referencję do obiektu, który w innym miejscu zostanie zmieniony, a później także zmieni się w tym miejscu gdzie go zapisaliśmy, i ogólnie cały program zacznie tworzyć różne dziwne rzeczy... Sytuacje rzadkie, ale trzeba je mieć na uwadze Uśmiech

Co do wydajności, kolumna if'ów będzie bardziej czasochłonna niż dostęp do elementów słownika, zależnie też, jak dobrze są wypozycjonowane (pod względem częstości występowania) if'y różnice mogą być znikome Uśmiech
Zapisane

Python 3.5+ / Mint

Daje wędkę zamiast ryby. Chyba że ktoś się chce czegoś nauczyć, wtedy chętnie pomogę każdemu.
Za rybę niestety trzeba zapłacić Z politowaniem.
Strony: [1]   Do góry
Drukuj
Skocz do:  

© 2007 - 2019 Polish Python Coders Group
Powered by SMF 1.1.21 | SMF © 2006-2009, Simple Machines | Theme by PixelSlot