2016-08-08 16 views
4

Ich versuche, mehrere Kreuztabellen in einem einzigen zusammenzuführen. Beachten Sie, dass die bereitgestellten Daten offensichtlich nur zu Testzwecken dienen. Die tatsächlichen Daten sind viel größer, so dass die Effizienz für mich sehr wichtig ist.Zusammenführen von Kreuztabellen in Python

Die Kreuztabellen werden generiert, aufgelistet und anschließend mit einer Lambda-Funktion in der Spalte word zusammengeführt. Das Ergebnis dieser Verschmelzung ist jedoch nicht das, was ich erwarte. Ich denke, das Problem ist, dass die Spalten mit nur NA-Werten der Kreuztabellen fallen gelassen werden, selbst wenn dropna = False verwendet wird, was dann dazu führen würde, dass die merge-Funktion fehlschlägt. Ich zeige zuerst den Code und danach die Zwischendaten und Fehler.

import pandas as pd 
import numpy as np 
import functools as ft 

def main(): 
    # Create dataframe 
    df = pd.DataFrame(data=np.zeros((0, 3)), columns=['word','det','source']) 
    df["word"] = ('banana', 'banana', 'elephant', 'mouse', 'mouse', 'elephant', 'banana', 'mouse', 'mouse', 'elephant', 'ostrich', 'ostrich') 
    df["det"] = ('a', 'the', 'the', 'a', 'the', 'the', 'a', 'the', 'a', 'a', 'a', 'the') 
    df["source"] = ('BE', 'BE', 'BE', 'NL', 'NL', 'NL', 'FR', 'FR', 'FR', 'FR', 'FR', 'FR') 

    create_frequency_list(df) 

def create_frequency_list(df): 
    # Create a crosstab of ALL values 
    # NOTE that dropna = False does not seem to work as expected 
    total = pd.crosstab(df.word, df.det, dropna = False) 
    total.fillna(0) 
    total.reset_index(inplace=True) 
    total.columns = ['word', 'a', 'the'] 

    crosstabs = [total] 

    # For the column headers, multi-level 
    first_index = [('total','total')] 
    second_index = [('a','the')] 

    # Create crosstabs per source (one for BE, one for NL, one for FR) 
    # NOTE that dropna = False does not seem to work as expected 
    for source, tempDf in df.groupby('source'): 
     crosstab = pd.crosstab(tempDf.word, tempDf.det, dropna = False) 
     crosstab.fillna(0) 
     crosstab.reset_index(inplace=True) 
     crosstab.columns = ['word', 'a', 'the'] 
     crosstabs.append(crosstab) 

     first_index.extend((source,source)) 
     second_index.extend(('a','the')) 

    # Just for debugging: result as expected 
    for tab in crosstabs: 
     print(tab) 

    merged = ft.reduce(lambda left,right: pd.merge(left,right, on='word'), crosstabs).set_index('word') 

    # UNEXPECTED RESULT 
    print(merged)  

    arrays = [first_index, second_index] 

    # Throws error: NotImplementedError: > 1 ndim Categorical are not supported at this time 
    columns = pd.MultiIndex.from_arrays(arrays) 

    df_freq = pd.DataFrame(data=merged.as_matrix(), 
         columns=columns, 
         index = crosstabs[0]['word']) 
    print(df_freq) 

main() 

Einzel Crosstabs: nicht wie erwartet. Die NA Spalten

fiel
 word a the 
0 banana 2 1 
1 elephant 1 2 
2  mouse 2 2 
3 ostrich 1 1 

     word a the 
0 banana 1 1 
1 elephant 0 1 

     word a the 
0 banana 1 0 
1 elephant 1 0 
2  mouse 1 1 
3 ostrich 1 1 

     word a the 
0 elephant 0 1 
1  mouse 1 1 

Das bedeutet, dass die Datenrahmen wiederum die wahrscheinlich die Verschmelzung vermasseln nicht alle Werte untereinander teilen.

Merge: nicht wie erwartet, offensichtlich

  a_x the_x a_y the_y a_x the_x a_y the_y 
word              
elephant 1  2 0  1 1  0 0  1 

der Fehler jedoch wird nur an der Spalten Zuordnung geworfen:

# NotImplementedError: > 1 ndim Categorical are not supported at this time 
columns = pd.MultiIndex.from_arrays(arrays) 

So soweit ich das Problem früh sagen kann, beginnt mit den NAs und bringt das Ganze zum Scheitern. Da ich jedoch in Python nicht genug Erfahrung habe, kann ich es nicht genau sagen.

Was ich erwartet hatte, war ein Multi-Index-Ausgang:

source  total  BE   FR   NL 
    det   a the  a the  a the  a the 
    word 
0 banana  2 1  1 1  1 0  0 0 
1 elephant 1 2  0 1  1 0  0 1 
2 mouse  2 2  0 0  1 1  1 1 
3 ostrich  1 1  0 0  1 1  0 0 

Antwort

2

Ich habe gerade beschlossen, um Ihnen einen besseren Weg, Sie bekommen, was Sie wollen:

ich df.groupby([col1, col2]).size().unstack() Proxy verwenden, wie meine pd.crosstab als eine allgemeine Regel. Sie haben versucht, eine Kreuztabelle für jede Gruppe von source zu erstellen. Ich kann das gut in meine bestehende Gruppe mit df.groupby([col1, col2, col3]).size().unstack([2, 1])

passen Die sort_index(1).fillna(0).astype(int) ist nur zu schönen Dingen.

Wenn Sie noch besser verstehen wollen. Versuchen Sie, die folgenden Dinge und sehen, was man bekommt:

  • df.groupby(['word', 'gender']).size()
  • df.groupby(['word', 'gender', 'source']).size()

unstack und stack sind praktische Möglichkeiten, um Dinge, die statt und umgekehrt in den Index in die Säulen waren. unstack([2, 1]) gibt die Reihenfolge an, in der Indexstufen entstapelt werden.

Schließlich nehme ich meine xtabs und stack wieder und summiere über die Zeilen und unstack zu prep zu pd.concat. Voilà!

xtabs = df.groupby(df.columns.tolist()).size() \ 
      .unstack([2, 1]).sort_index(1).fillna(0).astype(int) 

pd.concat([xtabs.stack().sum(1).rename('total').to_frame().unstack(), xtabs], axis=1) 

enter image description here

Ihren Code nun wie folgt aussehen:

bisher für die Mühe
import pandas as pd 
import numpy as np 
import functools as ft 

def main(): 
    # Create dataframe 
    df = pd.DataFrame(data=np.zeros((0, 3)), columns=['word','gender','source']) 
    df["word"] = ('banana', 'banana', 'elephant', 'mouse', 'mouse', 'elephant', 'banana', 'mouse', 'mouse', 'elephant', 'ostrich', 'ostrich') 
    df["gender"] = ('a', 'the', 'the', 'a', 'the', 'the', 'a', 'the', 'a', 'a', 'a', 'the') 
    df["source"] = ('BE', 'BE', 'BE', 'NL', 'NL', 'NL', 'FR', 'FR', 'FR', 'FR', 'FR', 'FR') 

    return create_frequency_list(df) 

def create_frequency_list(df): 
    xtabs = df.groupby(df.columns.tolist()).size() \ 
       .unstack([2, 1]).sort_index(1).fillna(0).astype(int) 

    total = xtabs.stack().sum(1) 
    total.name = 'total' 
    total = total.to_frame().unstack() 

    return pd.concat([total, xtabs], axis=1) 

main() 
+0

Danke. Kannst du mir erklären, wo ich es hinstellen soll? Es hat versucht, alles innerhalb 'create_frequency_list' zu entfernen und es durch deinen Code zu ersetzen, aber ich bekomme den Fehler '' str 'Objekt ist nicht aufrufbar' in der letzten Zeile deines Codes. Könntest du außerdem näher erläutern, was in deinem Code passiert? Wie gesagt, ich bin Anfänger, aber ich bin sehr lernbegierig. –

+0

@BramVanroy Ich habe meinen Beitrag aktualisiert. – piRSquared

+0

Ich kopiere Ihren Code und ich bekomme immer noch den gleichen Fehler. Hier ist [ein Zusammenbruch] (http://pastebin.com/NQwLhp6R). Python ausführen 3.4.3. –

Verwandte Themen