合并 Python 中的交叉表
Merging crosstabs in Python
我正在尝试将多个交叉表合并为一个。请注意,提供的数据显然仅用于测试目的。实际数据要大得多,所以效率对我来说很重要。
交叉表生成、列出,然后与 word
列上的 lambda 函数合并。然而,这次合并的结果并不是我所期望的那样。我认为问题在于,即使使用 dropna = False
,交叉表中只有 NA 值的列也会被删除,这将导致 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
这意味着数据帧不会在彼此之间共享所有值,这反过来可能会搞乱合并。
Merge: 不符合预期,很明显
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)
据我所知,问题很早就开始了,与 NA 一起导致整个事情失败。但是,由于我在 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
我只是决定给你一个更好的方法来让你得到你想要的东西:
我通常使用 df.groupby([col1, col2]).size().unstack()
作为我的 pd.crosstab
代理。您试图为 source
的每个组创建交叉表。我可以用 df.groupby([col1, col2, col3]).size().unstack([2, 1])
很好地适应我现有的 groupby
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
求和以准备 pd.concat
。瞧!
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()
我正在尝试将多个交叉表合并为一个。请注意,提供的数据显然仅用于测试目的。实际数据要大得多,所以效率对我来说很重要。
交叉表生成、列出,然后与 word
列上的 lambda 函数合并。然而,这次合并的结果并不是我所期望的那样。我认为问题在于,即使使用 dropna = False
,交叉表中只有 NA 值的列也会被删除,这将导致 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
这意味着数据帧不会在彼此之间共享所有值,这反过来可能会搞乱合并。
Merge: 不符合预期,很明显
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)
据我所知,问题很早就开始了,与 NA 一起导致整个事情失败。但是,由于我在 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
我只是决定给你一个更好的方法来让你得到你想要的东西:
我通常使用 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
求和以准备 pd.concat
。瞧!
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()