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