在Python和pandas之间选择

2024-10-01 11:36:15 发布

您现在位置:Python中文网/ 问答频道 /正文

我写了一个程序,读取两个.csv文件(它们不是很大,每个文件有几千行),我做了一些数据清理和争论,这是每个.csv文件的最终结构(假数据仅供说明)。在

import pandas as pd
data = [[112233, 'Rob', 99], [445566, 'John', 88]]
managers = pd.DataFrame(data)
managers.columns = ['ManagerId', 'ManagerName', 'ShopId']
print managers

   ManagerId ManagerName  ShopId
0     112233         Rob      99
1     445566        John      88


data = [[99, 'Shop1'], [88, 'Shop2']]
shops = pd.DataFrame(data)
shops.columns = ['ShopId', 'ShopName']
print shops

   ShopId ShopName
0      99    Shop1
1      88    Shop2

data = [[99, 2000, 3000, 4000], [88, 2500, 3500, 4500]]
sales = pd.DataFrame(data)
sales.columns = ['ShopId', 'Year2010', 'Year2011', 'Year2012']
print sales

   ShopId  Year2010  Year2011  Year2012
0      99      2000      3000      4000
1      88      2500      3500      4500

然后我使用xlsxwriterreportlabPython包在迭代数据帧的同时创建自定义的Excel表和.pdf报表。一切看起来都很好,所有命名的包都做得很好。在

但我担心的是,我觉得我的代码很难维护,因为我需要在多个调用中多次访问相同的数据帧行。在

假设我需要得到经理的名字,他们负责的商店在2010年的销售额超过1500。我的代码充满了这种调用:

^{pr2}$

我认为在阅读这行代码时很难看到发生了什么。我可以创建多个中间变量,但这将添加多行代码。在

牺牲数据库规范化思想并将所有部分合并到单个数据帧中以获得更易于维护的代码有多常见?显然有一个单一数据帧的缺点,因为当试图合并以后可能需要的其他数据帧时,它可能会变得混乱。合并它们当然会导致数据冗余,因为同一个管理器可以分配给多个车间。在

df = managers.merge(sales, how='left', on='ShopId').
    merge(shops, how='left', on='ShopId')
print df

   ManagerId ManagerName  ShopId  Year2010  Year2011  Year2012 ShopName
0     112233         Rob      99      2000      3000      4000    Shop1
1     445566        John      88      2500      3500      4500    Shop2

至少这个电话变小了:

df[df['Year2010'] > 1500]['ManagerName'].values
>>> array(['Rob', 'John'], dtype=object)

也许熊猫是做这种工作的一个错误的工具?在

office的C开发人员对我皱眉头,告诉我使用这些类,但是接下来我会有很多方法,比如get_manager_sales(managerid)等等。迭代用于报告的类实例听起来也很麻烦,因为我需要实现一些排序和索引(我可以用pandas免费获得)。在

字典可以工作,但它也使修改现有数据、进行合并等变得困难。语法也没有得到更好的改善。在

data_dict = df.to_dict('records')
[{'ManagerId': 112233L,
  'ManagerName': 'Rob',
  'ShopId': 99L,
  'ShopName': 'Shop1',
  'Year2010': 2000L,
  'Year2011': 3000L,
  'Year2012': 4000L},
 {'ManagerId': 445566L,
  'ManagerName': 'John',
  'ShopId': 88L,
  'ShopName': 'Shop2',
  'Year2010': 2500L,
  'Year2011': 3500L,
  'Year2012': 4500L}]

获得负责2010年销售额超过1500家店铺的经理姓名。在

[row['ManagerName'] for row in data_dict if row['Year2010'] > 1500]
>>> ['Rob', 'John']

在这个我操作数据的特殊情况下,我应该一直使用pandas还是有其他方法在利用pandas的能力的同时编写更干净的代码?


Tags: 数据代码pandasdfdatajohnrobsales
2条回答

创建在数据帧上操作的类不是一个好主意,因为这样做会隐藏您正在使用数据帧的事实,并为非常糟糕的决策打开道路(例如使用for循环迭代数据帧)。在

解决方案1:取消数据规范化。 你不必把你的数据保持在一个正常的形式。当您必须在整个数据库中保持条目的一致性时,最好使用普通形式。这不是一个数据库,你不需要不断地插入、更新和删除。因此,只需对其进行非规范化,并使用一个大的数据帧,因为它显然更方便,更适合您的需要。在

解决方案2:使用数据库。 您可以将数据转储到SQLite数据库(pandas有一个内置函数),并对其执行各种疯狂的查询。以我个人的观点,SQL查询比你发布的内容更具可读性。 如果您定期进行这种分析,并且数据结构保持不变,这可能是一个更好的解决方案。您可以将数据转储到数据库中,然后使用SQLAlchemy处理它。在

解决方案3。创建您自己的数据帧。 您可以从pandas.DataFrame继承并向其添加自定义方法。不过,您需要深入研究pandas的实质,看看如何实现这些方法。例如,您可以创建访问数据帧某些部分的自定义方法。在

除非你非常了解熊猫,否则我会选择解决方案1或2。如果您需要更大的灵活性,并且每次的数据操作都不同,请使用1。如果每次都需要执行大致相同的分析,请使用2(尤其是当数据分析代码是大型应用程序的一部分时)。在

另外,我不明白为什么“添加更多的代码行”是不好的。通过将一个巨大的单行线分解成许多表达式,您不会增加实际复杂度,而是降低了感知的复杂度。也许你只需要重构你的代码,把一些操作打包成可重用的函数?在

我会选择Pandas,因为它更快,有一个非常优秀和非常丰富的API,源代码看起来更干净更好等等

顺便说一句,下面这一行很容易重写:

managers[managers['ShopId'].isin(sales[sales['Year2010'] > 1500]['ShopId'])]['ManagerName'].values

作为:

^{pr2}$

在我看来,它很容易阅读和理解

PS您可能还希望将数据存储在一个SQL的数据库中,并使用SQL或将其存储在HDF store中并使用where参数-在这两种情况下,您都可以从索引“search”列中获益

相关问题 更多 >