在品味了Pandas数据分析库一段时间后,我已经总结了很长一个有用的代码段的列表,我发现我会一次又一次频繁使用这些代码段。这些小建议可以节省很多用来浏览Pandas文档的时间。
在本文中,我们从将披萨饼订单数据填充dataframe开始。如果你是新接触Pandas,这里给出一些关键的词。
Dataframe 数据中添加了索引的行和列,类似电子表格或者数据库的表。
Series= 数据的单独一列
Axis:0==行,1==列
Shape:Dataframe中(行数,列数)
- 导入CSV文件
对于read_csv函数有一千种选项可以用来简化数据的前处理。没有人想在清洗数据上花太多的时间,所以关键就在于你是否能在导入初始文件的时候就把事情搞定。
df = pd.read_csv('pizza.csv')
需要解析日期?仅仅需要把处理方法传递个相应的列名。
df = pd.read_csv('pizza.csv', parse_dates=['dates'])
只需要某些列?
df = pd.read_csv('pizza.csv', usecols=['foo', 'bar'])
- 在Dataframe中探索数据
你想要做的第一件事可能就是数据是什么样的。这里有几种方法可以检查Pandas的数据。
df.head() #先头5行
df.tail() #最后5行
df.sample(5) #行的随机采样
df.shape #一个元组,包含行列数目
df.describe() #
df.info() #内存使用情况和数据类型
order_number | date | size | topping | price | discount | coupon | |
---|---|---|---|---|---|---|---|
0 | PZZA0001 | 08/21/16 | Small | Anchovies | 12.99 | 3.5 | Yes |
1 | PZZA0000 | 09/26/16 | Large | Pepperoni | 14.50 | 0.0 | No |
2 | PZZA0001 | 09/27/16 | Extra Large | Bell Pepper | 19.99 | 0.0 | No |
3 | PZZA0002 | 09/28/16 | Extra Large | Olives | 20.99 | 5.0 | Yes |
4 | PZZA0003 | 09/29/16 | Extra Large | Pepperoni | 21.99 | 0.0 | No |
披萨饼数据的Dataframe的头几行结果如下:
order_number | date | size | topping | price | discount | coupon | |
---|---|---|---|---|---|---|---|
0 | PZZA0001 | 08/21/16 | Small | Anchovies | 12.99 | 3.5 | Yes |
1 | PZZA0000 | 09/26/16 | Large | Pepperoni | 14.50 | 0.0 | No |
2 | PZZA0001 | 09/27/16 | Extra Large | Bell Pepper | 19.99 | 0.0 | No |
3 | PZZA0002 | 09/28/16 | Extra Large | Olives | 20.99 | 5.0 | Yes |
4 | PZZA0003 | 09/29/16 | Extra Large | Pepperoni | 21.99 | 0.0 | No |
- 在Dataframe中增加一个新列
简单高效的做法就是在Dataframe中定义一个新列。下面的代码给我们一个新列,其中每一行的值都设为23。通常你需要用一个数组或者Series来设定列的内容,这里数组或者Series的长度必须跟数据中的行数相匹配。
df['new_column'] = 23
需要基于其他列来生产一个新列?
full_price = (df.price + df.discount)
df['original_price'] = full_price
需要根据某种顺序来生成列?insert方法中第一个参数是列的位置。下面的代码将列放到Dataframe的开始位置。
df.insert(0, 'original_price', full_price)
- 选择一个具体单元格的值
所谓的单元格这里指位置的是行列交叉所在的数据,跟Excel里的单元格是一个意思。你可能感觉这比较简单,但是具体语法却并不是那么直观。在Pandas中有3个方法同时能做到这件事:.loc, .iloc, .ix——对新接触的人而言,这又更加了理解的难度。
通常我都是用.ix,因为这个方法允许整数和字符串混合使用。首先输入行的索引,然后是列的索引。
df.ix[2, 'topping']
你也可以通过点符号先选择列,然后给定行的索引,这样看起来更清爽:
df.topping.ix[2]
上述的两种方法都会返回单元格的值:
>>> 'Bell Pepper'
- 通过条件逻辑来筛选DataFrame
假设我们需要分析在topping列中包含菠萝的订单。
filtered_data = df[df.topping == 'pineapple']
或者我们要找的是高于一定价格线的订单:
filtered_data = df[df.price > 11.99 ]
如果要同时满足上述两个条件我们怎么办?仅仅将条件包裹成元组并将条件用位运算符连接起来就行。
filtered_data = df[(df.price > 11.99) & (df.topping == 'Pineapple')]
现在我们得到了包含菠萝并且topping价格超过11.99的所有订单。
order_number | date | size | topping | price | discount | coupon |
---|---|---|---|---|---|---|
6 | PZZA0006 | 10/01/16 | Medium Pineapple | 17.50 | 0.0 | No |
9 | PZZA0009 | 10/04/16 | Medium Pineapple | 12.99 | 2.0 | Yes |
- 通过某一列来对DataFrame进行排序
下面的代码意义不言自明,非常有用。
df.sort_values('price', axis=0, ascending=False)
- 对于列中的每行都应用一个函数
Python中匿名lambda函数非常适合这样的任务。假设我们需要用一个自定义的函数来计算DataFrame中每一行的税金。Pandas中的apply函数运行我们将函数传递进来,在列中的每一个值上使用。在这个例子中,我们通过在价格数据上执行一个自定义的函数来抽取出新的税金特征。
def calculate_taxes(price):
taxes = price * 0.12
return taxes
df['taxes'] = df.price.apply(calculate_taxes)
order_number | price | taxes | |
---|---|---|---|
0 | PZZA0000 | 12.99 | 1.5588 |
1 | PZZA0001 | 14.50 | 1.7400 |
2 | PZZA0002 | 19.99 | 2.3988 |
3 | PZZA0003 | 20.99 | 2.5188 |
4 | PZZA0004 | 21.99 | 2.6388 |
- 通过条件逻辑追加一个新列
numpy中的where函数在根据条件逻辑来抽取特征方面非常有用。让我们设想一下披萨店只有在销售额度超过15美元时才有利润。我们根据这个观点创建一个新的列:
df['profitable'] = np.where(df['price']>=15.00, True, False)
- 找出复数列或者行的均值或者标准差
如果你有一个DataFrame,在每列中都是相同类型的数据,比如可能是一定时间内的金融数据,你可能需要找出水平方向上的均值:
df['mean'] = df.mean(axis=1)
或者找出垂直方向上的标准差:
df.std(axis=0)
- 将DataFrame转换成Numpy数组
将DataFrame中的数据转换成数组的方式非常简单:
df.values
如果你想要保留表的形式:
df.as_matrix
- 用连接的方式组合DataFrames
你可以将行或者列连接到一起,唯一的要求就是在相应的轴上具有相同的shape。比如在垂直方向上连接行:
pd.concat([df_1, df_2], axis=0)
或者水平方向上连接列:
pd.concat([df_1, df_2], axis=1)
- 基于一个索引键来组合DataFrames
在Pandas中合并做法跟SQL做法类似。如果你有两个DataFrame拥有共同的键,比如一个披萨的‘order_id‘,你可以进行内连接(inner join),外连接(outer join),左链接(left join),右连接(right join),正如你能用SQL做到的一样。
merged_df = df_1.merge(df_2, how='left', on='order_id')
- 将日期分裂成自己的日,周,月,年列
首先,确认数据时日期格式。然后用dt方法来抽取你需要的数据。
date = pd.to_datetime(df.date)
df['weekday'] = date.dt.weekday
df['year'] = date.dt.year
- 找出DataFrame中的Nan
计算Nan出现的总数:
df.isnull().sum().sum()
列举每个列中的Nan数:
df.isnull().sum()
- 填充Nan或者缺失数据
多数机器学习算法不喜欢Nan这样的值,所以你可能需要对他们进行转换。如果topping列有某些值缺失,我们可以用默认值来填充。
df.topping = df.topping.fillna('Cheese')
或者我们可以在整个DataFrame中丢掉具有缺失值的行:
df = df.dropna(axis=0)
- 通过列的groupby函数抽取特征
对列进行分组是从数据抽取特征的很好的方式。当你的数据可以计数或者可以用某种方式进行量化,分组就特别有用了。例如,你可以根据topping列对披萨分组,然后计算每组中价格的均值。
df.groupby('topping')['discount'].apply(lambda x: np.mean(x))
或者可能你想要看一下某个值出现多少次:
df.groupby('topping')['discount'].apply(lambda x: x.count())
topping
Anchovies 3
Bell Pepper 1
Cheese 2
Olives 1
Pepperoni 3
Pineapple 2
Veggie 1
Name: discount, dtype: int64
- 创建Bins
假设我们要根据不同的价格范围将订单归到3个不同的桶里。这样的做法非常适合讲拥有噪声的数据简化。
bins = [0, 5, 15, 30]
names = ['Cheap', 'Normal', 'Expensive']
df['price_point'] = pd.cut(df.price, bins, labels=names)
order_number | price | price_point | |
---|---|---|---|
0 | PZZA0000 | 12.99 | Normal |
1 | PZZA0001 | 14.50 | Normal |
2 | PZZA0002 | 19.99 | Expensive |
3 | PZZA0003 | 20.99 | Expensive |
4 | PZZA0004 | 21.99 | Expensive |
- 通过循环生成一个新列
假设说我吗想要将披萨配料分成‘蔬菜’和‘肉’两类。处理这样的名称类值可以用一个循环来做到。(注意:你也可以用之前提到的apply函数来完成这个任务。
topping_type = []
for row in df.topping:
if row in ['pepperoni', 'chicken', 'anchovies']:
topping_type.append('meat')
else:
topping_type.append('vegetable')
df['topping_type'] = topping_type
注意:这种方法效率非常低下,应该尽量避免。
种类有限的情况下,应该尽量使用pandas的indexing方法。
比如:
df[df[['toppings'] == 'chicken']['topping_type'] = 'meat'
- 分块方法来读入大规模数据集
有时你可能要处理一个很大的文件,超出了你内存能够处理的范围,让你的程序崩溃。在这种情况下,你可能需要将整个文件分成块来读入。
chunksize = 500
chunks = []
for chunk in pd.read_csv('pizza.csv', chunksize=chunksize):
# Do stuff...
chunks.append(chunk)
df = pd.concat(chunks, axis=0)