2017-11-26 2 views
13

ich ein Datenrahmen haben zusammenzufassen, die wie folgt aussieht:effiziente Weise alle möglichen Paare

from random import randint 
import pandas as pd 

df = pd.DataFrame({"ID": ["a", "b", "c", "d", "e", "f", "g"], 
        "Size": [randint(0,9) for i in range(0,7)]}) 

df 

    ID Size 
0 a  4 
1 b  3 
2 c  0 
3 d  2 
4 e  9 
5 f  5 
6 g  3 

Und was ich möchte erhalten, ist dies (könnte eine Matrix als auch sein):

sums_df 

     a  b c  d  e  f  g 
a 8.0 7.0 4.0 6.0 13.0 9.0 7.0 
b 7.0 6.0 3.0 5.0 12.0 8.0 6.0 
c 4.0 3.0 0.0 2.0 9.0 5.0 3.0 
d 6.0 5.0 2.0 4.0 11.0 7.0 5.0 
e 13.0 12.0 9.0 11.0 18.0 14.0 12.0 
f 9.0 8.0 5.0 7.0 14.0 10.0 8.0 
g 7.0 6.0 3.0 5.0 12.0 8.0 6.0 

Das ist die Summe der Size Werte für alle möglichen Paare in ID.

Für jetzt habe ich diese einfache, aber uneffizient Code:

sums_df = pd.DataFrame() 

for i in range(len(df)): 
    for j in range(len(df)): 
     sums_df.loc[i,j] = df.Size[i] + df.Size[j] 

sums_df.index = list(df.ID) 
sums_df.columns = list(df.ID) 

Es funktioniert gut für kleine Beispiele wie diese, aber für meine eigentlichen Daten wird es zu lange, und ich bin sicher, dass es möglich ist, die verschachtelte zu vermeiden for Schleifen. Kannst du dir einen besseren Weg vorstellen?

Danke für jede Hilfe!

+2

Nur neugierig, warum brauchen Sie es? –

+0

Ich habe einen anderen Datenrahmen, den ich als Adjazenzmatrix mit NetworkX verwende (nennen wir es df1), und ich möchte df1 elementweise durch sums_df dividieren, um say df2 zu erhalten. df1 enthält Häufigkeiten von gemeinsamen Elementen zwischen den IDs, aber ich habe auch die Anzahl der Elemente in jeder ID (hier Größe, und IDs sind eigentlich IDs von Gruppen). Auf diese Weise sind die Gewichte, die ich mit df2 verwende, die Anteile gemeinsamer Elemente und nicht die Häufigkeiten gemeinsamer Elemente, die df1 als Adjazenzmatrix verwenden. Ich hoffe, das ist klar genug! – atonnerre

Antwort

22

Verwendung np.add.outer():

In [65]: pd.DataFrame(np.add.outer(df['Size'], df['Size']), 
         columns=df['ID'].values, 
         index=df['ID'].values) 
Out[65]: 
    a b c d e f g 
a 8 7 4 6 13 9 7 
b 7 6 3 5 12 8 6 
c 4 3 0 2 9 5 3 
d 6 5 2 4 11 7 5 
e 13 12 9 11 18 14 12 
f 9 8 5 7 14 10 8 
g 7 6 3 5 12 8 6 

UPDATE: Speicher sparende (Pandas Multi-Index) Ansatz (Hinweis: dieser Ansatz ist viel langsamer, im Vergleich zu dem vorherigen):

In [33]: r = pd.DataFrame(np.array(list(combinations(df['Size'], 2))).sum(axis=1), 
    ...:     index=pd.MultiIndex.from_tuples(list(combinations(df['ID'], 2))), 
    ...:     columns=['TotalSize'] 
    ...:) 

In [34]: r 
Out[34]: 
    TotalSize 
a b   7 
    c   4 
    d   6 
    e   13 
    f   9 
    g   7 
b c   3 
    d   5 
    e   12 
    f   8 
    g   6 
c d   2 
    e   9 
    f   5 
    g   3 
d e   11 
    f   7 
    g   5 
e f   14 
    g   12 
f g   8 

Es kann wie folgt zugegriffen werden:

In [41]: r.loc[('a','b')] 
Out[41]: 
TotalSize 7 
Name: (a, b), dtype: int32 

In [42]: r.loc[('a','b'), 'TotalSize'] 
Out[42]: 7 

In [44]: r.loc[[('a','b'), ('c','d')], 'TotalSize'] 
Out[44]: 
a b 7 
c d 2 
Name: TotalSize, dtype: int32 

In [43]: r.at[('a','b'), 'TotalSize'] 
Out[43]: 7 

Speichernutzung Vergleich (DF Form: 7000x3):

In [65]: df = pd.concat([df] * 1000, ignore_index=True) 

In [66]: df.shape 
Out[66]: (7000, 2) 

In [67]: r1 = pd.DataFrame(np.add.outer(df['Size'], df['Size']), 
    ...:      columns=df['ID'].values, 
    ...:      index=df['ID'].values) 
    ...: 

In [68]: r2 = pd.DataFrame(np.array(list(combinations(df['Size'], 2))).sum(axis=1), 
    ...:     index=pd.MultiIndex.from_tuples(list(combinations(df['ID'], 2))), 
    ...:     columns=['TotalSize']) 
    ...: 

In [69]: r1.memory_usage().sum()/r2.memory_usage().sum() 
Out[69]: 2.6685407829018244 

Geschwindigkeitsvergleich (DF Form: 7000x3):

In [70]: %%timeit 
    ...: r1 = pd.DataFrame(np.add.outer(df['Size'], df['Size']), 
    ...:      columns=df['ID'].values, 
    ...:      index=df['ID'].values) 
    ...: 
180 ms ± 2.99 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

In [71]: %%timeit 
    ...: r2 = pd.DataFrame(np.array(list(combinations(df['Size'], 2))).sum(axis=1), 
    ...:     index=pd.MultiIndex.from_tuples(list(combinations(df['ID'], 2))), 
    ...:     columns=['TotalSize']) 
    ...: 
17 s ± 325 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 
+0

Genau das habe ich gesucht, und es ist tatsächlich extrem schneller. Vielen Dank! – atonnerre

+1

@atonnerre Grob gesagt, wie viel schneller wird es sein? Es ist wie ein 10% Gewinn oder ein 10x Gewinn? – Nat

+3

Gibt es eine Möglichkeit, die Symmetrie des Ergebnisses zu nutzen (eine oder beide: um die Laufzeit um etwa die Hälfte zu reduzieren, um den Speicher um etwa die Hälfte zu reduzieren)? –

11

Verwenden Numpy des

size = df.Size.values 
ids = df.ID.values 

pd.DataFrame(
    size[:, None] + size, 
    ids, ids 
) 

    a b c d e f g 
a 8 7 4 6 13 9 7 
b 7 6 3 5 12 8 6 
c 4 3 0 2 9 5 3 
d 6 5 2 4 11 7 5 
e 13 12 9 11 18 14 12 
f 9 8 5 7 14 10 8 
g 7 6 3 5 12 8 6 
9

Oder so etwas wie .values und .values.T Rundfunk

df1=df.set_index('ID') 
df1.values+df1.values.T 
Out[626]: 
array([[ 8, 7, 4, 6, 13, 9, 7], 
     [ 7, 6, 3, 5, 12, 8, 6], 
     [ 4, 3, 0, 2, 9, 5, 3], 
     [ 6, 5, 2, 4, 11, 7, 5], 
     [13, 12, 9, 11, 18, 14, 12], 
     [ 9, 8, 5, 7, 14, 10, 8], 
     [ 7, 6, 3, 5, 12, 8, 6]], dtype=int64) 

Weitere Informationen:

pd.DataFrame(data=df1.values+df1.values.T,index=df.index,columns=df.index) 
Out[627]: 
ID a b c d e f g 
ID       
a 8 7 4 6 13 9 7 
b 7 6 3 5 12 8 6 
c 4 3 0 2 9 5 3 
d 6 5 2 4 11 7 5 
e 13 12 9 11 18 14 12 
f 9 8 5 7 14 10 8 
g 7 6 3 5 12 8 6 
+0

Ich erhalte einen Fehler, wenn ich das versuche. Ich weiß nicht warum, aber wenn ich versuche "df ['Size']. Werte + df ['Size']. Values.T" Ich bekomme "Array ([10, 0, 12, 8, 16, 0, 16], dtype = int64) ". – atonnerre

+0

@atonnerre versuchen Sie es erneut, ich füge 'df1 = df.set_index ('ID')', Entschuldigung für die Verwirrung. – Wen

+1

Bitte beachten Sie, dass ich erwähnt habe, dass ich den Index gesetzt habe, als ich das geschrieben habe. Ich könnte mir das selbst vorstellen, nicht Ihre Lösung, die sehr wertvoll ist! :) Und danke dir auch! – atonnerre

Verwandte Themen