首先导入需要的库:
import numpy as np
import pandas as pd
Series
Series是一个一维标记数组,能够保存任何数据类型(整数、字符串、浮点数、Python 对象等)。轴标签统称为索引。创建系列的基本方法是调用:
s = pd.Series(data, index=index)
在这里,data
可以有很多不同的东西:
-
一个 Python 字典
-
一个ndarray
-
标量值(如 5)
传递的索引是轴标签的列表。因此,根据数据是什么,这分为几种情况:
从 ndarray
如果data
是 ndarray,则index必须与data的长度相同。如果没有传递索引,则将创建一个具有 values 的索引。[0, ..., len(data) - 1]
s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])
s
s.index
注意:pandas 支持非唯一索引值。如果尝试不支持重复索引值的操作,则此时将引发异常。
从字典
系列可以从 dicts 实例化:
d = {"b": 1, "a": 0, "c": 2}
pd.Series(d)
注意:
- 当数据是 dict 并且没有传递
Series
索引时,如果您使用 Python 版本 >= 3.6 和 pandas 版本 >= 0.23,则索引将按 dict 的插入顺序排序。 - 如果您使用 Python < 3.6 或 pandas < 0.23,并且未传递索引,则
Series
索引将是 dict 键的词法排序列表。
在上面的示例中,如果您使用的是低于 3.6 的 Python 版本或低于 0.23 的 pandas 版本,Series
则将按 dict 键的词法顺序(即,而不是)排序。['a', 'b', 'c']
['b', 'a', 'c']
如果传递了索引,则将索引中标签对应的数据中的值拉出。
d = {"a": 0.0, "b": 1.0, "c": 2.0}
pd.Series(d, index=["b", "c", "d", "a"])
注意:NaN(不是数字)是 pandas 中使用的标准缺失数据标记。
从标量值
如果data
是标量值,则必须提供索引。该值将重复以匹配index的长度。
pd.Series(5.0, index=["a", "b", "c", "d", "e"])
系列类似于 ndarray
Series
行为与 ndarray
非常相似,并且是大多数 NumPy 函数的有效参数。但是,切片等操作也会对索引进行切片。
与 NumPy 数组一样,pandas Series 具有dtype.
这通常是 NumPy dtype。然而,pandas 和 3rd-party 库在一些地方扩展了 NumPy 的类型系统,在这种情况下,dtype 将是一个ExtensionDtype. pandas 中的一些示例是Categorical data和Nullable integer data type。
虽然 Series 类似于 ndarray,但如果您需要实际的ndarray,请使用 Series.to_numpy().
s.to_numpy()
Series 类似于 dict
Series 就像一个固定大小的 dict,您可以通过索引标签获取和设置值:
如果不包含标签,则会引发异常:
使用该get
方法,缺少的标签将返回 None 或指定的默认值:
向量化操作和标签对齐系列
使用原始 NumPy 数组时,通常不需要逐个值循环。在 pandas 中使用 Series 时也是如此。Series 也可以传递给大多数需要 ndarray 的 NumPy 方法。
Series 和 ndarray 之间的一个关键区别是 Series 之间的操作会根据标签自动对齐数据。因此,您可以编写计算而不考虑所涉及的系列是否具有相同的标签。
未对齐系列之间的操作结果将包含所涉及的索引的并集。如果在一个系列或另一个系列中找不到标签,结果将被标记为缺失NaN
。无需进行任何明确的数据对齐即可编写代码,这为交互式数据分析和研究提供了极大的自由度和灵活性。pandas 数据结构的集成数据对齐功能使 pandas 有别于大多数用于处理标记数据的相关工具。
注意:通常,我们选择使不同索引对象之间的操作的默认结果产生索引的并集,以避免信息丢失。尽管缺少数据,但具有索引标签通常是作为计算的一部分的重要信息。您当然可以选择通过 dropna函数删除缺少数据的标签。
名称属性
系列也可以有一个name
属性:
在许多情况下, Seriesname
将自动分配,特别是在获取 DataFrame 的 1D 切片时,如下所示。
您可以使用该pandas.Series.rename()方法重命名系列。
DataFrame
DataFrame是一种二维标记数据结构,具有可能不同类型的列。您可以将其视为电子表格或 SQL 表,或 Series 对象的字典。它通常是最常用的 pandas 对象。与 Series 一样,DataFrame 接受许多不同类型的输入:
-
1D ndarrays、lists、dicts 或 Series 的字典
-
二维 numpy.ndarray
-
结构化或记录ndarray
-
一种
Series
-
其他
DataFrame
除了数据,您还可以选择传递索引(行标签)和 列(列标签)参数。如果您传递索引和/或列,则保证结果 DataFrame 的索引和/或列。因此,Series 的字典加上特定的索引将丢弃所有与传递的索引不匹配的数据。
注意:
- 如果没有传递轴标签,它们将根据常识规则从输入数据中构造出来。当数据是 dict
columns
且未指定时DataFrame
,如果您使用 Python 版本 >= 3.6 且 pandas >= 0.23,则列将按 dict 的插入顺序排序。 - 如果您使用的是 Python < 3.6 或 pandas < 0.23,并且
columns
未指定,则DataFrame
列将是字典键的词法排序列表。
来自 Series 或 dicts 的 dict
结果索引将是各种系列的索引的并集。如果有任何嵌套的 dicts,这些将首先转换为 Series。如果未传递任何列,则这些列将是 dict 键的有序列表。
d = {
"one": pd.Series([1.0, 2.0, 3.0], index=["a", "b", "c"]),
"two": pd.Series([1.0, 2.0, 3.0, 4.0], index=["a", "b", "c", "d"]),
}
df = pd.DataFrame(d)
df
pd.DataFrame(d, index=["d", "b", "a"])
pd.DataFrame(d, index=["d", "b", "a"], columns=["two", "three"])
通过访问index和columns属性可以分别访问行和列标签 :
注意:当一组特定的列与数据字典一起传递时,传递的列会覆盖字典中的键。
来自 ndarrays / 列表的 dict
ndarrays 必须都是相同的长度。如果传递了索引,它显然也必须与数组的长度相同。如果没有传递索引,则结果将为range(n)
,其中n
是数组长度。
d = {"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]}
d
pd.DataFrame(d)
pd.DataFrame(d, index=["a", "b", "c", "d"])
从结构化或记录数组
这种情况的处理方式与数组的 dict 相同。
data = np.zeros((2, ), dtype=[("A", "i4"), ("B", "f4"), ("C", "a10")])
data
data[:] = [(1, 2.0, "Hello"), (2, 3.0, "World")]
data
pd.DataFrame(data, index=["first", "second"])
pd.DataFrame(data, columns=["C", "A", "B"])
从字典的列表:
data2 = [{"a": 1, "b": 2}, {"a": 5, "b": 10, "c": 20}]
pd.DataFrame(data2)
pd.DataFrame(data2, index=["first", "second"])
pd.DataFrame(data2, columns=["a", "b"])
从元组的字典
可以通过传递元组字典来自动创建 MultiIndexed 框架。
pd.DataFrame(
{
("a", "b"): {("A", "B"): 1, ("A", "C"): 2},
("a", "a"): {("A", "C"): 3, ("A", "B"): 4},
("a", "c"): {("A", "B"): 5, ("A", "C"): 6},
("b", "a"): {("A", "C"): 7, ("A", "B"): 8},
("b", "b"): {("A", "D"): 9, ("A", "B"): 10},
}
)
从命名元组列表中
列表中第一个的字段名称namedtuple
确定DataFrame
. 剩下的命名元组(或元组)只是简单地解包,它们的值被输入到DataFrame
. 如果这些元组中的任何一个比第一个短namedtuple
,则相应行中后面的列被标记为缺失值。如果任何一个比第一个长namedtuple
,则 aValueError
被提高。
from collections import namedtuple
point = namedtuple("Point", "x y")
pd.DataFrame([point(0,0), point(0, 3), (2, 3)])
Point3D = namedtuple("Point3D", "x y z")
pd.DataFrame([Point3D(0, 0, 0), Point3D(0, 3, 5), point(2, 3)])
从数据类列表中
PEP557中介绍的数据类可以传递到 DataFrame 构造函数中。传递数据类列表等同于传递字典列表。
请注意,列表中的所有值都应该是数据类,在列表中混合类型会导致 TypeError。
from dataclasses import make_dataclass
Point = make_dataclass("Point", [("x", int), ("y", int)])
pd.DataFrame([Point(0, 0), Point(0, 3), Point(2, 3)])
替代构造函数
DataFrame.from_dict
DataFrame.from_dict
接受 dicts 或类似数组序列的 dict 并返回一个 DataFrame。它的操作类似于DataFrame
构造函数,除了默认情况下的orient
参数'columns'
,但可以设置为'index'
以便将 dict 键用作行标签。
pd.DataFrame.from_dict(dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]))
如果您通过orient='index'
,键将是行标签。在这种情况下,您还可以传递所需的列名:
pd.DataFrame.from_dict(
dict([("A", [1, 2, 3]), ("B", [4, 5, 6])]),
orient="index",
columns=["one", "two", "three"],
)
DataFrame.from_records
DataFrame.from_records
接受元组列表或具有结构化 dtype 的 ndarray。它的工作方式与普通构造函数类似DataFrame
,只是生成的 DataFrame 索引可能是结构化 dtype 的特定字段。例如:
pd.DataFrame.from_records(data, index="C")
列选择、添加、删除
可以在语义上将 DataFrame 视为类似索引的 Series 对象的字典。获取、设置和删除列的语法与类似的 dict 操作相同:
df["three"] = df["one"] * df["two"]
df
df["flag"] = df["one"] > 2
df
列可以像字典一样被删除或弹出:
del df["two"]
df
three = df.pop("three")
three
插入标量值时,自然会传播以填充列:
df["foo"] = "bar"
df
在插入与 DataFrame 索引不同的 Series 时,将符合 DataFrame 的索引:
df["one_trunc"] = df["one"][:2]
df
可以插入原始 ndarray,但它们的长度必须与 DataFrame 索引的长度匹配。
默认情况下,列在最后插入。该insert
函数可用于在列中的特定位置插入:
df.insert(1, "bar", df["one"])
df
在方法链中分配新列
受dplyr mutate
动词的启发,DataFrame 有一种assign() 方法可以让您轻松创建可能从现有列派生的新列。
iris = pd.DataFrame(np.random.randn(600, 5), columns=["A", "B", "C", "D", "E"])
iris.head()
iris.assign(sepal_ratio=iris["B"] / iris["A"]).head()
在上面的示例中,我们插入了一个预先计算的值。还可以传入一个参数的函数,以在分配给的 DataFrame 上进行评估。
iris.assign(sepal_ratio=lambda x: (x["B"] / x["A"])).head()
assign
始终返回数据的副本,而原始 DataFrame 保持不变。
当您手头没有对 DataFrame 的引用时,传递可调用对象而不是要插入的实际值很有用。assign
这在一系列操作中使用时很常见。例如,我们可以将 DataFrame 限制为仅那些萼片长度大于 5 的观察值,计算比率并绘制:
(
iris.query("A > 0").assign(
F = lambda x: x.B / x.A,
G = lambda x: x.D / x.C,
).plot(kind = "scatter", x = "F", y = "G")
)
由于传入了一个函数,因此该函数是在分配给的 DataFrame 上计算的。重要的是,这是被过滤到A长度大于 0 的那些行的 DataFrame。过滤首先发生,然后是比率计算。这是一个示例,我们没有对可用的已过滤DataFrame 的引用。
的函数签名assign
很简单**kwargs
。键是新字段的列名,值是要插入的值(例如,一个Series
或 NumPy 数组),或者是要在DataFrame
. 返回原始 DataFrame的副本,并插入新值。
从 Python 3.6 开始,**kwargs
保留了 的顺序。这允许依赖赋值,其中后面的表达式**kwargs
可以引用前面创建的列assign()。
dfa = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]})
dfa.assign(C=lambda x: x["A"] + x["B"], D=lambda x: x["A"] + x["C"])
在第二个表达式中,x['C']
将引用新创建的列,即等于。dfa['A'] + dfa['B']
索引/选择
索引的基础知识如下:
操作 |
句法 |
结果 |
---|---|---|
选择列 |
|
系列 |
按标签选择行 |
|
系列 |
按整数位置选择行 |
|
系列 |
切片行 |
|
数据框 |
按布尔向量选择行 |
|
数据框 |
例如,行选择返回一个 Series,其索引是 DataFrame 的列:
数据对齐和算术
DataFrame 对象之间的数据对齐会自动对齐列和索引(行标签)。同样,生成的对象将具有列和行标签的联合。
df = pd.DataFrame(np.random.randn(10, 4), columns=["A", "B", "C", "D"])
df2 = pd.DataFrame(np.random.randn(7, 3), columns=["A", "B", "C"])
df + df2
在 DataFrame 和 Series 之间进行操作时,默认行为是在 DataFrame列上对齐 Series索引,从而逐行广播 。例如:
df - df.iloc[0]
标量运算与您预期的一样:
df * 5 + 2
1 / df
布尔运算符也可以:
df1 = pd.DataFrame({"a": [1, 0, 1], "b": [0, 1, 1]}, dtype=bool)
df2 = pd.DataFrame({"a": [0, 1, 1], "b": [1, 1, 0]}, dtype=bool)
df1
Transposing
要转置,访问T
属性(也是transpose
函数),类似于 ndarray:
df[:5].T
DataFrame 与 NumPy 函数的互操作性
Elementwise NumPy ufuncs (log, exp, sqrt, ...) 和其他各种 NumPy 函数可以在 Series 和 DataFrame 上毫无问题地使用,假设其中的数据是数字:
np.exp(df)
np.asarray(df)
DataFrame 并不打算成为 ndarray 的替代品,因为它的索引语义和数据模型在某些地方与 n 维数组完全不同。
Seriesimplements __array_ufunc__
,这允许它与 NumPy 的 通用函数一起工作。
ufunc 应用于系列中的底层数组。
ser = pd.Series([1, 2, 3, 4])
np.exp(ser)
与库的其他部分一样,pandas 将自动将标记的输入作为 ufunc 的一部分与多个输入对齐。例如,使用numpy.remainder()
两个Series具有不同顺序标签的标签将在操作之前对齐。
ser1 = pd.Series([1, 2, 3], index=["a", "b", "c"])
ser2 = pd.Series([1, 3, 5], index=["b", "a", "c"])
ser1
np.remainder(ser1, ser2)
像往常一样,取两个索引的并集,不重叠的值用缺失值填充。
ser3 = pd.Series([2, 4, 6], index=["b", "c", "d"])
ser3
np.remainder(ser1, ser3)
当二进制 ufunc 应用于 a SeriesandIndex时,Series 实现优先并返回 Series。
例如,NumPy ufunc 可以安全地应用于Series非 ndarray 数组的支持arrays.SparseArray(请参阅稀疏计算)。如果可能,应用 ufunc 而不将基础数据转换为 ndarray。
控制台显示
非常大的 DataFrame 将被截断以在控制台中显示它们。您还可以使用info().
df = pd.DataFrame(np.random.randn(100, 6))
df
df.info()