Python 将熊猫数据框制作成dict和dropna
我有一些熊猫数据框,里面有南。 像这样:Python 将熊猫数据框制作成dict和dropna,python,pandas,Python,Pandas,我有一些熊猫数据框,里面有南。 像这样: import pandas as pd import numpy as np raw_data={'A':{1:2,2:3,3:4},'B':{1:np.nan,2:44,3:np.nan}} data=pd.DataFrame(raw_data) >>> data A B 1 2 NaN 2 3 44 3 4 NaN 现在我想把它写下来,同时去掉NAN。 结果应该如下所示: {'A': {1: 2, 2: 3, 3
import pandas as pd
import numpy as np
raw_data={'A':{1:2,2:3,3:4},'B':{1:np.nan,2:44,3:np.nan}}
data=pd.DataFrame(raw_data)
>>> data
A B
1 2 NaN
2 3 44
3 4 NaN
现在我想把它写下来,同时去掉NAN。
结果应该如下所示:
{'A': {1: 2, 2: 3, 3: 4}, 'B': {2: 44.0}}
>>> data.to_dict()
{'A': {1: 2, 2: 3, 3: 4}, 'B': {1: nan, 2: 44.0, 3: nan}}
但使用pandas to_dict函数会产生如下结果:
{'A': {1: 2, 2: 3, 3: 4}, 'B': {2: 44.0}}
>>> data.to_dict()
{'A': {1: 2, 2: 3, 3: 4}, 'B': {1: nan, 2: 44.0, 3: nan}}
那么,如何从数据帧中生成dict并去掉NAN呢?编写一个函数,该函数由pandas提供
import pandas as pd
import numpy as np
from pandas import compat
def to_dict_dropna(self,data):
return dict((k, v.dropna().to_dict()) for k, v in compat.iteritems(data))
raw_data={'A':{1:2,2:3,3:4},'B':{1:np.nan,2:44,3:np.nan}}
data=pd.DataFrame(raw_data)
dict=to_dict_dropna(data)
结果你得到了你想要的:
>>> dict
{'A': {1: 2, 2: 3, 3: 4}, 'B': {2: 44.0}}
有很多方法可以实现这一点,我花了一些时间在一个不太大(70k)的数据帧上评估性能。尽管@der_die_das_jojojo的答案是实用的,但它也相当缓慢 事实证明,在大数据帧上,由建议的答案大约快5倍 在我的测试数据帧(
df
)上:
上述方法:
%time [ v.dropna().to_dict() for k,v in df.iterrows() ]
CPU times: user 51.2 s, sys: 0 ns, total: 51.2 s
Wall time: 50.9 s
另一个缓慢的方法:
%time df.apply(lambda x: [x.dropna()], axis=1).to_dict(orient='rows')
CPU times: user 1min 8s, sys: 880 ms, total: 1min 8s
Wall time: 1min 8s
我能找到的最快方法:
%time [ {k:v for k,v in m.items() if pd.notnull(v)} for m in df.to_dict(orient='rows')]
CPU times: user 14.5 s, sys: 176 ms, total: 14.7 s
Wall time: 14.7 s
此输出的格式是一个面向行的字典,如果需要问题中面向列的形式,可能需要进行调整
如果有人能更快地找到这个问题的答案,我会非常感兴趣。你可以使用听写理解并循环阅读专栏
{col:df[col].dropna().to_dict() for col in df}
我编写了一个函数来解决这个问题,无需重新实现,也无需多次调用它。该方法是递归地修剪带有nan/None值的“叶子”
def trim_nan_leaf(tree):
"""For a tree of dict-like and list-like containers, prune None and NaN leaves.
Particularly applicable for json-like dictionary objects
"""
# d may be a dictionary, iterable, or other (element)
# * Do not recursively iterate if string
# * element is the base case
# * Only remove nan and None leaves
def valid_leaf(leaf):
if leaf is None:
return(False)
if isinstance(leaf, numbers.Number):
if (not math.isnan(leaf)):
return(leaf != -9223372036854775808)
return(False)
return(True)
# Attempt dictionary
try:
return({k: trim_nan_leaf(tree[k]) for k in tree.keys() if valid_leaf(tree[k])})
except AttributeError:
# Execute base case on string for simplicity...
if isinstance(tree, str):
return(tree)
# Attempt iterator
try:
# Avoid infinite recursion for self-referential objects (like one-length strings!)
if tree[0] == tree:
return(tree)
return([trim_nan_leaf(leaf) for leaf in tree if valid_leaf(leaf)])
# TypeError occurs when either [] or iterator are availble
except TypeError:
# Base Case
return(tree)
第一个图为每列生成字典,所以输出的是一些很长的字典,dict的数量取决于列的数量 我用
perfplot
测试了多种方法,最快的方法是按每列循环,并在较大的数据帧中删除缺少的值或None
s
Is较小的数据帧是通过NaN!=NaN
技巧和测试None
s
另一种情况是,如果每行生成字典-获取大量小字典的列表,则最快的是使用过滤非和非的列表理解:
您可以拥有自己的映射类,在该类中可以摆脱NAN:
class NotNanDict(dict):
@staticmethod
def is_nan(v):
if isinstance(v, dict):
return False
return np.isnan(v)
def __new__(self, a):
return {k: v for k, v in a if not self.is_nan(v)}
data.to_dict(into=NotNanDict)
输出:
{'A': {1: 2, 2: 3, 3: 4}, 'B': {2: 44.0}}
时间安排(来自@jezrael answer):
要提高速度,您可以使用:
输出:
{'A': {1: 2, 2: 3, 3: 4}, 'B': {2: 44.0}}
时间安排(来自@jezrael answer):
对问题答案的改进 对于包含2个完整nan列的~300K数据帧,他的答案是:
%time[{k:v表示k,v表示m.items(),如果pd.notnull(v)}表示m表示df.to_dict(orient='records')]
CPU时间:用户8.63秒,系统137毫秒,总计8.77秒
墙时间:8.79秒
稍微扭转一下:
%time[{k:v代表k,v代表m.items()}代表m代表df.dropna(axis=1)。to_dict(orient='records')]
CPU时间:用户4.37秒,系统109毫秒,总计4.48秒
墙壁时间:4.49秒
其思想是始终先删除nan,以避免对nan值进行不必要的迭代。在第一个答案中,nan在被删除之前首先转换为dict,这可以进行优化。尝试下面的代码
import numpy as np
import pandas as pd
raw_data = {'A': {1: 2, 2: 3, 3: 4}, 'B': {1: np.nan, 2: 44, 3: np.nan}}
data = pd.DataFrame(raw_data)
{col: data[col].dropna().to_dict() for col in data}
输出
{'A': {1: 2, 2: 3, 3: 4}, 'B': {2: 44.0}}
有很多方法可以解决这个问题。根据行数的不同,最快的方法将发生变化。因为性能是相关的,所以我知道行的数量很大
将熊猫作为pd导入
将numpy作为np导入
#使用随机数据创建数据帧
df=pd.DataFrame(np.random.randint(10,size=[1,000,2]),columns=[“A”,“B”])
#添加一些NaN
df.loc[df[“A”]==1,“B”]=np.nan
我得到的最快解决方案是简单地使用dropna
方法和dict理解:
%time {col: df[col].dropna().to_dict() for col in df.columns}
CPU times: user 528 ms, sys: 87.2 ms, total: 615 ms
Wall time: 615 ms
与其中一种建议的解决方案相比,它的速度要快10倍:
现在,如果我们使用其中一个建议的解决方案进行测试,我们会得到:
%time [{k:v for k,v in m.items() if pd.notnull(v)} for m in df.to_dict(orient='rows')]
CPU times: user 5.49 s, sys: 205 ms, total: 5.7 s
Wall time: 5.69 s
它也比其他选项快2倍,例如:
%time {k1: {k:v for k,v in v1.items() if v == v and v is not None} for k1, v1 in df.to_dict().items()}
CPU times: user 900 ms, sys: 133 ms, total: 1.03 s
Wall time: 1.03 s
我们的想法是始终尝试使用pandas
或numpy
内置函数,因为它们比常规python更快。处理非规范化数据帧的最佳解决方案
当单元格确实包含列表或序列(未规范化的数据帧)时,我遇到了问题
我解决了创建自己的isna()
函数的问题:
def is_na(possible_array):
is_not_na = pd.notnull(possible_array)
if isinstance(is_not_na, bool):
return not is_not_na
return not is_not_na.any()
def to_json(df, remove_missing_fields=True):
rows = df.to_dict("records")
if remove_missing_fields:
return [{k:v for k,v in m.items() if not is_na(v)} for m in df.to_dict(orient='records')]
else:
return rows
与其他答案相比,这种方法的时间安排如何?也许我看错了图表:运行时间越高,性能是否越差?紫色线对于大型dfs来说是最慢的,对吗?@RicksupportsMonica是的。你是对的。这一个比较慢。很遗憾,因为我认为这是最可读的方法。@RicksupportsMonica您可以查看我的answer@PeterMularien,你能给我一些提示吗?你是如何做到这一点来检查最佳性能的?hello@PrateshTamhankar:他正在使用一个名为ipython的口译员(您可以自己安装;它也可以在jupyter笔记本中使用)。您在那里看到的%time指令称为“magic”command.See.hmmm,第二种解决方案不是OP需要的,因为它删除了至少有一个NaN的所有列。检查一些示例数据。与其他答案相比,此方法的时间安排如何?我认为这是最好的解决方案,因为它简单易懂。只有当此代码占用总处理时间的很大一部分时,才可以应该考虑优化代码。
def is_na(possible_array):
is_not_na = pd.notnull(possible_array)
if isinstance(is_not_na, bool):
return not is_not_na
return not is_not_na.any()
def to_json(df, remove_missing_fields=True):
rows = df.to_dict("records")
if remove_missing_fields:
return [{k:v for k,v in m.items() if not is_na(v)} for m in df.to_dict(orient='records')]
else:
return rows