【pandas】17 数据处理和绘图
2023.1.16 pandas数据处理方法和绘图:读取数据、更改数据、时间数据等
主要参考:https://mofanpy.com/tutorials/data-manipulation/pandas/time
17.1运算方法
17.1.1 筛选赋值运算
- 就是用前面的方法对数据进行筛选,然后对值进行一定的操作(对数据修改)
本章实例就是通过不同方法筛选出需要的位置,进行赋值
import pandas as pd
import numpy as np
data = np.arange(-12, 12).reshape((6, 4))
df = pd.DataFrame(
data,
index=list("abcdef"),
columns=list("ABCD"))
df
| A | B | C | D | |
|---|---|---|---|---|
| a | -12 | -11 | -10 | -9 |
| b | -8 | -7 | -6 | -5 |
| c | -4 | -3 | -2 | -1 |
| d | 0 | 1 | 2 | 3 |
| e | 4 | 5 | 6 | 7 |
| f | 8 | 9 | 10 | 11 |
# 对A列*0操作
df["A"] *= 0
df
| A | B | C | D | |
|---|---|---|---|---|
| a | 0 | -11 | -10 | -9 |
| b | 0 | -7 | -6 | -5 |
| c | 0 | -3 | -2 | -1 |
| d | 0 | 1 | 2 | 3 |
| e | 0 | 5 | 6 | 7 |
| f | 0 | 9 | 10 | 11 |
# 对(0,0),(1,0)位置重新导入值 用两种方法
# loc是索引,iloc是位置
df.loc['a','A']=100
df.iloc[1,0]=200
df
| A | B | C | D | |
|---|---|---|---|---|
| a | 100 | -11 | -10 | -9 |
| b | 200 | -7 | -6 | -5 |
| c | 0 | -3 | -2 | -1 |
| d | 0 | 1 | 2 | 3 |
| e | 0 | 5 | 6 | 7 |
| f | 0 | 9 | 10 | 11 |
# 对a行都乘以2
df.iloc[0,:] *= 2
df
| A | B | C | D | |
|---|---|---|---|---|
| a | 200 | -22 | -20 | -18 |
| b | 200 | -7 | -6 | -5 |
| c | 0 | -3 | -2 | -1 |
| d | 0 | 1 | 2 | 3 |
| e | 0 | 5 | 6 | 7 |
| f | 0 | 9 | 10 | 11 |
#对A列中是0的都选为1
df["A"][df["A"]==0]=1
df
| A | B | C | D | |
|---|---|---|---|---|
| a | 200 | -22 | -20 | -18 |
| b | 200 | -7 | -6 | -5 |
| c | 1 | -3 | -2 | -1 |
| d | 1 | 1 | 2 | 3 |
| e | 1 | 5 | 6 | 7 |
| f | 1 | 9 | 10 | 11 |
17.1.2 apply
数据.apply(方程式,。。):通过方程式的筛选选择,批量修改
df
| A | B | C | D | |
|---|---|---|---|---|
| a | 200 | -22 | -20 | -18 |
| b | 200 | -7 | -6 | -5 |
| c | 1 | -3 | -2 | -1 |
| d | 1 | 1 | 2 | 3 |
| e | 1 | 5 | 6 | 7 |
| f | 1 | 9 | 10 | 11 |
# 做开2次方
df.apply(np.sqrt)
| A | B | C | D | |
|---|---|---|---|---|
| a | 14.142136 | NaN | NaN | NaN |
| b | 14.142136 | NaN | NaN | NaN |
| c | 1.000000 | NaN | NaN | NaN |
| d | 1.000000 | 1.000000 | 1.414214 | 1.732051 |
| e | 1.000000 | 2.236068 | 2.449490 | 2.645751 |
| f | 1.000000 | 3.000000 | 3.162278 | 3.316625 |
- 通过一个方程 返回了第一列的两倍 ;第二列-1
def func(x):
return x[0] * 2, x[1] * -1
df.apply(func, axis=1, result_type='expand')
| 0 | 1 | |
|---|---|---|
| a | 400 | 22 |
| b | 400 | 7 |
| c | 2 | 3 |
| d | 2 | -1 |
| e | 2 | -5 |
| f | 2 | -9 |
df.iloc[0]
A 200
B -22
C -20
D -18
Name: a, dtype: int32
result_type='expand':让输出的结果可以生成多 column。如下:
def func(x):
return x[0] * 2, x[1] * -1
df.apply(func, axis=1)
a (400, 22)
b (400, 7)
c (2, 3)
d (2, -1)
e (2, -5)
f (2, -9)
dtype: object
- 如果
reult_type="broadcast",那么原 column 和 index 名会继承到新生成的数据中;没有的话本例column是0-3 - 因为广播,这个return必须凑齐colunm个数,即使没变化,也要写上,不然报错
print("df:\n",df)
def func(x):
return x[0] * 2, x[1] * -1,x[2] ,x[3]
print("-----")
print(df.apply(func, axis=1, result_type='expand'))
df.apply(func, axis=1, result_type='broadcast')
df:
A B C D
a 200 -22 -20 -18
b 200 -7 -6 -5
c 1 -3 -2 -1
d 1 1 2 3
e 1 5 6 7
f 1 9 10 11
-----
0 1 2 3
a 400 22 -20 -18
b 400 7 -6 -5
c 2 3 -2 -1
d 2 -1 2 3
e 2 -5 6 7
f 2 -9 10 11
| A | B | C | D | |
|---|---|---|---|---|
| a | 400 | 22 | -20 | -18 |
| b | 400 | 7 | -6 | -5 |
| c | 2 | 3 | -2 | -1 |
| d | 2 | -1 | 2 | 3 |
| e | 2 | -5 | 6 | 7 |
| f | 2 | -9 | 10 | 11 |
def func(r):
return r[2] * 4
last_row = df.apply(func, axis=0)
print("last_row:\n", last_row)
df.iloc[2, :] = last_row
print("\ndf:\n", df)
last_row:
A 16
B -48
C -32
D -16
dtype: int64
df:
A B C D
a 200 -22 -20 -18
b 200 -7 -6 -5
c 16 -48 -32 -16
d 1 1 2 3
e 1 5 6 7
f 1 9 10 11
注意:
- 默认是对列进行操作,也就是
axis=1,是按行来操作的,是对每行的第3个数操作,所以输出你看这变化是列; axis=0是对列的第3个数操作,反而输出变化的是行; 这个别弄反了
17.2 文字处理
17.2.1 格式化字符
str.upper(); str.lower(); 全大写全小写str.len():长度str.strip(); str.lstrip(); str.rstrip()去掉两边空格。去掉左空格、有空格str.split()数据用什么分割
我们用这些处理字符的时候,你要确保 Series 或者 DataFrame 的 dtype="string"
.dtype:看类型.asdtype:改类型
pd_not_s = pd.Series(
["A", "B", "C", "Aaba", "Baca", "CABA", "dog", "cat"],
)
print("pd_not_s type:", pd_not_s.dtype)
pd_s = pd_not_s.astype("string")
print("pd_s type:", pd_s.dtype)
pd_not_s type: object
pd_s type: string
用法很简单,举几个简单的例子:
# 【例子1】全部大写
print("\npandas:\n", pd_s.str.upper())
pandas:
0 A
1 B
2 C
3 AABA
4 BACA
5 CABA
6 DOG
7 CAT
dtype: string
#【例子2】每个字符串数据长度
print("\npandas len:\n", pd_s.str.len())
pandas len:
0 1
1 1
2 1
3 4
4 4
5 4
6 3
7 3
dtype: Int64
# 【例子3】从新有需要的字符切割数据
py_s = ["a_b_c", "jill_jesse", "frank"]
pd_s = pd.Series(py_s, dtype="string")
print("\npandas split:\n", pd_s.str.split("_"))
pandas split:
0 [a, b, c]
1 [jill, jesse]
2 [frank]
dtype: object
- **【和python比较】**我们用python,一般形式是这样的(除了大小写)
print("python len:\n", [len(s) for s in py_s])
print("python split:\n", [s.split("_") for s in py_s])
python len:
[5, 10, 5]
python split:
[['a', 'b', 'c'], ['jill', 'jesse'], ['frank']]
# 加一个 result_type="expand",将拆分出来的结果放到不同的 column 中去
pd_s.str.split("_", expand=True)
| 0 | 1 | 2 | |
|---|---|---|---|
| 0 | a | b | c |
| 1 | jill | jesse | <NA> |
| 2 | frank | <NA> | <NA> |
17.2.2 正则方案
我们用 str.contains() 或 str.match() 来确认它真的找到了匹配文字
str.contains():有就识别出来str.match():完全匹配
pattern = r"[0-9][a-z]"
s = pd.Series(["1", "1a", "11c", "abc"], dtype="string")
s.str.contains(pattern)
0 False
1 True
2 True
3 False
dtype: boolean
- ` r"[0-9][a-z]"`:这里面【条件1】【条件2】两个条件都要有。
- `11c`识别出来了`1c`,用`contains`是`True`,用`match`是`False`
s.str.match(pattern)
0 False
1 True
2 False
3 False
dtype: boolean
17.3 异常数据处理
17.3.1 找到NaN数据
pd.isna(), pd.notna()
import pandas as pd
import numpy as np
df = pd.DataFrame([[1, None],[np.nan, 4]])
df
| 0 | 1 | |
|---|---|---|
| 0 | 1.0 | NaN |
| 1 | NaN | 4.0 |
#找NaN数据
df.isna()
| 0 | 1 | |
|---|---|---|
| 0 | False | True |
| 1 | True | False |
【找不是NaN数据】 df.notna()或者~df.isna()
df.notna()
| 0 | 1 | |
|---|---|---|
| 0 | True | False |
| 1 | False | True |
17.3.2 处理NaN
df.dropna():移除df.fillna():填充df.clip():不符合范围的填充
df = pd.DataFrame({
"a": [1, None, 3],
"b": [4, 5, 6]
})
df
| a | b | |
|---|---|---|
| 0 | 1.0 | 4 |
| 1 | NaN | 5 |
| 2 | 3.0 | 6 |
- 移除,**默认**是`axis=0`,列方向改变;
df.dropna()
| a | b | |
|---|---|---|
| 0 | 1.0 | 4 |
| 2 | 3.0 | 6 |
df.dropna(axis=1)
| b | |
|---|---|
| 0 | 4 |
| 1 | 5 |
| 2 | 6 |
df.fillna():填充
# 在a标签上做了平均,这里排除掉了没有意义的值
a_mean = df["a"].mean()
new_col = df["a"].fillna(a_mean)
df["a"] = new_col
df
| a | b | |
|---|---|---|
| 0 | 1.0 | 4 |
| 1 | 2.0 | 5 |
| 2 | 3.0 | 6 |
- 有规律的值,也可以做一些运算
# b是的4倍
df = pd.DataFrame({
"a": [1, None, 3, None],
"b": [4, 8, 12, 12]
})
a_nan = df["a"].isna()
a_new_value = df["b"][a_nan] / 4
print("返回了索引和值:\n",a_new_value)
new_col = df["a"].fillna(a_new_value)
df["a"] = new_col
df
返回了索引和值:
1 2.0
3 3.0
Name: b, dtype: float64
| a | b | |
|---|---|---|
| 0 | 1.0 | 4 |
| 1 | 2.0 | 8 |
| 2 | 3.0 | 12 |
| 3 | 3.0 | 12 |
`["b"][a_nan] / 4`:b中是NaN的值/4
df.clip():不符合范围的填充;
有些值大的离谱小的离谱,这些超出的值会给一个上下的阈值
df = pd.DataFrame({
"a": [1, 1, 2, 1, 2, 40, 1, 2, 1],
})
df["a"] = df["a"].clip(lower=0, upper=3)
df
| a | |
|---|---|
| 0 | 1 |
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
| 4 | 2 |
| 5 | 3 |
| 6 | 1 |
| 7 | 2 |
| 8 | 1 |
17.4 时间数据
17.4.1 读时间序列
pd.to_datetime():pandas数据中有时间,但是它不知道是时间序列 所以要转换一下
import pandas as pd
df = pd.DataFrame({
"time": ["2022/03/12", "2022/03/13", "2022/03/14"],
"value": [1,2,3]
})
print(df)
time value
0 2022/03/12 1
1 2022/03/13 2
2 2022/03/14 3
# 这一列 是时间序列
pd.to_datetime(df["time"])
0 2022-03-12
1 2022-03-13
2 2022-03-14
Name: time, dtype: datetime64[ns]
-
任意形式都可以,个性化识别
format: -
- 所有前面都加个
%如%% 比配一个 %;%S 是秒
- 所有前面都加个
-
- 比如喜欢用 月@日@年%%秒|时|分
pd.to_datetime(
[
"1@21@2022%%11|11|32",
"12@01@2022%%44|02|2",
"4@01@2022%%14|22|2"
],
format="%m@%d@%Y%%%%%S|%H|%M"
)
DatetimeIndex(['2022-01-21 11:32:11', '2022-12-01 02:02:44',
'2022-04-01 22:02:14'],
dtype='datetime64[ns]', freq=None)
17.4.2 自建时间序列datetime
- 【类似于range】:
pd.date_range(start, end)
freq="48h":间隔时间。(没有)默认是一天
import datetime
# 设置成时间序列 默认是年月日
start = datetime.datetime(2022, 3, 12)
end = datetime.datetime(2022, 3, 18)
index = pd.date_range(start, end, freq="48h")
index
DatetimeIndex(['2022-03-12', '2022-03-14', '2022-03-16', '2022-03-18'], dtype='datetime64[ns]', freq='48H')
periods=:从开始到结束 平均去几个值
print("\n\npd.date_range(start, end, periods=5)\n",
pd.date_range(start, end, periods=5)
)
pd.date_range(start, end, periods=5)
DatetimeIndex(['2022-03-12 00:00:00', '2022-03-13 12:00:00',
'2022-03-15 00:00:00', '2022-03-16 12:00:00',
'2022-03-18 00:00:00'],
dtype='datetime64[ns]', freq=None)
17.4.3 选取时间data_range
- 做了一个在时间上随机生成正态分布的值
start = datetime.datetime(2022, 3, 1)
end = datetime.datetime(2022, 3, 23)
rng = pd.date_range(start, end)
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts
2022-03-01 0.471139
2022-03-02 -1.101305
2022-03-03 -0.544299
2022-03-04 1.244004
2022-03-05 -1.594690
2022-03-06 1.686098
2022-03-07 -0.613820
2022-03-08 -2.399893
2022-03-09 1.132414
2022-03-10 -1.198595
2022-03-11 0.779643
2022-03-12 0.265703
2022-03-13 -0.545246
2022-03-14 1.518926
2022-03-15 0.255980
2022-03-16 0.876557
2022-03-17 -0.599384
2022-03-18 -0.351991
2022-03-19 0.033286
2022-03-20 0.085700
2022-03-21 0.421871
2022-03-22 -1.981706
2022-03-23 0.209435
Freq: D, dtype: float64
- index=rng 这里面把时间放在了index上
- 在这个时间序列下,可以利用各种方式 切片,直接输入等来取一些值
#可分片
ts[1:8].plot()
<AxesSubplot:>

import datetime
t1 = datetime.datetime(2022, 3, 12)
t2 = datetime.datetime(2022, 3, 18)
ts[t1: t2].plot()
<AxesSubplot:>

ts["2022-03-12": "2022-03-18"].plot()
<AxesSubplot:>

17.4.4 时间运算pd.Timedelta .dayofyear .strftime
- 比如我想复制一份这周的表格,给下周用, 我就直接将这个月 copy 过来,然后日期上加一周时间。
Timedelta是一种用于时间加减的时间单位
rng = pd.date_range("2022-01-01", "2022-01-07")
rng + pd.Timedelta(weeks=1)
DatetimeIndex(['2022-01-08', '2022-01-09', '2022-01-10', '2022-01-11',
'2022-01-12', '2022-01-13', '2022-01-14'],
dtype='datetime64[ns]', freq='D')
`weeks`参数,还可以用 `days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds` 这些
- 算这是今年第几天
.dayofyear
import pandas as pd
rng = pd.date_range("2022-2-08","2022-2-14")
rng.dayofyear
Int64Index([39, 40, 41, 42, 43, 44, 45], dtype='int64')
除此之外,还有:rng.dayofweek; rng.weekofyear; rng.weekday
.strftime:输出形式切换
rng.strftime("%m/%d/%Y")
Index(['02/08/2022', '02/09/2022', '02/10/2022', '02/11/2022', '02/12/2022',
'02/13/2022', '02/14/2022'],
dtype='object')
.day_name(); .month_name():看是周几或者那个月
rng.day_name()
Index(['Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday',
'Monday'],
dtype='object')
17.4.5 时区
.tz_localize:时区初始化
#方法1
s = pd.to_datetime(
["2022/03/12 22:11", "2022/03/12 12:11", "2022/03/12 2:11"]
)
s_us = s.tz_localize("America/New_York")
s_us
DatetimeIndex(['2022-03-12 22:11:00-05:00', '2022-03-12 12:11:00-05:00',
'2022-03-12 02:11:00-05:00'],
dtype='datetime64[ns, America/New_York]', freq=None)
# 方法2 在创建时间数据时候
rng = pd.date_range(
"2022-01-08", "2022-01-11",
tz="America/New_York")
rng
.tz_convert:时区转换
s_cn = s_us.tz_convert("Asia/Shanghai")
s_cn
DatetimeIndex(['2022-03-13 11:11:00+08:00', '2022-03-13 01:11:00+08:00',
'2022-03-12 15:11:00+08:00'],
dtype='datetime64[ns, Asia/Shanghai]', freq=None)
17.5 绘制图表
- 散点图
scatter - 折线图
plot - 条形图
.plot.bar() - 分布图
.plot.hist() - 饼图
plot.Pie - 面积图
plot.area
用法就是 df.图片名字.什么图
17.5.1 散点图Scatter
c: 对于这组数据中每个(x,y)数据点的颜色值s: 画点的大小(size)alpha:不透明度cmap:colormap,你可以在这里找到非常丰富的案例.我这里觉得应该是选哪个调色板或色彩格式
import numpy as np
import pandas as pd
n = 10 # data size
df = pd.DataFrame({
"x": np.random.normal(0, 1, n),
"y": np.random.normal(0, 1, n),
})
#color = np.arctan2(df["y"], df["x"])
df.plot.scatter(x="x", y="y", c=color, s=60, alpha=.5, cmap="rainbow")
<AxesSubplot:xlabel='x', ylabel='y'>

17.5.2 散点图 plot.Scatter()
n = 20 # data size
x = np.linspace(-1, 1, n)
y1 = x * -1 - 0.1 + np.random.normal(0, 0.3, n)
y2 = x * 2 + 0.4 + np.random.normal(0, 0.3, n)
df = pd.DataFrame({
"x": x,
"y1": y1,
"y2": y2,
})
df.plot(x="x", y=["y1", "y2"], alpha=.9)
<AxesSubplot:xlabel='x'>

更改的地方:透明度改成了9
17.5.3 条形图Bar
df = pd.DataFrame(np.random.rand(5, 3), columns=["a", "b", "c"])
print(df)
df.plot.bar()
a b c
0 0.768000 0.963485 0.460891
1 0.824841 0.599105 0.607710
2 0.372849 0.541759 0.114071
3 0.278834 0.283976 0.661167
4 0.570884 0.638697 0.581616
<AxesSubplot:>

- 放一起看
stacked=True
df.plot.bar(stacked=True)
<AxesSubplot:>

.plot.barh()横竖轴调换
df.plot.barh()
<AxesSubplot:>

17.5.4 分布图 plot.hist()
分布图在机器学习和统计学中非常重要,我经常画分布图,比如要画 神经网络的参数分布可视化。 又或者是 GAN 生成对抗网络当中的数据分布。
df = pd.DataFrame(
{
"a": np.random.randn(1000) + 1,
"b": np.random.randn(1000),
"c": np.random.randn(1000) - 4,
}
)
df.plot.hist(alpha=0.3, bins=60)
<AxesSubplot:ylabel='Frequency'>

bins 柱状体的数量
17.5.5 饼图plot.Pie
df = pd.DataFrame(
{
"bigBoss": np.random.rand(4),
"smallBoss": np.random.rand(4),
},
index=["meeting", "supervise", "teaching", "team building"],
)
df.plot.pie(subplots=True, figsize=(9,9), legend=False)
array([<AxesSubplot:ylabel='bigBoss'>, <AxesSubplot:ylabel='smallBoss'>],
dtype=object)

subplots=True, figsize=(9,9), legend=False:
- 这里是两组数据,要加subplots,是分开画意思;
- legend 是用来确定要不要输出图例的。也就是有个图 来说什么颜色代表什么
17.5.6面 积图plot.area()
df = pd.DataFrame(
np.random.rand(10, 4),
columns=["a", "b", "c", "d"]
)
df.plot.area()
<AxesSubplot:>

如果你不想上下堆砌在一起观看,而是有统一的一个起点,那可以用这个参数 stacked=False。
df.plot.area(stacked=False)
<AxesSubplot:>




















