在 Pandas Dataframe 中保存其他属性

Posted

技术标签:

【中文标题】在 Pandas Dataframe 中保存其他属性【英文标题】:Save additional attributes in Pandas Dataframe 【发布时间】:2015-05-21 15:39:52 【问题描述】:

我记得在我使用结构化数组的 MatLab 日子里,您可以将不同的数据存储为主要结构的属性。比如:

a = 
a.A = magic(10)
a.B = magic(50); etc.

其中a.Aa.B 彼此完全分开,允许您在a 中存储不同的类型并根据需要对其进行操作。 Pandas 允许我们做类似的事情,但并不完全相同。

我正在使用 Pandas 并希望存储数据帧的属性,而不是将其实际放入数据帧中。这可以通过以下方式完成:

import pandas as pd

a = pd.DataFrame(data=pd.np.random.randint(0,100,(10,5)),columns=list('ABCED')

# now store an attribute of <a>
a.local_tz = 'US/Eastern'

现在,本地时区存储在 a 中,但我在保存数据帧时无法保存此属性(即重新加载 a 后没有 a.local_tz)。有没有办法保存这些属性?

目前,我只是在数据框中创建新列来保存时区、纬度、经度等信息,但这似乎有点浪费。此外,当我对数据进行分析时,我遇到了必须排除这些其他列的问题。

################## 开始编辑 ##################

根据 unutbu 的建议,我现在以 h5 格式存储数据。如前所述,将元数据作为数据框的属性重新加载是有风险的。但是,由于我是这些文件(和处理算法)的创建者,我可以选择哪些存储为元数据,哪些不存储。在处理将进入 h5 文件的数据时,我选择将元数据存储在一个字典中,该字典被初始化为我的类的属性。我做了一个简单的 IO 类来导入 h5 数据,并将元数据作为类属性。现在我可以处理我的数据帧,而不会丢失元数据。

class IO():
    def __init__(self):
        self.dtfrmt = 'dummy_str'

    def h5load(self,filename,update=False):
        '''h5load loads the stored HDF5 file.  Both the dataframe (actual data) and 
        the associated metadata are stored in the H5file

        NOTE: This does not load "any" H5 
        file, it loads H5 files specifically created to hold dataframe data and 
        metadata.

        When multi-indexed dataframes are stored in the H5 format the date 
        values (previously initialized with timezone information) lose their
        timezone localization.  Therefore, <h5load> re-localizes the 'DATE' 
        index as UTC.

        Parameters
        ----------
        filename : string/path
            path and filename of H5 file to be loaded.  H5 file must have been 
            created using <h5store> below.

        udatedf : boolean True/False
            default: False
            If the selected dataframe is to be updated then it is imported 
            slightly different.  If update==True, the <metadata> attribute is
            returned as a dictionary and <data> is returned as a dataframe 
            (i.e., as a stand-alone dictionary with no attributes, and NOT an 
            instance of the IO() class).  Otherwise, if False, <metadata> is 
            returned as an attribute of the class instance.

        Output
        ------
        data : Pandas dataframe with attributes
            The dataframe contains only the data as collected by the instrument.  
            Any metadata (e.g. timezone, scaling factor, basically anything that
            is constant throughout the file) is stored as an attribute (e.g. lat 
            is stored as <data.lat>).'''

        with pd.HDFStore(filename,'r') as store:
            self.data = store['mydata']
            self.metadata = store.get_storer('mydata').attrs.metadata    # metadata gets stored as attributes, so no need to make <metadata> an attribute of <self>

            # put metadata into <data> dataframe as attributes
            for r in self.metadata:
                setattr(self,r,self.metadata[r])

        # unscale data
        self.data, self.metadata = unscale(self.data,self.metadata,stringcols=['routine','date'])

        # when pandas stores multi-index dataframes as H5 files the timezone
        # initialization is lost.  Remake index with timezone initialized: only
        # for multi-indexed dataframes
        if isinstance(self.data.index,pd.core.index.MultiIndex):
            # list index-level names, and identify 'DATE' level
            namen = self.data.index.names
            date_lev = namen.index('DATE')

            # extract index as list and remake tuples with timezone initialized
            new_index = pd.MultiIndex.tolist(self.data.index)
            for r in xrange( len(new_index) ):
                tmp = list( new_index[r] )
                tmp[date_lev] = utc.localize( tmp[date_lev] )

                new_index[r] = tuple(tmp)

            # reset multi-index
            self.data.index = pd.MultiIndex.from_tuples( new_index, names=namen )


        if update:
            return self.metadata, self.data
        else:
            return self





    def h5store(self,data, filename, **kwargs):
        '''h5store stores the dataframe as an HDF5 file.  Both the dataframe 
        (actual data) and the associated metadata are stored in the H5file

        Parameters
        ----------
        data : Pandas dataframe NOT a class instance
            Must be a dataframe, not a class instance (i.e. cannot be an instance 
            named <data> that has an attribute named <data> (e.g. the Pandas 
            data frame is stored in data.data)).  If the dataframe is under
            data.data then the input variable must be data.data.

        filename : string/path
            path and filename of H5 file to be loaded.  H5 file must have been 
            created using <h5store> below.

        **kwargs : dictionary
            dictionary containing metadata information.


        Output
        ------
        None: only saves data to file'''

        with pd.HDFStore(filename,'w') as store:
            store.put('mydata',data)
            store.get_storer('mydata').attrs.metadata = kwargs

然后通过data = IO().h5load('filename.h5') 加载H5 文件 数据框存储在 data.data 下 我将元数据字典保留在 data.metadata 下,并创建了单独的元数据属性(例如 data.lat 创建自 data.metadata['lat'])。

我的索引时间戳本地化为pytz.utc()。但是,当多索引数据帧存储到 h5 时,时区定位会丢失(使用 Pandas 15.2),所以我在 IO().h5load 中对此进行了更正。

【问题讨论】:

虽然属性可以添加到 df 中,但它们不会被复制,即使你做了df.copy() 所以你必须使用另一种方法来存储它 【参考方案1】:

There is an open issue 关于在 NDFrames 中存储自定义元数据。但由于 pandas 函数可能返回 DataFrame 的方式多种多样,_metadata 属性并未(尚未)在所有情况下都保留。

目前,您只需将元数据存储在辅助变量中。

将 DataFrames + 元数据存储到文件有多种选择,具体取决于您希望使用的格式——pickle、JSON、HDF5 都是可能的。

以下是使用 HDF5 存储和加载包含元数据的 DataFrame 的方法。存储元数据的配方来自Pandas Cookbook。

import numpy as np
import pandas as pd

def h5store(filename, df, **kwargs):
    store = pd.HDFStore(filename)
    store.put('mydata', df)
    store.get_storer('mydata').attrs.metadata = kwargs
    store.close()

def h5load(store):
    data = store['mydata']
    metadata = store.get_storer('mydata').attrs.metadata
    return data, metadata

a = pd.DataFrame(
    data=pd.np.random.randint(0, 100, (10, 5)), columns=list('ABCED'))

filename = '/tmp/data.h5'
metadata = dict(local_tz='US/Eastern')
h5store(filename, a, **metadata)
with pd.HDFStore(filename) as store:
    data, metadata = h5load(store)

print(data)
#     A   B   C   E   D
# 0   9  20  92  43  25
# 1   2  64  54   0  63
# 2  22  42   3  83  81
# 3   3  71  17  64  53
# 4  52  10  41  22  43
# 5  48  85  96  72  88
# 6  10  47   2  10  78
# 7  30  80   3  59  16
# 8  13  52  98  79  65
# 9   6  93  55  40   3

print(metadata)

产量

'local_tz': 'US/Eastern'

【讨论】:

太棒了!实际上,当我通过以下方式加载 H5file 时,我可以将属性放回数据框中: for r in metadata: setattr(data,r,metadata[r]) 这似乎使它变得非常灵活,正是我想要的。 是的,但是如果你使用setattr,请小心,因为如果你调用类似data.groupby(...).agg(...)的东西,返回的DataFrame可能会丢弃元数据。【参考方案2】:

我使用的方法是添加额外的MultiIndex 级别来存储我想要的额外信息(我使用列,但任何一个都可以)。对于这些附加参数,所有列都具有相同的值。这也很有用,因为我可以组合多个数据框或拆分单个列,并且这些值被保留。

>>> col=pd.MultiIndex.from_product([['US/Eastern'], ['A', 'B', 'C', 'E', 'D']], names=['local_tz', 'name'])
>>> a = pd.DataFrame(data=pd.np.random.randint(0,100,(10,5)),columns=col)
>>> print(a)
local_tz US/Eastern                
name              A   B   C   E   D
0                38  93  63  24  55
1                21  25  84  98  62
2                 4  60  78   0   5
3                26  50  82  89  23
4                32  70  80  90   1
5                 6  17   8  60  59
6                95  98  69  19  76
7                71  90  45  45  40
8                94  16  44  60  16
9                53   8  30   4  72

【讨论】:

感谢黑猫。这会起作用,尽管我尽量避免多索引(也许对我自己不利)。【参考方案3】:

Pandas 现在有一个属性“attrs”,用于存储元数据。正如documentation 中所述,它是实验性的,将来可能会更改。文档中没有太多其他内容,虽然内容非常有限,但也非常易于访问和使用:

df = pd.DataFrame('fruit':['apple','pear','banana'], 'price':[3,4,2])

df.attrs =  'description':'fruit prices in dollars per pound' 

当您创建“df”时,“df.attrs”被初始化为一个空字典。除此之外,您似乎可以在“df.attrs”中存储您想要的任何内容,只要顶层是字典即可。上面,我只是存储了数据框的描述,但另一种选择是存储列标签(例如,在这里查看我的答案: How to handle meta data associated with a pandas dataframe?)

持久性似乎仅限于少量数据帧操作。例如,“attrs”中的值保留在 copy、loc 和 iloc 中,但不保留在 groupby 中。当然,人们总是可以通过简单的分配重新连接。例如,如果您通过 groupby 操作创建一个名为“grouped_means”的数据框,它不会保留“attrs”,但您可以轻松地重新附加:

grouped_means.attrs = df.attrs

也许这可以在未来版本的 pandas 中实现自动化。所以这是一个非常有限的功能,但非常易于使用,将来可能会改进。

【讨论】:

以上是关于在 Pandas Dataframe 中保存其他属性的主要内容,如果未能解决你的问题,请参考以下文章

对pandas的dataframe绘图并保存

Python将Pandas中Dataframe数据保存为gzip/zip文件:gzip压缩文件zip压缩文件

pandas - 如何仅将 DataFrame 的选定列保存到 HDF5

在 Python 中将函数输出(包括 pandas DataFrame)保存到工作区

将具有相同列/索引的两个 pandas DataFrame 合并为一个 DataFrame

如何有效地将 Pandas Dataframe 保存到一个/多个 TFRecord 文件中?