2017-08-19 2 views
0

私は、特定の年のデータを取り込み、データフレームを返す関数を持っています。例えば個々のパンダのデータフレームをフラット化して新しいフレームを作成する方法を教えてください。

DF

year fruit license  grade 
1946 apple  XYZ  1 
1946 orange  XYZ  1 
1946 apple  PQR  3 
1946 orange  PQR  1 
1946 grape  XYZ  2 
1946 grape  PQR  1 
.. 
2014 grape  LMN  1 

注: 1)特定のライセンスの値は例えば(のみ特定の年のために存在し、一度だけ、特定の果物のためになるXYZだけのために。 1946年、リンゴ、オレンジ、ブドウのために一度だけ)。 2)グレード値はカテゴリに分類されます。

私は以下の機能が目的の目標である、 を達成するのに非常に効率的ではないことを認識していますが、これは私が現在取り組んでいることです。

df_1946=func(df,1946) 
df_1946.head() 

Source Target Weight 
Apple  Orange  0.6 
Apple  Grape  0.3 
Orange Grape  0.7 

が、私は単一の行に上記を平らにしたい:

def func(df, year): 
    #1. Filter out only the data for the year needed 

    df_year=df[df['year']==year] 
    ''' 
    2. Transform DataFrame to the form: 
       XYZ PQR ..  LMN 
    apple  1  3    1 
    orange  1  1    3 
    grape  2  1    1 
    Note that 'LMN' is just used for representation purposes. 
    It won't logically appear here because it can only appear for the year 2014. 
    ''' 
    df_year = df_year.pivot(index='fruit',columns='license',values='grade')  

    #3. Remove all fruits that have ANY NaN values 
    df_year=df_year.dropna(axis=1, how="any") 

    #4. Some additional filtering 

    #5. Function to calculate similarity between fruits 
    def similarity_score(fruit1, fruit2): 
     agreements=np.sum( ((fruit1 == 1) & (fruit2 == 1)) | \ 
     ( (fruit1 == 3) & (fruit2 == 3))) 

     disagreements=np.sum( ((fruit1 == 1) & (fruit2 == 3)) |\ 
     ( (fruit1 == 3) & (fruit2 == 1))) 

     return (((agreements-disagreements) /float(len(fruit1))) +1)/2) 

    #6. Create Network dataframe 
    network_df=pd.DataFrame(columns=['Source','Target','Weight']) 

    for i,c in enumerate(combinations(df_year,2)): 
     c1=df[[c[0]]].values.tolist() 
     c2=df[[c[1]]].values.tolist() 
     c1=[item for sublist in c1 for item in sublist] 
     c2=[item for sublist in c2 for item in sublist] 
     network_df.loc[i] = [c[0],c[1],similarity_score(c1,c2)] 

    return network_df 

以上実行すると、与え上記の3つの列を持っていません

 (Apple,Orange) (Apple,Grape) (Orange,Grape) 
1946  0.6    0.3   0.7 

ますが、実際には周り5000カラム。

df_all_years

 (Apple,Orange) (Apple,Grape) (Orange,Grape) 
1946  0.6    0.3   0.7 
1947  0.7    0.25   0.8 
.. 
2015  0.75   0.3   0.65 

これを行うための最善の方法は何ですか

結局、私のような何かを得るために変換データフレームの列をスタックしたいですか?

+0

'(アップル、オレンジ)' - それは文字列またはタプルですか? – MaxU

+0

タプル。特定のセルがどのような組み合わせを表しているかを示す方法がある限り、好きなものを使うことができます。 – Melsauce

答えて

2

計算を少しずつ並べ替えています。代わりに、長年にわたってループの :

for year in range(1946, 2015): 
    partial_result = func(df, year) 

して、部分的な結果を連結し、あなたはdf.groupby(...)を呼び出す前に、全体のデータフレームにdf、 をできるだけ多くの作業を行うことによって より良いパフォーマンスを得ることができます。また、sumcountなどの組み込みアグリゲータの観点から計算を表現できる場合は、groupby/applyでカスタム関数を使用する場合よりも迅速に計算を実行できます。

import itertools as IT 
import numpy as np 
import pandas as pd 
np.random.seed(2017) 

def make_df(): 
    N = 10000 
    df = pd.DataFrame({'fruit': np.random.choice(['Apple', 'Orange', 'Grape'], size=N), 
         'grade': np.random.choice([1,2,3], p=[0.7,0.1,0.2], size=N), 
         'year': np.random.choice(range(1946,1950), size=N)}) 
    df['manufacturer'] = (df['year'].astype(str) + '-' 
          + df.groupby(['year', 'fruit'])['fruit'].cumcount().astype(str)) 
    df = df.sort_values(by=['year']) 
    return df 

def similarity_score(df): 
    """ 
    Compute the score between each pair of columns in df 
    """ 
    agreements = {} 
    disagreements = {} 
    for col in IT.combinations(df,2): 
     fruit1 = df[col[0]].values 
     fruit2 = df[col[1]].values 
     agreements[col] = (((fruit1 == 1) & (fruit2 == 1)) 
          | ((fruit1 == 3) & (fruit2 == 3))) 
     disagreements[col] = (((fruit1 == 1) & (fruit2 == 3)) 
           | ((fruit1 == 3) & (fruit2 == 1))) 
    agreements = pd.DataFrame(agreements, index=df.index) 
    disagreements = pd.DataFrame(disagreements, index=df.index) 
    numerator = agreements.astype(int)-disagreements.astype(int) 
    grouped = numerator.groupby(level='year') 
    total = grouped.sum() 
    count = grouped.count() 
    score = ((total/count) + 1)/2 
    return score 

df = make_df() 
df2 = df.set_index(['year','fruit','manufacturer'])['grade'].unstack(['fruit']) 
df2 = df2.dropna(axis=0, how="any") 

print(similarity_score(df2)) 

利回り

  Grape Orange   
     Apple  Apple  Grape 
year        
1946 0.629111 0.650426 0.641900 
1947 0.644388 0.639344 0.633039 
1948 0.613117 0.630566 0.616727 
1949 0.634176 0.635379 0.637786 
+0

質問を編集し、dfとfuncの両方を定義して、何が起こっているのかをよりよく理解できるようにしました。より多くの情報を提供して幸いです。 – Melsauce

1

ここでは、を参照してください方法でテーブルを旋回するようにパンダのルーチンを行うための一つの方法です。最初の2つのクラスからコンビナトリアルに至るまでに、5,000個のカラムを処理すると同時に(クアッドコアのMacBookではボトルネックステップが約20秒かかりました)、はるかに大きなスケーリングのためには、はるかに速い戦略があります。この例のデータはきわめてまばらです(5K列、70行からの5Kランダムサンプル[1947-2016])、より完全なデータフレームでは実行時間が数秒長くなることがあります。

from itertools import chain 
import pandas as pd 
import numpy as np 
import random # using python3 .choices() 
import re 

# Make bivariate data w/ 5000 total combinations (1000x5 categories) 
# Also choose 5,000 randomly; some combinations may have >1 values or NaN 
random_sample_data = np.array(
    [random.choices(['Apple', 'Orange', 'Lemon', 'Lime'] + 
        ['of Fruit' + str(i) for i in range(1000)], 
        k=5000), 
    random.choices(['Grapes', 'Are Purple', 'And Make Wine', 
        'From the Yeast', 'That Love Sugar'], 
        k=5000), 
    [random.random() for _ in range(5000)]] 
).T 
df = pd.DataFrame(random_sample_data, columns=[ 
        "Source", "Target", "Weight"]) 
df['Year'] = random.choices(range(1947, 2017), k=df.shape[0]) 

# Three views of resulting df in jupyter notebook: 
df 
df[df.Year == 1947] 
df.groupby(["Source", "Target"]).count().unstack() 

enter image description here

GROUPBYを適用する機能を必要とするので、あなたは一時DF仲介を使用することができ、グループ化により、年間のデータをフラット化するには、次の

  1. プッシュ全てdata.groupby("Year")へ個々の行が、「ターゲット」+「ソース」(後で拡張する)+「重量」の2つの列ごとに別々のデータフレームで表示されます。
  2. zippd.core.reshape.util.cartesian_productを使用して、temp_dfから発生する最終テーブルになる、空の適切な形のピボットdfを作成します。最後に

例えば、

df_temp = df.groupby("Year").apply(
    lambda s: pd.DataFrame([(s.Target, s.Source, s.Weight)], 
          columns=["Target", "Source", "Weight"]) 
).sort_index() 
df_temp.index = df_temp.index.droplevel(1) # reduce MultiIndex to 1-d 

# Predetermine all possible pairwise column category combinations 
product_ts = [*zip(*(pd.core.reshape.util.cartesian_product(
    [df.Target.unique(), df.Source.unique()]) 
))] 

ts_combinations = [str(x + ' ' + y) for (x, y) in product_ts] 

ts_combinations 

enter image description here

、(示されているようpd.DataFrame.iterrowsが、スピード物事を助けるかもしれませんが、最速再び、ではない)のための-のためのネストされた反復の簡単な使用しています。無作為抽出のために複数の値を扱う必要があったので、おそらく2番目のforループ以下の条件を削除したいと思っていました。これは3つの別々のデータフレームが毎年1つの行ピボットされた( "Weight")x( "Target" - "Source")の関係を介して、

df_pivot = pd.DataFrame(np.zeros((70, 5000)), 
         columns=ts_combinations) 
df_pivot.index = df_temp.index 

for year, values in df_temp.iterrows(): 

    for (target, source, weight) in zip(*values): 

     bivar_pair = str(target + ' ' + source) 
     curr_weight = df_pivot.loc[year, bivar_pair] 

     if curr_weight == 0.0: 
      df_pivot.loc[year, bivar_pair] = [weight] 
     # append additional values if encountered 
     elif type(curr_weight) == list: 
      df_pivot.loc[year, bivar_pair] = str(curr_weight + 
               [weight]) 

enter image description here

# Spotcheck: 
# Verifies matching data in pivoted table vs. original for Target+Source 
# combination "And Make Wine of Fruit614" across all 70 years 1947-2016 
df 
df_pivot['And Make Wine of Fruit614'] 
df[(df.Year == 1947) & (df.Target == 'And Make Wine') & (df.Source == 'of Fruit614')] 
関連する問題