2017-05-24 2 views
2

私はすべてのフロート列のデータフレームを持っています。例えば:すべての列の差を計算する最速の方法

import numpy as np 
import pandas as pd 

df = pd.DataFrame(np.arange(12.0).reshape(3,4), columns=list('ABCD')) 
# A B  C  D 
# 0 0.0 1.0 2.0 3.0 
# 1 4.0 5.0 6.0 7.0 
# 2 8.0 9.0 10.0 11.0 

Iは、列のすべての組み合わせのための列方向の差を計算したい(例えば、-B、A-C、B-C、など)。列の数が多い場合もありますので、私は同様の効率/できるだけ早く計算を行うにはしたいと思い

A_B A_C A_D B_C B_D C_D 
-1.0 -2.0 -3.0 -1.0 -2.0 -1.0 
-1.0 -2.0 -3.0 -1.0 -2.0 -1.0 
-1.0 -2.0 -3.0 -1.0 -2.0 -1.0 

例えば、所望の出力は次のようなものになるだろう。私はデータフレームを最初にnumpy配列に変換することで大きなスピードバンプを得るだろうと思いますが、私はそれを行いますが、大きなパフォーマンス向上をもたらす他の戦略があるかどうかは疑問です。たぶんいくつかの行列代数や多次元データ形式のトリックが、すべてのユニークな組み合わせをループする必要はありません。どんな提案も大歓迎です。このプロジェクトはPython 3で書かれています。

+0

を '[-1、-1、-1] '?あなたはこれから何を期待していますか? – Psidom

+0

出力例をいくつか追加しました。私は列の各セットの配列/ベクトル出力を探していますが、それらがスタンドアロンかリスト、多次元配列などで収集されている場合はあまり関係ありません –

答えて

3

がありますパフォーマンスのための2つのNumPyアプローチ - 1つは完全にベクトル化されたアプローチであり、もう1つはループ1つです。

アプローチ#1

def numpy_triu1(df):   
    a = df.values 
    r,c = np.triu_indices(a.shape[1],1) 
    cols = df.columns 
    nm = [cols[i]+"_"+cols[j] for i,j in zip(r,c)] 
    return pd.DataFrame(a[:,r] - a[:,c], columns=nm) 

サンプル実行 -

In [72]: df 
Out[72]: 
    A B  C  D 
0 0.0 1.0 2.0 3.0 
1 4.0 5.0 6.0 7.0 
2 8.0 9.0 10.0 11.0 

In [78]: numpy_triu(df) 
Out[78]: 
    A_B A_C A_D B_C B_D C_D 
0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 
1 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 
2 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 

アプローチ#2

我々は専門の列名なしで出力またはデータフレームとして配列を持つ大丈夫です場合は、ここが別です -

def pairwise_col_diffs(a): # a would df.values 
    n = a.shape[1] 
    N = n*(n-1)//2 
    idx = np.concatenate(([0], np.arange(n-1,0,-1).cumsum())) 
    start, stop = idx[:-1], idx[1:] 
    out = np.empty((a.shape[0],N),dtype=a.dtype) 
    for j,i in enumerate(range(n-1)): 
     out[:, start[j]:stop[j]] = a[:,i,None] - a[:,i+1:] 
    return out 

ランタイム試験

OPマルチDIMアレイの出力は、ここで、同様にそれらのために働くだろうと述べているので、他の著者(複数可)からのアレイベースのアプローチである -

# @Allen's soln 
def Allen(arr): 
    n = arr.shape[1] 
    idx = np.asarray(list(itertools.combinations(range(n),2))).T 
    return arr[:,idx[0]]-arr[:,idx[1]] 

# @DYZ's soln 
def DYZ(arr): 
    result = np.concatenate([(arr.T - arr.T[x])[x+1:] \ 
      for x in range(arr.shape[1])]).T 
    return result 

pandasベースの@Gerges Dibのポストは、他のものと比較して非常に遅くなっているので、含まれていませんでした。

タイミング -

我々は3つのデータセットのサイズを使用します - 1005001000:あなたは配列をしたいですか、* AB *について

In [118]: df = pd.DataFrame(np.random.randint(0,9,(3,100))) 
    ...: a = df.values 
    ...: 

In [119]: %timeit DYZ(a) 
    ...: %timeit Allen(a) 
    ...: %timeit pairwise_col_diffs(a) 
    ...: 
1000 loops, best of 3: 258 µs per loop 
1000 loops, best of 3: 1.48 ms per loop 
1000 loops, best of 3: 284 µs per loop 

In [121]: df = pd.DataFrame(np.random.randint(0,9,(3,500))) 
    ...: a = df.values 
    ...: 

In [122]: %timeit DYZ(a) 
    ...: %timeit Allen(a) 
    ...: %timeit pairwise_col_diffs(a) 
    ...: 
100 loops, best of 3: 2.56 ms per loop 
10 loops, best of 3: 39.9 ms per loop 
1000 loops, best of 3: 1.82 ms per loop 

In [123]: df = pd.DataFrame(np.random.randint(0,9,(3,1000))) 
    ...: a = df.values 
    ...: 

In [124]: %timeit DYZ(a) 
    ...: %timeit Allen(a) 
    ...: %timeit pairwise_col_diffs(a) 
    ...: 
100 loops, best of 3: 8.61 ms per loop 
10 loops, best of 3: 167 ms per loop 
100 loops, best of 3: 5.09 ms per loop 
+0

申し訳ありませんが、比較ソリューションのタイミングを追加することは可能ですか? – jezrael

+0

@jezraelが追加されました。 – Divakar

1

私はあなたがNumPyでそれを行うことができると思います。 Let arr=df.valuesとしましょう。まずは、すべての2列の組み合わせを見つけてみましょう:、今

from itertools import combinations  
column_combos = combinations(range(arr.shape[1]), 2) 

を列ペアワイズを減算して2次元配列に戻しアレイのリストを変換:

result = np.array([(arr[:,x[1]] - arr[:,x[0]]) for x in column_combos]).T 
#array([[1., 2., 3., 1., 2., 1.], 
#  [1., 2., 3., 1., 2., 1.], 
#  [1., 2., 3., 1., 2., 1.]]) 

別の解決策である、多少(〜15%) 速くそれが列ではなく、全体の2次元配列を減算し、持っているので、少ないのPython側の反復:

result = np.concatenate([(arr.T - arr.T[x])[x+1:] for x in range(arr.shape[1])]).T 
#array([[ 1., 2., 3., 1., 2., 1.], 
#  [ 1., 2., 3., 1., 2., 1.], 
#  [ 1., 2., 3., 1., 2., 1.]]) 

あなたは結果BAを変換することができます

columns = list(map(lambda x: x[1]+x[0], combinations(df.columns, 2))) 
#['BA', 'CA', 'DA', 'CB', 'DB', 'DC'] 

pd.DataFrame(result, columns=columns) 
# BA CA DA CB DB DC 
#0 1.0 2.0 3.0 1.0 2.0 1.0 
#1 1.0 2.0 3.0 1.0 2.0 1.0 
#2 1.0 2.0 3.0 1.0 2.0 1.0 
0

私はこれが他の可能な方法と比較することができますどのくらいの速わかりませんが、ここでは、次のとおりです:DATAFRAMEにCKあなたがしたい場合は、この記事に記載されている

df = pd.DataFrame(np.arange(12.0).reshape(3,4), columns=list('ABCD')) 

# get the columns as list 
cols = list(df.columns) 
# define output dataframe 
out = pd.DataFrame() 

# loop over possible periods 
for period in range(1, df.shape[1]): 
    names = [l1 + l2 for l1, l2, in zip(cols, cols[period:])] 
    out[names] = df.diff(periods=period, axis=1).dropna(axis=1, how='all') 

print(out) 

# column name shows which two columns are subtracted 

    AB BC CD AC BD AD 
0 1.0 1.0 1.0 2.0 2.0 3.0 
1 1.0 1.0 1.0 2.0 2.0 3.0 
2 1.0 1.0 1.0 2.0 2.0 3.0 
1
import itertools 
df = pd.DataFrame(np.arange(12.0).reshape(3,4), columns=list('ABCD')) 
df_cols = df.columns.tolist() 
#build a index array of all the pairs need to do the subtraction 
idx = np.asarray(list(itertools.combinations(range(len(df_cols)),2))).T 
#build a new DF using the pairwise difference and column names 
df_new = pd.DataFrame(data=df.values[:,idx[0]]-df.values[:,idx[1]], 
         columns=[''.join(e) for e in (itertools.combinations(df_cols,2))]) 

df_new 
Out[43]: 
    AB AC AD BC BD CD 
0 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 
1 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 
2 -1.0 -2.0 -3.0 -1.0 -2.0 -1.0 
関連する問題