让我们举例来说,我在我的代码中使用了很多pandas.DataFrames
。这是一个非常大的类,有很多部分,它有一个非常类似于函数的API,你倾向于将它的方法调用链接在一起。
感觉能够轻松地为现有类添加功能,同时仍保持功能强大,流畅的API,这样会很好。
我希望将此类转换为特定于域的内容,以便删除在使用此类时经常使用的许多特定于域的样板代码。
让我们说在我的梦中我能够做到这样的事情:
pandas.read_csv('sales.csv') \
.filter(items=['one', 'three']) \
.apply(myTransformationFunction) \
.saveToHivePartition(tablename = 'sales', partitionColumn = 'four') \
.join(pandas.read_csv('employees.csv')) \
.filter(items=['one','three','five']) \
.saveToHivePartition(tablename = 'EmployeeMetrics', partitionColumn = 'ten')
在这个例子中,saveToHivePartition
是一个自定义方法,可以在正确的位置以原子方式将某些内容保存到HDFS,然后将该信息添加到hive metadatastore中。超级有用!
显然,“简单”的答案是简单地创建一个独立的函数,我可以将DataFrame
对象与我需要的其他参数一起传递。每次我想执行保存时,我都需要将数据框封装到一个变量中,然后将该变量传递给一个单独的函数。没有上面的例子那么干净!
那么接下来要想到的是创建一个很酷的新SuperDataFrame
类,它是DataFrame
的超集。这种方法的唯一问题是:你如何构建它?我无法改变pandas.read_csv()
开始返回我的新班级。我不能将基类强制转换为子类。我可以让SuperDataFrame
成为...数据帧类的包装器?但是我觉得任何时候我想调用一个基础DataFrame
函数,它必须是这样的:
SuperDataFrame(pandas.read_csv('sales.csv')) \
.df.filter .... \
.df.apply .... \
.saveToHivePartition .... \
.df.join .... \
.df.filter .... \
.saveToHivepartition ....
我甚至认为这不起作用,因为这假设每当pandas执行一个函数时,就会异常改变DataFrame
,我很确定它甚至不会这样做。
有什么想法吗?或者这只是一件坏事吗?
答案 0 :(得分:1)
您可以使用constructor-override properties确保pandas操作返回新类的实例。一个简单的例子:
class MyDF(pandas.DataFrame):
@property
def _constructor(self):
return MyDF
def myMethod(self):
return "I am cool"
>>> d = MyDF([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=["A", "B", "C"])
>>> d.filter(["A", "B"]).apply(lambda c: c**2).myMethod()
'I am cool'
当然,如果您还希望能够处理Series,那么您还必须创建自己的Series子类并可能定义custom data slots。
那就是说,我不清楚你的例子真的值得这样保证。你已经可以在普通的DataFrame上链接方法调用了,所以如果你想要做的只是保存一些中间阶段,那就不那么繁重了:
data = pandas.read_csv('sales.csv') \
.filter(items=['one', 'three', 'five']) \
.apply(myTransformationFunction)
data2 = data.join(pandas.read_csv('employees.csv')) \
.filter(items=['one', 'three'])
saveToHivePartition(data, tablename='sales', partitionColumn='four')
saveToHivePartition(data2, tablename='EmployeeMetrics', partitionColumn='ten')
(由于我在问题评论中提到的问题,我改变了过滤器的顺序。首先过滤到较小的子集而稍后过滤较大的子集是没有意义的;较大的子集赢了&# 39;稍后会因为其中一些已经过滤掉了。)
将事物作为方法链写作的能力本身并不具有优势。我会特别怀疑像.saveToHivePartition
这样的链接方法,这些方法可能只是因为它们的副作用而被调用。当链接方法以流水线方式运行时,它们是有意义的,每个方法都接受前一个方法的输入并修改它以传递给下一个方法。但是,让方法只产生副作用并返回未更改的对象并不会使代码更具可读性。
另请注意,此解决方案特定于熊猫。通常,如果某些库中的类创建了彼此的实例,则必须仔细设计库以允许以保留类之间关系的方式进行子类化。 Pandas用我描述的构造函数覆盖机制完成了这个,但它并不总是这样,在此之前,很难做到你想要做的事情。
答案 1 :(得分:0)
一种方法是扩展DataFrame
类。为了保持流畅的界面,您可以创建自己的custom.read_csv
def read_csv(file):
dataframe = pandas.read_csv(file)
return SuperDataFrame(dataframe)
首先调用该函数,其余的函数调用保持不变。您只需在新数据框中添加所需的功能即可。
或者(特定于python的hack),您可以将自己的函数 monkey-patch 直接导入到导入它的模块中的原始数据框类中,但不要......