Объединение кросс-таблиц в Python

Я пытаюсь объединить несколько кросс-таблиц в один. Обратите внимание, что предоставленные данные, очевидно, предназначены только для тестирования. Фактические данные намного больше, поэтому эффективность для меня очень важна.

Кросс-таблицы генерируются, перечисляются, а затем объединены с лямбда-функцией в столбце word. Однако результат этого слияния - это не то, что я ожидаю. Я думаю, проблема заключается в том, что столбцы с только значениями N кросс-таблицы отбрасываются даже при использовании dropna = False, что приведет к ошибке функции merge. Сначала я покажу код и после этого получу промежуточные данные и ошибки.

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()

Индивидуальные кросс-таблицы: не так, как ожидалось. Столбцы NA отбрасываются

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

Это означает, что dataframes не разделяют между собой все значения, которые, в свою очередь, могут испортить слияние.

Объединить: не так, как ожидалось, очевидно

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

Однако ошибка возникает только при назначении столбцов:

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

Итак, насколько я могу судить о том, что проблема начинается рано, с НС и все это проваливается. Однако, поскольку я недостаточно опытен в Python, я не знаю точно.

Я ожидал, что это результат с несколькими индексами:

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
1 ответ

Я просто решил дать вам лучший способ получить то, что вы хотите:

Я использую df.groupby([col1, col2]).size().unstack() для прокси-сервера как мой pd.crosstab как общее правило. Вы пытались сделать кросс-таблицу для каждой группы source. Я могу подойти так хорошо с моей существующей группой с помощью df.groupby([col1, col2, col3]).size().unstack([2, 1])

sort_index(1).fillna(0).astype(int) - это просто все.

Если вы хотите понять еще лучше. Попробуйте следующее и посмотрите, что вы получаете:

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

unstack и stack - это удобные способы получить вещи, которые были в индексе, в столбцы вместо этого и наоборот. unstack([2, 1]) задает порядок, в котором индексы выравниваются.

Наконец, я снова беру мои xtabs и stack и суммирую их по строкам и unstack до prep до 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)

Ваш код должен выглядеть следующим образом:

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()

licensed under cc by-sa 3.0 with attribution.