我是Dask的新手,正在寻找一种方法来展平PANDAS数据框中的字典列。这是一个1600万行数据帧的第一行的屏幕截图:
这是来自三行的文本示例:
{{u'F9_07_PZ_COMP_DIRECT': u'0', u'F9_07_PZ_DIRTRSTKEY_NAME': u'DEBRA MEALY', u'F9_07_PZ_COMP_OTHER': u'0', u'F9_07_PZ_COMP_RELATED': u'0', u'F9_07_PZ_TITLE': u'CHAIR PERSON', u'F9_07_PZ_AVE_HOURS_WEEK': u'1.00', u'F9_07_PC_TRUSTEE_INDIVIDUAL': u'X'}, {u'F9_07_PZ_COMP_DIRECT': u'0', u'F9_07_PZ_DIRTRSTKEY_NAME': u'HELEN GORDON', u'F9_07_PZ_COMP_OTHER': u'0', u'F9_07_PZ_COMP_RELATED': u'0', u'F9_07_PZ_TITLE': u'VICE CHAIR', u'F9_07_PZ_AVE_HOURS_WEEK': u'1.00', u'F9_07_PC_TRUSTEE_INDIVIDUAL': u'X'}, {'F9_07_PC_HIGH_COMP_EMPLOYEE': 'X', 'F9_07_PZ_DIRTRSTKEY_NAME': 'ROB S KHANUJA', 'F9_07_PZ_COMP_OTHER': '14902', 'F9_07_PZ_COMP_RELATED': '0', 'F9_07_PZ_TITLE': 'EXEC. DIR. OPERATIONS', 'F9_07_PZ_AVE_HOURS_WEEK': '40.00', 'F9_07_PZ_COMP_DIRECT': '133173'}}
我通常会使用以下代码来平整 Form990PartVIISectionAGrp 列:
df = pd.concat([df.drop(['Form990PartVIISectionAGrp'], axis=1), df['Form990PartVIISectionAGrp'].swifter.apply(pd.Series)], axis=1)
我正在Dask中进行此操作,但出现以下错误:“ ValueError:计算数据中的列与提供的元数据中的列不匹配。”
我正在使用Python 2.7。我导入了相关的软件包
from dask import dataframe as dd
from dask.multiprocessing import get
from multiprocessing import cpu_count
nCores = cpu_count()
为了测试代码,我创建了数据的随机样本:
dfs = df.sample(1000)
然后生成Dask数据框:
ddf = dd.from_pandas(dfs, npartitions=nCores)
该列当前为字符串格式,因此我将其转换为字典。通常,我只写一行代码:
dfs['Form990PartVIISectionAGrp'] = dfs['Form990PartVIISectionAGrp'].apply(literal_eval)
但是我在这里尝试以一种更类似于“ Dask”的形式进行操作,因此我编写了以下函数,然后将其应用:
def make_dict(dfs):
dfs['Form990PartVIISectionAGrp'] = dfs['Form990PartVIISectionAGrp'].apply(literal_eval)
return dfs
ddf_out = ddf.map_partitions(make_dict, meta=dfs[:0]).compute()
这有效-它返回一个PANDAS数据帧,其中Form990PartVIISectionAGrp列采用字典格式(但是,它的速度不比非Dask应用的速度快)。
然后我重新创建Dask DF:
ddf = dd.from_pandas(ddf_out, npartitions=nCores)
并编写一个使列变平的函数:
def flatten(ddf_out):
ddf_out = pd.concat([ddf_out.drop(['Form990PartVIISectionAGrp'], axis=1), ddf_out['Form990PartVIISectionAGrp'].apply(pd.Series)], axis=1)
#ddf_out = ddf_out['Form990PartVIISectionAGrp'].apply(pd.Series)
return ddf_out
如果我随后运行此代码:
result = ddf.map_partitions(flatten)
我得到以下输出,其中列尚未展平:
我也遇到有关丢失元数据的错误,并且鉴于上述内容无助于解析字典列,因此我创建了一个列表,这些列是由普通的Python平展列产生的,并用于创建字典列和数据类型:
metadir = {u'BusinessName': 'O', u'F9_07_PC_FORMER': 'O', u'F9_07_PC_HIGH_COMP_EMPLOYEE': 'O',
u'F9_07_PC_KEY_EMPLOYEE': 'O', u'F9_07_PC_OFFICER': 'O',
u'F9_07_PC_TRUSTEE_INDIVIDUAL': 'O', u'F9_07_PC_TRUSTEE_INSTITUTIONAL': 'O',
u'F9_07_PZ_AVE_HOURS_WEEK': 'O', u'F9_07_PZ_AVE_HOURS_WEEK_RELATED': 'O',
u'F9_07_PZ_COMP_DIRECT': 'O', u'F9_07_PZ_COMP_OTHER': 'O',
u'F9_07_PZ_COMP_RELATED': 'O', u'F9_07_PZ_DIRTRSTKEY_NAME': 'O',
u'F9_07_PZ_TITLE': 'O', u'NameBusiness': 'O', u'URL': 'O'}
然后我将flatten函数与此元数据一起应用:
result = ddf.map_partitions(flatten, meta=metadir)
我得到以下输出结果:
运行result.columns会产生以下结果:
失败的地方是运行compute(),我收到以下错误消息:“ ValueError:计算数据中的列与提供的元数据中的列不匹配。”我是否写同样的错误:
result.compute()
或
result.compute(meta=metadir)
我不确定我在做什么错。结果 中的列似乎与 metadir 中的列匹配。任何建议将不胜感激。
更新: 这是我更新展平功能的过程。
meta = pd.DataFrame(columns=['URL', 'F9_07_PC_TRUSTEE_INDIVIDUAL',
'F9_07_PZ_DIRTRSTKEY_NAME',
'F9_07_PZ_COMP_OTHER',
'F9_07_PZ_COMP_RELATED',
'F9_07_PZ_TITLE',
'F9_07_PZ_AVE_HOURS_WEEK',
'F9_07_PZ_COMP_DIRECT',
'F9_07_PZ_AVE_HOURS_WEEK_RELATED',
'F9_07_PC_OFFICER',
'F9_07_PC_HIGH_COMP_EMPLOYEE',
'BusinessName',
'F9_07_PC_KEY_EMPLOYEE',
'F9_07_PC_TRUSTEE_INSTITUTIONAL',
'NameBusiness',
'F9_07_PC_FORMER'], dtype="O")
def flatten(ddf_out):
ddf_out = pd.concat([df.drop(['Form990PartVIISectionAGrp'], axis=1), df['Form990PartVIISectionAGrp'].apply(pd.Series)], axis=1)
for m in meta:
if m not in ddf_out:
df[m] = ''
return ddf_out
然后我跑:
result = ddf.map_partitions(flatten, meta=meta).compute()
答案 0 :(得分:2)
一些注意事项:
.apply(literal_eval)
像map
一样好吗?
然后我重新创建Dask DF:
ddf = dd.from_pandas(ddf_out,npartitions = nCores)
ddf_out
已经是一个令人迷惑的数据框,我不知道你为什么要这么做。
结果中的列似乎与metadir中的列匹配。
result.columns
的值是从您提供的元数据中获取的,除非您要求,否则不会进行任何计算(在大多数操作中,延迟是懒惰的)。 ValueError异常不提供更多信息吗?
这是一个完整的例子
x = ({'F9_07_PZ_COMP_DIRECT': '0',
'F9_07_PZ_DIRTRSTKEY_NAME': 'DEBRA MEALY',
'F9_07_PZ_COMP_OTHER': '0',
'F9_07_PZ_COMP_RELATED': '0',
'F9_07_PZ_TITLE': 'CHAIR PERSON',
'F9_07_PZ_AVE_HOURS_WEEK': '1.00',
'F9_07_PC_TRUSTEE_INDIVIDUAL': 'X'},
{'F9_07_PZ_COMP_DIRECT': '0',
'F9_07_PZ_DIRTRSTKEY_NAME': 'HELEN GORDON',
'F9_07_PZ_COMP_OTHER': '0',
'F9_07_PZ_COMP_RELATED': '0',
'F9_07_PZ_TITLE': 'VICE CHAIR',
'F9_07_PZ_AVE_HOURS_WEEK': '1.00',
'F9_07_PC_TRUSTEE_INDIVIDUAL': 'X'})
df = pd.DataFrame({'a': x})
d = dd.from_pandas(df, 1)
meta = pd.DataFrame(columns=['F9_07_PZ_COMP_DIRECT',
'F9_07_PZ_DIRTRSTKEY_NAME',
'F9_07_PZ_COMP_OTHER', 'F9_07_PZ_COMP_RELATED', 'F9_07_PZ_TITLE',
'F9_07_PZ_AVE_HOURS_WEEK', 'F9_07_PC_TRUSTEE_INDIVIDUAL'], dtype="O")
d.map_partitions(lambda df: df.a.apply(pd.Series), meta=meta).compute()
我怎么知道要使用什么meta
?我将此功能应用于了熊猫数据框-您可能会使用一小部分数据框来做到这一点。
一些附加说明:
dd.read_csv
之类的东西进行加载,并且最好使用速写函数进行聚合或编写。只有compute()
会很小或不会返回任何东西(因为它涉及写入输出)。官方示例不使用from_pandas。答案 1 :(得分:0)
鉴于中小型数据集,普通的PANDAS解决方案可以工作:
df = pd.concat([df.drop(['Form990PartVIISectionAGrp'], axis=1), df['Form990PartVIISectionAGrp'].apply(pd.Series)], axis=1)
但是,具有1600万行的PANDAS解决方案将无法在具有16GB RAM的Macbook或96GB Windows机器上运行。因此,我看了达斯克。但是,如上面的答案和评论所示,Dask解决方案不起作用,因为我的数据集中的每个观察结果不一定都具有所有字典键。总计, Form990PartVIISectionAGrp 的1600万个观察值具有以下列表中的15个键:
newkeys = ['F9_07_PC_TRUSTEE_INDIVIDUAL',
'F9_07_PZ_DIRTRSTKEY_NAME',
'F9_07_PZ_COMP_OTHER',
'F9_07_PZ_COMP_RELATED',
'F9_07_PZ_TITLE',
'F9_07_PZ_AVE_HOURS_WEEK',
'F9_07_PZ_COMP_DIRECT',
'F9_07_PZ_AVE_HOURS_WEEK_RELATED',
'F9_07_PC_OFFICER',
'F9_07_PC_HIGH_COMP_EMPLOYEE',
'BusinessName',
'F9_07_PC_KEY_EMPLOYEE',
'F9_07_PC_TRUSTEE_INSTITUTIONAL',
'NameBusiness',
'F9_07_PC_FORMER']
因此,我的解决方案涉及采取上述@mdurant提供的一些提示,并首先在每行中添加所有缺少的键:
for index, row in df[:].iterrows():
for k in newkeys:
row['Form990PartVIISectionAGrp'].setdefault(k, np.nan)
在Macbook上花了100分钟。根据mdurant的评论,然后将数据框保存为JSON格式:
df.to_json('df.json', orient='records', lines=True)
然后将文件作为文本读入Dask:
import json
import dask.bag as db
b = db.read_text('df.json').map(json.loads)
然后创建一个将列展平的函数:
def flatten(record):
return {
'F9_07_PZ_COMP_OTHER': record['Form990PartVIISectionAGrp']['F9_07_PZ_COMP_OTHER'],
'F9_07_PZ_COMP_RELATED': record['Form990PartVIISectionAGrp']['F9_07_PZ_COMP_RELATED'],
'F9_07_PC_TRUSTEE_INDIVIDUAL': record['Form990PartVIISectionAGrp']['F9_07_PC_TRUSTEE_INDIVIDUAL'],
'F9_07_PZ_DIRTRSTKEY_NAME': record['Form990PartVIISectionAGrp']['F9_07_PZ_DIRTRSTKEY_NAME'],
'F9_07_PZ_COMP_DIRECT': record['Form990PartVIISectionAGrp']['F9_07_PZ_COMP_DIRECT'],
'F9_07_PZ_COMP_OTHER': record['Form990PartVIISectionAGrp']['F9_07_PZ_COMP_OTHER'],
'BusinessName': record['Form990PartVIISectionAGrp']['BusinessName'],
'F9_07_PC_FORMER': record['Form990PartVIISectionAGrp']['F9_07_PC_FORMER'],
'F9_07_PC_HIGH_COMP_EMPLOYEE': record['Form990PartVIISectionAGrp']['F9_07_PC_HIGH_COMP_EMPLOYEE'],
'F9_07_PC_KEY_EMPLOYEE': record['Form990PartVIISectionAGrp']['F9_07_PC_KEY_EMPLOYEE'],
'F9_07_PC_OFFICER': record['Form990PartVIISectionAGrp']['F9_07_PC_OFFICER'],
'F9_07_PC_TRUSTEE_INSTITUTIONAL': record['Form990PartVIISectionAGrp']['F9_07_PC_TRUSTEE_INSTITUTIONAL'],
'F9_07_PZ_AVE_HOURS_WEEK': record['Form990PartVIISectionAGrp']['F9_07_PZ_AVE_HOURS_WEEK'],
'F9_07_PZ_AVE_HOURS_WEEK_RELATED': record['Form990PartVIISectionAGrp']['F9_07_PZ_AVE_HOURS_WEEK_RELATED'],
'F9_07_PZ_TITLE': record['Form990PartVIISectionAGrp']['F9_07_PZ_TITLE'],
'NameBusiness': record['Form990PartVIISectionAGrp']['NameBusiness'],
'URL': record['URL'],
}
然后我可以应用该功能:
df = b.map(flatten).to_dataframe()
并将数据导出到CSV:
df.to_csv('compensation*.csv')
这就像一种魅力!简而言之,根据上面mdurant的有用评论,关键是1)将缺失的关键添加到所有观察值中; 2)不将数据从PANDAS读入Dask(改用文本或CSV)。照顾好这两个问题可以很好地解决这个问题。