1

To create a pandas.Series from a generic object, the following can be used:

import datetime

class Test(object):
    def __init__(self):
        self.creation = datetime.datetime.now()

a = Test()
c = pandas.Series(a.__dict__)

This results in a Series that is described as

creation   2017-12-17 09:51:48.157503
dtype: datetime64[ns]

This failed to work if the object contains "attributes" created using @property, like so

class Test(object):
    def __init__(self):
        self.creation = datetime.datetime.now()

    @property
    def as_string(self):
        return 'Test at {}'.format(self.creation)

a = Test()
c = pandas.Series(a.__dict__) # same as above

How can the object's properties be included in the Series? Or is there an easier way to create a Series from a generic object, including its properties?

serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • Think about this semantically... how useful would it be to put these disparate properties into a single series? – cs95 Dec 17 '17 at 09:05
  • @cᴏʟᴅsᴘᴇᴇᴅ: this is supposed to be a MCVE. In reality, it's a bit more complicated: there is a list of data objects with properties. These should be included in a dataframe. – serv-inc Dec 17 '17 at 09:07
  • 1
    Oh... well, here's a filthy way of doing it: `pd.Series([getattr(a, x) for x in dir(a) if not x.startswith('__')])`. Can't think of a better way. – cs95 Dec 17 '17 at 09:09
  • @cᴏʟᴅsᴘᴇᴇᴅ:thank you. Now all that's missing is to get rid of those ` – serv-inc Dec 17 '17 at 09:16
  • 1
    See here: https://stackoverflow.com/q/624926/4909087 And modify your if statement a bit. – cs95 Dec 17 '17 at 09:17
  • 1
    By the way, keep in mind that @property is actually an attribute of the class, not the instance, (for example, try `Test.__dict__` and see what comes out). – cs95 Dec 17 '17 at 09:17

1 Answers1

1

I would probably do something like this:

def data_properties(obj):
    def _props(obj):
        for attr in dir(obj):
            if attr.startswith('__'):
                continue
            val = getattr(obj, attr)
            if not callable(val):
                yield (attr, val)
    return dict(_props(obj))

You can use it this way:

import datetime
import pandas


class Foo(object):
    def __init__(self):
        self.x = datetime.datetime.now()

    @property
    def y(self):
        return 5

    def method(self, x):
        return self.y * x


f = Foo()
pd.Series(data_properties(f))
# x    2017-12-17 07:09:20.694000
# y                             5
# dtype: object

The benefit of the approach is that the _props generator can be easily modified to fine-tune the results if you have other constraints on what you'd like to have in your resulting series.

jakevdp
  • 77,104
  • 11
  • 125
  • 160