既然python归根结底是C语言,那么C语言能否直接使用numpy?
答案是:不可以直接在C语言中使用NumPy,要分情况的。
虽然Python的NumPy库使用C语言实现,但它仍然需要Python解释器来解释和执行Python代码。NumPy提供了Python的高级数据结构和一些用于操作这些数据结构的函数和方法,这些函数和方法都是在Python层次上实现的,
比如数组的创建、形状操作、元素操作等。具体来说,以下方法是在Python层面实现的:
numpy.array
numpy.zeros
numpy.ones
numpy.arange
ndarray.reshape
ndarray.flatten
ndarray.item
ndarray.itemset
numpy.concatenate
这些方法是在Python层次上实现的,因此它们的执行速度相对较慢,也无法在C语言中直接使用,不能满足大规模数据处理的需求。
为了提高执行速度,NumPy还提供了许多基于C语言实现的函数和方法,例如:
numpy.add
numpy.subtract
numpy.multiply
numpy.divide
这些函数是通过C语言编写的,可以充分利用CPU的并行计算能力,因此在大规模数据处理时能够获得更高的执行效率。
如果您想在C语言中使用NumPy中的算法和数据结构,您可以考虑使用NumPy提供的C API。NumPy提供了一组C函数,这些函数可以与NumPy数组进行交互,这些函数包括:
- NPY_NO_EXPORT PyObject *PyArray_SimpleNew(int nd, npy_intp *dims, int typenum);
- NPY_NO_EXPORT void *PyArray_DATA(PyArrayObject *obj);
- NPY_NO_EXPORT npy_intp *PyArray_SHAPE(PyArrayObject *obj);
使用这些函数,您可以将NumPy数组传递给C语言函数,并在C语言中访问和操作它们。但是,这需要一些C语言和NumPy的知识和经验,因此需要具备一定的技术水平和编程能力。
一个C调用numpy的C-API示例
#include <Python.h>
#include <numpy/arrayobject.h>
void c_function(PyArrayObject *arr) {
// 获取数组的形状和元素类型
int ndim = PyArray_NDIM(arr);
npy_intp *shape = PyArray_SHAPE(arr);
int dtype = PyArray_TYPE(arr);
printf("NumPy array with shape (%ld, %ld) and type %d\n",
shape[0], shape[1], dtype);
// 访问数组的元素
npy_float64 *data = PyArray_DATA(arr);
npy_intp i, j;
for (i = 0; i < shape[0]; i++) {
for (j = 0; j < shape[1]; j++) {
npy_float64 val = data[i * shape[1] + j];
printf("%f ", val);
}
printf("\n");
}
}
int main() {
// 初始化Python解释器
Py_Initialize();
import_array();
// 创建NumPy数组
npy_intp dims[] = {2, 3};
PyArrayObject *arr = (PyArrayObject *)PyArray_SimpleNew(2, dims, NPY_FLOAT64);
npy_float64 *data = PyArray_DATA(arr);
data[0] = 1.0; data[1] = 2.0; data[2] = 3.0;
data[3] = 4.0; data[4] = 5.0; data[5] = 6.0;
// 调用C函数,并传递NumPy数组作为参数
c_function(arr);
// 释放数组内存并清理Python解释器
Py_XDECREF(arr);
Py_Finalize();
return 0;
}
一个Cython调用numpy的C-API或Python-API示例
然而,实际上高效的做法,会使用Cython对numpy函数的调用会在纯Python会相多高效的多。
下面这个例子中,使用了Cython来声明一个接受NumPy数组作为参数的函数。在函数中,用Cython的类型声明来获取数组的形状、元素类型和元素值,并进行了一些简单的操作。需要注意的是,Cython使用NumPy的数组切片语法来访问数组的元素,而不是C API中使用指针算术运算的方式。
在main函数中,创建了一个与前面相同的2x3的NumPy数组,并将其作为参数传递给了我们声明的函数。需要注意的是,在使用Cython声明的函数时,不需要像C语言中一样手动初始化和清理Cython底层会调用Python对应的API会自动为我们处理这些问题。
# cython: language_level=3
import numpy as np
cimport numpy as np
def c_function(np.ndarray[np.float64_t, ndim=2] arr):
# 获取数组的形状和元素类型
cdef np.npy_intp_t ndim = arr.ndim
cdef np.npy_intp_t[2] shape = arr.shape
cdef int dtype = arr.dtype.num
print(f"NumPy array with shape ({shape[0]}, {shape[1]}) and type {dtype}")
# 访问数组的元素
cdef np.float64_t *data = &arr[0, 0]
cdef np.npy_intp_t i, j
for i in range(shape[0]):
for j in range(shape[1]):
val = data[i * shape[1] + j]
print(f"{val:.2f} ", end="")
print("")
def main():
# 创建NumPy数组
arr = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], dtype=np.float64)
# 调用C函数,并传递NumPy数组作为参数
c_function(arr)
if __name__ == "__main__":
main()
类似地pandas很多数学函数用到numpy实现的api有纯Py实现的,有原生的C函数接口。但对于巨量的数据集也是力不从心的,这种情况下就要对那些API进行Cython和C函数的二次重构了。答主专长对Python项目的二次开发,Cython性能优化、C扩展功能定制与开发,有技术问题可戳她咨询