2017-07-28 14 views
5

Tytuł jest nieco mylący, ale zrobię co w mojej mocy, aby wyjaśnić mój problem tutaj. Mam 2 pandy dataframes, A i B:Porównywanie wartości z jednej ramki danych z wartościami z kolumn w innej ramce danych i uzyskiwanie danych z trzeciej kolumny

>> print a 

id | value 
1 | 250 
2 | 150 
3 | 350 
4 | 550 
5 | 450 

>> print b 

low | high | class 
100 | 200 | 'A' 
200 | 300 | 'B' 
300 | 500 | 'A' 
500 | 600 | 'C' 

chcę utworzyć nową kolumnę o nazwie klasa w tabeli A, który zawiera klasę wartości zgodnie z tabelą B. Oto wynik chcę:

>> print a 

id | value | class 
1 | 250 | 'B' 
2 | 150 | 'A' 
3 | 350 | 'A' 
4 | 550 | 'C' 
5 | 450 | 'A' 

Mam następujący kod napisany tego rodzaju robi to, co chcę:

a['class'] = pd.Series() 
for i in range(len(a)): 
    val = a['value'][i] 
    cl = (b['class'][ (b['low'] <= val) \ 
         (b['high'] >= val) ].iat[0]) 
    a['class'].set_value(i,cl) 

Problem ten jest szybki dla tabel długości 10 lub tak, ale ja Próbuję to zrobić przy stole o wielkości 100 000+ zarówno dla a, jak i dla b. Czy jest to szybszy sposób, używając jakiejś funkcji/atrybutu w pandach?

Odpowiedz

1

Oto rozwiązanie, które jest wprawdzie mniej elegancki niż przy użyciu Series.searchsorted, ale działa super szybko!

Wyciągam dane z pandas DataFrames i konwertuję je na listy, a następnie używam np.where, aby wypełnić zmienną o nazwie "aclass", gdzie warunki są zharmonizowane (w trybie brute force dla pętli). Następnie piszę "aclass" do oryginalnej ramki danych.

Czas oceny wyniósł 0,07489705 s, więc jest dość szybki, nawet z 200 000 punktów danych!

# create 200,000 fake a data points 
avalue = 100+600*np.random.random(200000) # assuming you extracted this from a with avalue = np.array(a['value']) 

blow = [100,200,300,500] # assuming you extracted this from b with list(b['low']) 
bhigh = [200,300,500,600] # assuming you extracted this from b with list(b['high']) 
bclass = ['A','B','A','C'] # assuming you extracted this from b with list(b['class']) 

aclass = [[]]*len(avalue) # initialize aclass 

start_time = time.time() # this is just for timing the execution 
for i in range(len(blow)): 
    for j in np.where((avalue>=blow[i]) & (avalue<=bhigh[i]))[0]: 
     aclass[j]=bclass[i] 

# add the class column to the original a DataFrame 
a['class'] = aclass 

print("--- %s seconds ---" % np.round(time.time() - start_time,decimals = 8)) 
+0

o wiele szybciej niż mój schemat pętli, jaki mam! również przetestuje to rozwiązanie! – rbae

5

Oto sposób zrobić szereg join zainspirowany @ piRSquared na solution:

A = a['value'].values 
bh = b.high.values 
bl = b.low.values 

i, j = np.where((A[:, None] >= bl) & (A[:, None] <= bh)) 

pd.DataFrame(
    np.column_stack([a.values[i], b.values[j]]), 
    columns=a.columns.append(b.columns) 
) 

wyjściowa:

id value low high class 
0 1 250 200 300 'B' 
1 2 150 100 200 'A' 
2 3 350 300 500 'A' 
3 4 550 500 600 'C' 
4 5 450 300 500 'A' 
+0

ah, to doskonałe rozwiązanie. W końcu otrzymuję MemoryError. Rozwiązanie piRSquared zawiera tabele o długości 2M +, podczas gdy moje wynosi tylko około 100 000. Sprawdzę dwa błędy. – rbae

+0

@Scott Boston, czy możesz spojrzeć na pytanie, które napisałem, nikt jeszcze nie odpowiedział, będę zadowolony, jeśli mi pomożesz. https://stackoverflow.com/questions/45511223/how-to-perform-single-operation-on-multiple-columns-of-dataframe – BlueQuant

Powiązane problemy