Pandas数据处理加速技巧汇总

目录
  • 数据准备
  • 日期时间数据优化
  • 数据的简单循环
  • 循环 .itertuples() 和 .iterrows() 方法
  • .apply() 方法
  • .isin() 数据选择
  • .cut() 数据分箱
  • Numpy 方法处理
  • 处理效率比较
  • HDFStore 防止重新处理

Pandas 处理数据的效率还是很优秀的,相对于大规模的数据集只要掌握好正确的方法,就能让在数据处理时间上节省很多很多的时间。

Pandas 是建立在 NumPy 数组结构之上的,许多操作都是在 C 中执行的,要么通过 NumPy,要么通过 Pandas 自己的 Python 扩展模块库,这些模块用 Cython 编写并编译为 C。理论上来说处理速度应该是很快的。

那么为什么同样一份数据由2个人处理,在设备相同的情况下处理时间会出现天差地别呢?

需要明确的是,这不是关于如何过度优化 Pandas 代码的指南。如果使用得当 Pandas 已经构建为可以快速运行。此外优化和编写干净的代码之间存在很大差异。

这是以 Python 方式使用 Pandas 以充分利用其强大且易于使用的内置功能的指南。

数据准备

此示例的目标是应用分时能源关税来计算一年的能源消耗总成本。 也就是说,在一天中的不同时间,电价会有所不同,因此任务是将每小时消耗的电量乘以消耗该小时的正确价格。

从一个包含两列的 CSV 文件中读取数据,一列用于日期加时间,另一列用于以千瓦时 (kWh) 为单位消耗的电能。

日期时间数据优化

import pandas as pd
df = pd.read_csv('数据科学必备Pandas实操数据处理加速技巧汇总/demand_profile.csv')
df.head()
     date_time  energy_kwh
0  1/1/13 0:00       0.586
1  1/1/13 1:00       0.580
2  1/1/13 2:00       0.572
3  1/1/13 3:00       0.596
4  1/1/13 4:00       0.592

乍一看这看起来不错,但有一个小问题。 Pandas 和 NumPy 有一个 dtypes(数据类型)的概念。 如果未指定任何参数,则 date_time 将采用 object dtype。

df.dtypes
date_time      object
energy_kwh    float64
dtype: object

type(df.iat[0, 0])
str

object 不仅是 str 的容器,而且是任何不能完全适合一种数据类型的列的容器。将日期作为字符串处理会既费力又低效(这也会导致内存效率低下)。为了处理时间序列数据,需要将 date_time 列格式化为日期时间对象数组( Timestamp)。

df['date_time'] = pd.to_datetime(df['date_time'])
df['date_time'].dtype
datetime64[ns]

现在有一个名为 df 的 DataFrame,有两列和一个用于引用行的数字索引。

df.head()
               date_time    energy_kwh
0    2013-01-01 00:00:00         0.586
1    2013-01-01 01:00:00         0.580
2    2013-01-01 02:00:00         0.572
3    2013-01-01 03:00:00         0.596
4    2013-01-01 04:00:00         0.592

使用 Jupyter 自带的 %%time 计时装饰器进行测试。

def convert(df, column_name):
	return pd.to_datetime(df[column_name])

%%time
df['date_time'] = convert(df, 'date_time')

Wall time: 663 ms

def convert_with_format(df, column_name):
	return pd.to_datetime(df[column_name],format='%d/%m/%y %H:%M')

%%time
df['date_time'] = convert(df, 'date_time')

Wall time: 1.99 ms

处理效率提高将近350倍。如果在处理大规模数据的情况下,处理数据的时间会无限的放大。

数据的简单循环

既然日期和时间格式处理完毕,就可以着手计算电费了。成本因小时而异,因此需要有条件地将成本因素应用于一天中的每个小时。

在此示例中,使用时间成本将定义成三个部分。

data_type = {
    # 高峰
    "Peak":{"Cents per kWh":28,"Time Range":"17:00 to 24:00"},
    # 正常时段
    "Shoulder":{"Cents per kWh":20,"Time Range":"7:00 to 17:00"},
    # 非高峰
    "Off-Peak":{"Cents per kWh":12,"Time Range":"0:00 to 7:00"},
}

如果价格是一天中每小时每千瓦时 28 美分。

df['cost_cents'] = df['energy_kwh'] * 28

               date_time    energy_kwh       cost_cents
0    2013-01-01 00:00:00         0.586           16.408
1    2013-01-01 01:00:00         0.580           16.240
2    2013-01-01 02:00:00         0.572           16.016
3    2013-01-01 03:00:00         0.596           16.688
4    2013-01-01 04:00:00         0.592           16.576
...

但是成本计算取决于一天中的不同时间。这就是你会看到很多人以意想不到的方式使用 Pandas 的地方,通过编写一个循环来进行条件计算。

def apply_tariff(kwh, hour):
    """计算给定小时的电费"""
    if 0 <= hour < 7:
        rate = 12
    elif 7 <= hour < 17:
        rate = 20
    elif 17 <= hour < 24:
        rate = 28
    else:
        raise ValueError(f'无效时间: {hour}')
    return rate * kwh

def apply_tariff(kwh, hour):
    """计算给定小时的电费"""
    if 0 <= hour < 7:
        rate = 12
    elif 7 <= hour < 17:
        rate = 20
    elif 17 <= hour < 24:
        rate = 28
    else:
        raise ValueError(f'无效时间: {hour}')
    return rate * kwh

def apply_tariff_loop(df):
    energy_cost_list = []
    for i in range(len(df)):
    	# 循环数据直接修改df
        energy_used = df.iloc[i]['energy_kwh']
        hour = df.iloc[i]['date_time'].hour
        energy_cost = apply_tariff(energy_used, hour)
        energy_cost_list.append(energy_cost)

    df['cost_cents'] = energy_cost_list

Wall time: 2.59 s

循环 .itertuples() 和 .iterrows() 方法

Pandas 实际上 for i in range(len(df)) 通过引入 DataFrame.itertuples() 和 DataFrame.iterrows() 方法使语法就可能显得多余,这些都是yield一次一行的生成器方法。

.itertuples() 为每一行生成一个命名元组,行的索引值作为元组的第一个元素。 名称元组是来自 Python 集合模块的数据结构,其行为类似于 Python 元组,但具有可通过属性查找访问的字段。

.iterrows() 为 DataFrame 中的每一行生成 (index, Series) 对(元组)。

def apply_tariff_iterrows(df):
    energy_cost_list = []
    for index, row in df.iterrows():
        energy_used = row['energy_kwh']
        hour = row['date_time'].hour
        energy_cost = apply_tariff(energy_used, hour)
        energy_cost_list.append(energy_cost)
    df['cost_cents'] = energy_cost_list

%%time
apply_tariff_iterrows(df)

Wall time: 808 ms

速度提高又3倍之多。

.apply() 方法

可以使用 .apply() 方法进一步改进此操作。 Pandas 的 .apply() 方法采用函数(可调用对象)并将它们沿 DataFrame 的轴(所有行或所有列)应用。

lambda 函数将两列数据传递给 apply_tariff()。

def apply_tariff_withapply(df):
    df['cost_cents'] = df.apply(
        lambda row: apply_tariff(
            kwh=row['energy_kwh'],
            hour=row['date_time'].hour),
        axis=1)

%%time
apply_tariff_withapply(df)

Wall time: 181 ms

.apply() 的语法优势很明显,代码简洁、易读、明确。在这种情况下所用时间大约是该 .iterrows() 方法的4分之一。

.isin() 数据选择

但是如何在 Pandas 中将条件计算应用为矢量化操作呢?一个技巧是根据的条件选择和分组 DataFrame 的部分,然后对每个选定的组应用矢量化操作。

使用 Pandas 的.isin()方法选择行,然后在矢量化操作中应用。在执行此操作之前,如果将 date_time 列设置为 DataFrame 的索引会更方便。

df.set_index('date_time', inplace=True)

def apply_tariff_isin(df):
    peak_hours = df.index.hour.isin(range(17, 24))
    shoulder_hours = df.index.hour.isin(range(7, 17))
    off_peak_hours = df.index.hour.isin(range(0, 7))

    df.loc[peak_hours, 'cost_cents'] = df.loc[peak_hours, 'energy_kwh'] * 28
    df.loc[shoulder_hours,'cost_cents'] = df.loc[shoulder_hours, 'energy_kwh'] * 20
    df.loc[off_peak_hours,'cost_cents'] = df.loc[off_peak_hours, 'energy_kwh'] * 12

%%time
apply_tariff_isin(df)

Wall time: 53.5 ms

其中整个过程方法返回一个布尔列表。

[False, False, False, ..., True, True, True]

.cut() 数据分箱

设置时间切分的列表和对那个计算的函数公式,让操作起来更简单,但是这个对于新手来说代码阅读起来有些困难。

def apply_tariff_cut(df):
    cents_per_kwh = pd.cut(x=df.index.hour,
                           bins=[0, 7, 17, 24],
                           include_lowest=True,
                           labels=[12, 20, 28]).astype(int)
    df['cost_cents'] = cents_per_kwh * df['energy_kwh']

%%time
apply_tariff_cut(df)

Wall time: 2.99 ms

Numpy 方法处理

Pandas Series 和 DataFrames 是在 NumPy 库之上设计的。这为提供了更大的计算灵活性,因为 Pandas 可以与 NumPy 数组和操作无缝协作。

使用 NumPy 的digitize()函数。它与 Pandas 的相似之处cut()在于数据将被分箱,但这次它将由一个索引数组表示,该数组表示每个小时属于哪个箱。然后将这些索引应用于价格数组。

import numpy as np

def apply_tariff_digitize(df):
    prices = np.array([12, 20, 28])
    bins = np.digitize(df.index.hour.values, bins=[7, 17, 24])
    df['cost_cents'] = prices[bins] * df['energy_kwh'].values

%%time
apply_tariff_digitize(df)

Wall time: 1.99 ms

处理效率比较

对比一下上面几种不同的处理方式的效率吧。

功能 运行时间(秒)
apply_tariff_loop() 2.59 s
apply_tariff_iterrows() 808 ms
apply_tariff_withapply() 181 ms
apply_tariff_isin() 53.5 ms
apply_tariff_cut() 2.99 ms
apply_tariff_digitize() 1.99 ms

HDFStore 防止重新处理

通常构建复杂的数据模型时,对数据进行一些预处理会很方便。如果有 10 年的分钟频率用电量数据,即指定了格式参数简单地将日期和时间转换为日期时间也可能需要 20 分钟。只需要这样做一次而不是每次运行模型时都需要进行测试或分析。

可以在这里做的一件非常有用的事情是预处理,然后以处理后的形式存储数据,以便在需要时使用。但是如何才能以正确的格式存储数据而无需再次重新处理呢?如果要保存为 CSV 只会丢失您的日期时间对象,并且在再次访问时必须重新处理它。

Pandas 有一个内置的解决方案使用 HDF5,一种专为存储表格数据数组而设计的高性能存储格式。Pandas 的 HDFStore 类允许将 DataFrame 存储在 HDF5 文件中,以便可以有效地访问它,同时仍保留列类型和其他元数据。dict 是一个类似字典的类,因此可以像对 Python对象一样进行读写。

将预处理的耗电量 DataFrame 存储df在 HDF5 文件中。

data_store = pd.HDFStore('processed_data.h5')

# 将 DataFrame 放入对象中,将键设置为 preprocessed_df
data_store['preprocessed_df'] = df
data_store.close()

从 HDF5 文件访问数据的方法,并保留数据类型。

data_store = pd.HDFStore('processed_data.h5')

preprocessed_df = data_store['preprocessed_df']
data_store.close()

到此这篇关于Pandas数据处理加速技巧汇总的文章就介绍到这了,更多相关Pandas数据处理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • python pandas数据处理教程之合并与拼接

    目录 前言 一.join 1.leftjoin 2.rightjoin 3.innerjoin 4.outjoin 二.merge 三.concat 1.纵向合并 2.横向合并 四.append 1.同结构数据追加 2.不同结构数据追加 3.追加合并多个数据集 五.combine_first 六.update 总结 前言 在许多应用中,数据可能来自不同的渠道,在数据处理的过程中常常需要将这些数据集进行组合合并拼接,形成更加丰富的数据集.pandas提供了多种方法完全可以满足数据处理的常用需求.具

  • 让你一文弄懂Pandas文本数据处理

    目录 前言 1. 文本数据类型 1.1. 类型简介 1.2. 类型差异 2. 字符串方法 2.1. 文本格式 2.2. 文本对齐 2.3. 计数与编码 2.4. 格式判断 3. 文本高级操作 3.1. 文本拆分 3.2. 文本替换 3.3. 文本拼接 3.4. 文本匹配 3.5. 文本提取 总结 前言 日常工作中我们经常接触到一些文本类信息,需要从文本中解析出数据信息,然后再进行数据分析操作. 而对文本类信息进行解析是一件比较头秃的事情,好巧,Pandas刚好对这类文本数据有比较好的处理方法,那

  • Python Pandas读取Excel日期数据的异常处理方法

    目录 异常描述 出现原因 解决方案:修改自定义格式 pandas直接解析Excel数值为日期 总结 异常描述 有时我们的Excel有一个调整过自定义格式的日期字段: 当我们用pandas读取时却是这样的效果: 不管如何指定参数都无效. 出现原因 没有使用系统内置的日期单元格格式,自定义格式没有对负数格式进行定义,pandas读取时无法识别出是日期格式,而是读取出单元格实际存储的数值. 解决方案:修改自定义格式 可以修改为系统内置的自定义格式: 或者在自定义格式上补充负数的定义: 增加;@即可 p

  • Pandas 多进程处理数据提高速度

    目录 1.单进程 2.多进程 前言: python 有自己的多进包 multiprocessing 去实现并行计算,但在Pandas处理数据中,使用 multiprocessing 并不好使,只听见风扇转啊转,就不见运行完毕. 为了提高一点数据清洗的速度,找到一个Pandas多进程的方法,pandarallel 库,做了一下测试.下面来看看具体过程吧 [注]文末提供技术交流方式 小数据集(先试过了1w)可能多进程还没单进程快,因为进程开启关闭也要一点时间.于是我弄了 100w 数据来测试: 利用

  • Python数据处理的26个Pandas实用技巧总结

    目录 从剪贴板中创建DataFram 将DataFrame划分为两个随机的子集 多种类型过滤DataFrame DataFrame筛选数量最多类别 处理缺失值 一个字符串划分成多列 Series扩展成DataFrame 对多个函数进行聚合 聚合结果与DataFrame组合 选取行和列的切片 MultiIndexedSeries重塑 创建数据透视表 连续数据转类别数据 StyleaDataFrame 额外技巧 ProfileaDataFrame 大家好,今天给大家分享一篇 pandas 实用技巧,

  • Pandas数据处理加速技巧汇总

    目录 数据准备 日期时间数据优化 数据的简单循环 循环 .itertuples() 和 .iterrows() 方法 .apply() 方法 .isin() 数据选择 .cut() 数据分箱 Numpy 方法处理 处理效率比较 HDFStore 防止重新处理 Pandas 处理数据的效率还是很优秀的,相对于大规模的数据集只要掌握好正确的方法,就能让在数据处理时间上节省很多很多的时间. Pandas 是建立在 NumPy 数组结构之上的,许多操作都是在 C 中执行的,要么通过 NumPy,要么通过

  • Pandas提高数据分析效率的13个技巧汇总

    目录 1.计算变量缺失率 2.获取分组里最大值所在的行方法 3.多列合并为一行 4.删除包含特定字符串所在的行 5.组内排序 6.选择特定类型的列 7.字符串转换为数值 8.优化 DataFrame 对内存的占用 9.根据最大的类别筛选 DataFrame 10.把字符串分割为多列 11.把 Series 里的列表转换为 DataFrame 12.用多个函数聚合 13.分组聚合 1.计算变量缺失率 df=pd.read_csv('titanic_train.csv') def missing_c

  • JS 模态对话框和非模态对话框操作技巧汇总

    模态窗口 javascript 技巧汇总(传值.打开.刷新) 1.要弹出的页面中,一定要保证<head></head>标签间有<base target="_self">,否则会弹出的模态窗口上,点击按钮时,会再次弹出一个新页面. 2.被弹出页面的按钮的事件处理中,应该有Response.Write(new Function().ClosePage());语句,用以关闭当前的模态窗口. 3.因为幽默的缓存原因,如果你在模态窗口中修改了数据,你会发现,父

  • PHP常用技巧汇总

    本文为大家分享了多个php常用技巧,供大家参考,具体内容如下 1.PHP文件读取函式 //文件读取函式 function PHP_Read($file_name) { $fd=fopen($file_name,r); while($bufline=fgets($fd, 4096)){ $buf.=$bufline; } fclose($fd); return $buf; } ?> 2.文件写入函式 //文件写入函式 function PHP_Write($file_name,$data,$met

  • pandas数据处理基础之筛选指定行或者指定列的数据

    pandas主要的两个数据结构是:series(相当于一行或一列数据机构)和DataFrame(相当于多行多列的一个表格数据机构). 本文为了方便理解会与excel或者sql操作行或列来进行联想类比 1.重新索引:reindex和ix 上一篇中介绍过数据读取后默认的行索引是0,1,2,3...这样的顺序号.列索引相当于字段名(即第一行数据),这里重新索引意思就是可以将默认的索引重新修改成自己想要的样子. 1.1 Series 比方说:data=Series([4,5,6],index=['a',

  • Pandas 数据处理,数据清洗详解

    如下所示: # -*-coding:utf-8-*- from pandas import DataFrame import pandas as pd import numpy as np """ 获取行列数据 """ df = DataFrame(np.random.rand(4, 5), columns=['A', 'B', 'C', 'D', 'E']) print df print df['col_sum'] = df.apply(lam

  • pandas数据处理之绘图的实现

    Pandas是Python中非常常用的数据处理工具,使用起来非常方便.它建立在NumPy数组结构之上,所以它的很多操作通过NumPy或者Pandas自带的扩展模块编写,这些模块用Cython编写并编译到C,并且在C上执行,因此也保证了处理速度. 今天我们就来体验一下它的强大之处. 1.创建数据 使用pandas可以很方便地进行数据创建,现在让我们创建一个5列1000行的pandas DataFrame: mu1, sigma1 = 0, 0.1 mu2, sigma2 = 0.2, 0.2 n

  • Android使用EditText小技巧汇总

    1.隐藏android中EditText自带的的下划线 android:background="@null" 或android:background="@/drawable/bg_edittext_norma.xml" bg_edittext_norma.xml <?xml version="1.0" encoding="UTF-8"?> <shape xmlns:android="http://s

  • nodejs代码执行绕过的一些技巧汇总

    在php中,eval代码执行是一个已经被玩烂了的话题,各种奇技淫巧用在php代码执行中来实现bypass.这篇文章主要讲一下nodejs中bypass的一些思路. 1. child_process 首先介绍一下nodejs中用来执行系统命令的模块child_process.Nodejs通过使用child_process模块来生成多个子进程来处理其他事物.在child_process中有七个方法它们分别为:execFileSync.spawnSync,execSync.fork.exec.exec

  • pandas数据处理清洗实现中文地址拆分案例

    目录 一.案例场景 二.初步方案 三.优化方案 一.案例场景 字段login_place,一共267725行记录,随机15条记录如下:   后续数据分析工作需要用到地理维度进行分析,所以需要把login_place字段进行拆分成:国家.省份.地区. 二.初步方案   第三方中文分词库:jieba,可以对文本进行拆分.使用参考资料:jieba库的使用. 初步方案: 用jieba.cut()将文本拆分为单词列表list_word; 分支判断list_word长度,赋值国家.城市.地区. 代码:(抽取

随机推荐