1 Numpy 和 Pandas 回顾

1 Numpy 和 Pandas 回顾

1 技术要求
安装Anaconda和Jupyter Notebook
书籍代码:
https://github.com/PacktPublishing/Hands-On-Data-Preprocessing-in-Python
2 回顾Jupyter Notebook
User Interface (UI)
Jupyter Notebook provides an interactive environment where you can run your Python code, see immediate outputs, and take notes.


3 我们是在使用计算机编程技术来分析数据吗?
作者指出,真正使用计算机编程技术的是开发Pandas、Numpy等包的高级技术人员,我们只不过是通过调用这些包的内置的函数和方法实现数据处理的目的而已。
作为一名数据分析师,我们的工作室将业务问题、数据和技术有机结合起来。
4 回顾Numpy的基本方法
Numpy由Num和Py构成,the Num stands for numbers, and Py stands for Python.
使用np作为numpy包别名的好处:
其他人都使用这个别名,所以如果你与其他人分享你的代码,他们就会知道你在整个项目中做什么。
第二,很多时候,你最终会在你的项目中使用别人写的代码,所以一致性会使你的工作更容易。你会看到大多数著名的模块也有一个著名的别名,例如,Pandas的pd,matplotlib.pyplot的plt。
# 1. 导包
import numpy as np
# 2.The np.arange() function:产生一个增量相同的数字序列
np.arange(5) #array([0, 1, 2, 3, 4])
np.arange(1,5)#array([1, 2, 3, 4])
np.arange(-5.1,5)#array([-5.1, -4.1, -3.1, -2.1, -1.1, -0.1, 0.9, 1.9, 2.9, 3.9, 4.9])
# 3.np.zeros:创建一个充满0的NumPy数组
np.zeros([3,2])#np.zeros((3,2))
'''array([[0., 0.],
[0., 0.],
[0., 0.]])'''
# 4.np.ones:创建一个充满1的NumPy数组
np.ones(7)#array([1., 1., 1., 1., 1., 1., 1.])
np.ones((3,2))
'''array([[1., 1.],
[1., 1.],
[1., 1.]])'''
# 5.The np.linspace() function:返回指定区间内均匀分布的数字。前两个输入指定了区间,第三个输入显示了输出将有的元素的数量。(end-start)/ 步长 + 1 = numbers
np.linspace(0,10,11)#array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
np.logspace(0,5,6,base=2)#array([ 1., 2., 4., 8., 16., 32.])
# 6.np.mean()与 .mean()的区别
# type:list
lst_nums = [3,4,5,1,6]
np.mean(lst_nums)#3.8
# type:numpy.ndarray
ary_nums = np.array(lst_nums)
ary_nums.mean()#3.8
#np.mean():使用这种方法的好处是,在NumPy满足你的请求之前,你不需要在大多数时候改变你的数据类型。你可以输入列表、Pandas系列或DataFrames。
#.mean():它是任何NumPy数组的一个属性
5 Pandas回顾
Pandas最伟大的工具是它的数据结构,也就是所谓的DataFrame。简而言之,DataFrame是一种二维数据结构,具有良好的接口和强大的可编性。
每一列都是一个DataFrame的属性,所以要访问它,你所需要做的就是在DataFrame后面使用.ColumnName
不仅每个属性是一个Series,而且每一行也是一个Series。
.head()要求只输出前五行的数据。
.tail()代码可以对数据的底部五行做同样的处理。
import numpy as np
import pandas as pd
df = pd.DataFrame(np.arange(1,101).reshape(10,10))
df.columns = [chr(i) for i in range(65,75)]
df.head()

要访问一个DataFrame的每一行,你需要在DataFrame后面使用.loc[]。
df.loc[0].index
#Index(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'], dtype='object')
df.A.index
#RangeIndex(start=0, stop=10, step=1)
5.1 了解你将要工作的数据集
第一步。按照我刚才的解释,了解每个属性。
第二步。检查数据集的形状。数据集有多少行和多少列?这个很容易。例如,只需尝试 df.shape 并查看结果。
第三步。检查数据是否有任何缺失值。
第四步。计算数字属性的总结值,如平均数、中位数和标准差,并计算分类属性的所有可能值。
第五步。将属性可视化。对于数字属性,使用直方图或boxplot,对于分类属性,使用条形图。
5.2 Pandas 数据访问
1.Pandas DataFrame access
DataFrame access rows
每个Pandas系列或DataFrame都带有两种类型的索引:默认索引或指定索引。默认指数是读取时自动分配给你的数据集的整数。Pandas允许你更新它们。你可以使用的函数是.set_index()。DataFrame的每一行都可以通过在.loc[]的括号中指定索引来访问。还可以使用.iloc[]来访问使用默认的整数索引的数据。Python将破折号解读为减法运算符,除非在引号内出现。
df.set_index(np.arange(100,110),inplace=True)
df.tail()

df.loc[109]
df.iloc[9]
'''
A 91
B 92
C 93
D 94
E 95
F 96
G 97
H 98
I 99
J 100
Name: 109, dtype: int64
'''
DataFrame access columns
每一列都被编码为DataFrame的一个属性。你可以通过使用.ColumnName访问每一列.
df.A
'''
100 2
101 12
102 22
103 32
104 42
105 52
106 62
107 72
108 82
109 92
Name: B, dtype: int64
''''
df.rename(columns={'A':'A-A'},inplace=True)
df.head()
'''
A-A B C D E F G H I J
100 1 2 3 4 5 6 7 8 9 10
101 11 12 13 14 15 16 17 18 19 20
102 21 22 23 24 25 26 27 28 29 30
103 31 32 33 34 35 36 37 38 39 40
104 41 42 43 44 45 46 47 48 49 50
'''
df.A-A #Python将破折号解读为减法运算符,除非在引号内出现。
'''
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/Users/peanut123/Documents/IT/datascience/numpy.ipynb Cell 20 in()
----> 1 df.A-A
File ~/opt/miniconda3/envs/datascience/lib/python3.10/site-packages/pandas/core/generic.py:5575, in NDFrame.__getattr__(self, name)
5568 if (
5569 name not in self._internal_names_set
5570 and name not in self._metadata
5571 and name not in self._accessors
5572 and self._info_axis._can_hold_identifiers_and_holds_name(name)
5573 ):
5574 return self[name]
-> 5575 return object.__getattribute__(self, name)
AttributeError: 'DataFrame' object has no attribute 'A'
'''
df['A-A']
'''
100 1
101 11
102 21
103 31
104 41
105 51
106 61
107 71
108 81
109 91
Name: A-A, dtype: int64
'''
DataFrame access values
你可以从列开始,一旦你得到一个列系列,就访问这个值,或者你可以从行开始,一旦你得到一个行系列,就访问这个值。用.at[]访问值是我最喜欢的,原因有二。首先,它更整洁、更直接。第二,你可以把DataFrame当成一个矩阵。
#先访问列
df.B.iloc[0]
df.B.loc[100]
df['B'].iloc[0]
#先访问行
df.iloc[0].loc['B']
df.iloc[0].B
df.at[100,'B']
# 2
2 Pandas series access
你可以使用所有为DataFrames提到的方法来访问一个系列的值,除了.at[]。
row_series = df.loc[100]
print(row_series.loc['B'])
print(row_series.iloc[1])
print(row_series['B'])
print(row_series.B)
'''
2
2
2
2
'''
columns_series = df.B
print(columns_series.loc[100])
print(columns_series.iloc[0])
print(columns_series[100])
'''
2
2
2
'''
# print(row_series.100) This will give syntax error!
#要使用这个方法,你必须确保系列索引是字符串类型的
3 Slicing
3.1 Slicing a NumPy array
当我们需要访问一个以上的数据值时,我们会对NumPy数组进行切分。区分正常访问和分片访问的是在任何一个索引输入中出现冒号:。
numpy_array_slice = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
numpy_array_slice
'''
array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
'''
numpy_array_slice[1,1] #5
numpy_array_slice[1,:]#array([4, 5, 6])
numpy_array_slice[:,2]#array([4, 5, 6])
你也可以使用冒号(:)只指定从某个索引到另一个索引的访问
numpy_array_slice[:2,:2]
'''
array([[1, 2],
[4, 5]])
'''
numpy_array_slice[1:3,[0,2]]#访问第二行至第三行,第一列和第三列的值
'''
array([[4, 6],
[7, 9]])
'''
3.2 Slicing a Pandas DataFrame
Pandas DataFrames也可以在列和行上进行切分。然而,切片功能只能在.loc[](包含右端点)或.iloc[](不包括右端点)中进行。访问方法,.at[],以及其他访问数据的方式不支持分片。
pandas_dataframe_slice = pd.DataFrame(np.linspace(1,12,12).reshape(4,3))
pandas_dataframe_slice.columns = [chr(i) for i in range(65,68)]
pandas_dataframe_slice.head()
'''
A B C
0 1.0 2.0 3.0
1 4.0 5.0 6.0
2 7.0 8.0 9.0
3 10.0 11.0 12.0
'''
pandas_dataframe_slice.loc[:2,:'C']
'''
A B C
0 1.0 2.0 3.0
1 4.0 5.0 6.0
2 7.0 8.0 9.0
'''
pandas_dataframe_slice.iloc[:3,:3]
'''
A B C
0 1.0 2.0 3.0
1 4.0 5.0 6.0
2 7.0 8.0 9.0
'''
pandas_dataframe_slice.loc[:2,'A':'C']
'''
A B C
0 1.0 2.0 3.0
1 4.0 5.0 6.0
2 7.0 8.0 9.0
'''
pandas_dataframe_slice.loc[:2,['A','C']]
'''
A C
0 1.0 3.0
1 4.0 6.0
2 7.0 9.0
'''
5.3. Boolean masking for filtering a DataFrame
当你想用布尔掩码过滤一个DataFrame时,你需要一个布尔值(真或假)的一维集合,其布尔值的数量与你想过滤的DataFrame的行数相同。
布尔掩码的工作方式是很直接的。如果布尔掩码(BM)中来自boolean_mask_array的数值的对应值是假的,掩码就会阻止这个数字,如果是真,掩码就会让它通过。检查一下前面代码的输出是否是这样的情况。这在下图中显示。
boolean_mask_array = pd.Series(np.logspace(0,10,11,base=2))
BM = [False,False,False,True,False,False,False,True,True,True,True]
boolean_mask_array[BM]
'''
3 8.0
7 128.0
8 256.0
9 512.0
10 1024.0
dtype: float64
'''

可以使用任何一个数学比较运算符。
BM = boolean_mask_array >= 500
boolean_mask_array[BM]
'''
9 512.0
10 1024.0
dtype: float64
'''
boolean_mask_array[boolean_mask_array>=500]
'''
9 512.0
10 1024.0
dtype: float64
'''
df.query()
from faker import Faker
import numpy as np
import pandas as pd
Faker.seed(0)
f = Faker(locale='zh_CN')
df_query_demo = pd.DataFrame(columns=['name','value_'])
df_query_demo.name = [f.name_male() for i in range(20)]
np.random.seed(0)
df_query_demo.value_ = np.random.randint(50,80,size=20)
df_query_demo.head()
'''
name value_
0 廖婷婷 62
1 刘凤兰 65
2 赵莉 71
3 黄红霞 50
4 陈帆 53
'''
BM = df_query_demo.name == '廖婷婷'
df_query_demo.loc[BM].head()
'''
name value_
0 廖婷婷 62
1 廖婷婷 65
2 廖婷婷 71
3 廖婷婷 50
4 廖婷婷 53
'''
df_query_demo.query("name == '廖婷婷'").head()
'''
name value_
0 廖婷婷 62
1 廖婷婷 65
2 廖婷婷 71
3 廖婷婷 50
4 廖婷婷 53
'''
BM = (df_query_demo.name == '廖婷婷') & (df_query_demo.value_ == 62)
df_query_demo.loc[BM].head()
'''
name value_
0 廖婷婷 62
17 廖婷婷 62
'''
df_query_demo.query("name == '廖婷婷' and value_ == 62").head()
'''
name value_
0 廖婷婷 62
17 廖婷婷 62
'''
5.4 Pandas functions for exploring a DataFrame
了解一个数据集有两个方面。第一是了解数据的结构,如行数、列数和列的名称。第二是要了解每一列下的数值。
5.4.1 Getting to know the structure of a dataset
pandas_dataframe = pd.DataFrame(np.linspace(1,12,12).reshape(4,3))
pandas_dataframe.columns = [chr(i) for i in range(65,68)]
pandas_dataframe.head()
'''
A B C
0 1.0 2.0 3.0
1 4.0 5.0 6.0
2 7.0 8.0 9.0
3 10.0 11.0 12.0
'''
# 1.The .shape property:它告诉你DataFrame有多少行和多少列。
pandas_dataframe.shape#(4, 3)
# 2.The .columns property
#.columns允许你查看和编辑你的DataFrame中的列名。
pandas_dataframe.columns #Index(['A', 'B', 'C'], dtype='object')
#更新DataFrame的列标题
pandas_dataframe.columns = ['one','two','three']
pandas_dataframe.head()
'''
one two three
0 1.0 2.0 3.0
1 4.0 5.0 6.0
2 7.0 8.0 9.0
3 10.0 11.0 12.0
'''
# 3.The .info() function
# 这个函数提供了关于DataFrame的形状和列的信息。如果你运行apandas_dataframe.info(),你会看到其他信息,比如非空值的数量,还有每一列下的数据类型都会被报告。
pandas_dataframe.info()
'''
RangeIndex: 4 entries, 0 to 3
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 one 4 non-null float64
1 two 4 non-null float64
2 three 4 non-null float64
dtypes: float64(3)
memory usage: 224.0 bytes
'''
5.4.2 Getting to know the values of a dataset
数字列和分类列的区别在于,分类列没有用数字表示,或者更准确地说,没有携带数字信息。
To get to know numerical columns:.describe(),.plot.hist()and .plot.box()functions are very useful.
the.unique()and .value_counts()functions are instrumental for categorical columns.
The .describe() function
# 1.The .describe() function
#这个函数输出许多有用的统计指标,目的是为了总结每一列的数据。这些指标包括计数、平均值、标准差(std)、最小值(min)、第一四分位数(25%)、第二四分位数(50%)或中位数、第三四分位数(75%)、以及最大值(max)。
pandas_dataframe.describe()
'''
one two three
count 4.000000 4.000000 4.000000
mean 5.500000 6.500000 7.500000
std 3.872983 3.872983 3.872983
min 1.000000 2.000000 3.000000
25% 3.250000 4.250000 5.250000
50% 5.500000 6.500000 7.500000
75% 7.750000 8.750000 9.750000
max 10.000000 11.000000 12.000000
'''
.describe()函数输出的指标是非常有价值的总结工具,特别是如果这些指标是为了用于算法分析。然而,一下子研究这些指标还是会让我们的人类理解力不堪重负。为了总结数据以利于人类理解,还有更有效的工具,比如用直方图和boxplots将数据可视化。
Histograms and boxplots to visualize numerical columns
每个Pandas series都有一个非常有用的绘图函数集合
pandas_dataframe.two.plot.hist()

The .unique() function
.unique()简单地返回列的所有可能的值.
pandas_dataframe_demo = pd.concat([pandas_dataframe,pd.Series(['smile','smile','cry','OK'])],axis=1)
pandas_dataframe_demo.rename(columns={0:'four'},inplace=True)
pandas_dataframe_demo.head()
'''
one two three four
0 1.0 2.0 3.0 smile
1 4.0 5.0 6.0 smile
2 7.0 8.0 9.0 cry
3 10.0 11.0 12.0 OK
'''
pandas_dataframe_demo.four.unique() #array(['smile', 'cry', 'OK'], dtype=object)
The .value_counts() function
了解分类列的下一步是了解每种可能性的发生频率..value_counts()函数的输出也被称为频率表。
还有一种相对频率表,它显示的是出现的比率,而不是每种可能性的出现次数。要得到相对频率表,你只需指定你希望该表被规范化:.value_counts(normalize=True)。
pandas_dataframe_demo.four.value_counts()
'''
smile 2
cry 1
OK 1
Name: four, dtype: int64
'''
pandas_dataframe_demo.four.value_counts(normalize=True)
'''
smile 0.50
cry 0.25
OK 0.25
Name: four, dtype: float64
'''
Barcharts for visualizing numerical columns
为了创建柱状图,你必须首先创建频率表。由于频率表本身就是一个Pandas系列,你可以用它来绘制柱状图。
pandas_dataframe_demo.four.value_counts().plot.bar()

5.5 Pandas applying a function
在很多情况下,我们要对数据集中的每一行进行同样的计算。进行这种计算的传统方法是在数据中进行循环,在循环的每一次迭代中,执行并保存计算结果。Python 和 Pandas 通过引入应用函数的概念改变了这种模式。当你对一个DataFrame应用一个函数时,你要求Pandas对每一行都运行它。
Applying a function to a series
def MultiplyBy2(x):
return x**2
pandas_dataframe_demo.one.apply(MultiplyBy2)
'''
0 1.0
1 16.0
2 49.0
3 100.0
Name: one, dtype: float64
'''
Applying a Lambda function
lambda函数是一个用一行表示的函数。所以,很多时候,应用lambda函数可能会使编码变得更容易,也许有时会帮助我们的代码变得更有可读性。例如,如果你想 "即时 "回答前面的计算,你可以简单地应用一个lambda函数而不是显式函数。
pandas_dataframe_demo.one.apply(lambda x: x**2)
'''
0 1.0
1 16.0
2 49.0
3 100.0
Name: one, dtype: float64
'''
重要的是要明白,在lambda函数或显式函数之间的正确选择取决于情况。有时,不得不把一个也许很复杂的函数塞进一行,导致编码变得更加困难,并使代码的可读性降低。如果该函数有一个以上的条件语句,就会出现这种情况。
Applying a function to a DataFrame
将函数应用于DataFrame和Series的主要区别在于你在定义函数的时候。对于一个系列,我们必须假设一个值将被输入到函数中,而对于一个DataFrame,我们必须假设一个行系列将被输入。包含 axis=1 是必要的,这意味着你要将函数应用于每一行.
def CalcThreeMinusOne(row):
return row.three - row.one
pandas_dataframe_demo.apply(CalcThreeMinusOne,axis=1)
'''
0 2.0
1 2.0
2 2.0
3 2.0
dtype: float64
'''
pandas_dataframe_demo.apply(lambda row: row.three - row.one,axis=1)
'''
0 2.0
1 2.0
2 2.0
3 2.0
dtype: float64
'''
.corr(),计算DataFrame中数字属性的所有组合的皮尔逊相关系数。
pandas_dataframe_demo[['one','two','three']].corr()
'''
one two three
one 1.0 1.0 1.0
two 1.0 1.0 1.0
three 1.0 1.0 1.0
'''
5.6 The Pandas groupby function
通常情况下,你会想按分类属性来分组你的数据。如果你熟悉SQL查询,Pandas groupby几乎与SQL groupby相同。对于SQL查询和Pandas查询,将你的数据分组本身不会有任何附加值或任何输出,除非它伴随着一个聚合函数。
唯一不需要指定感兴趣的列就能工作的聚合函数是.size()
np.random.seed(123)
df_groupby_demo = pd.DataFrame(np.random.randint(1,11,size=100).reshape(10,10),columns=[chr(i) for i in range(65,75)])
cate_1 = ['one','two','three']
cate_2 = ['up','down']
cate_3 = ['smile','cry']
df_groupby_demo.A = [cate_1[j] for j in np.random.randint(0,3,size=10)]
df_groupby_demo.B = [cate_2[j] for j in np.random.randint(0,2,size=10)]
df_groupby_demo.C = [cate_3[j] for j in np.random.randint(0,2,size=10)]
df_groupby_demo.at[0,'D']=pd.NaT
df_groupby_demo

df_groupby_demo.groupby('A').size()
'''
A
one 3
three 2
two 5
dtype: int64
'''
df_groupby_demo.groupby(['A','B']).size()
'''
A B
one down 1
up 2
three down 1
up 1
two down 4
up 1
dtype: int64
'''
一旦你指定了你想要聚合的数据的兴趣列,你可以使用任何你可以在Pandas系列或DataFrame上使用的聚合函数。下表显示了你可以使用的所有聚合函数的列表。

df_groupby_demo.groupby('A')['D'].count()
'''
A
one 3
three 2
two 4
Name: D, dtype: int64
'''
.count() 与 .size() 的区别:.count()聚合不包含空值。.size()聚合包含空值。
df_groupby_demo.groupby('A').D.mode()
'''
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/Users/peanut123/Documents/IT/datascience/numpy.ipynb Cell 67 in()
----> 1 df_groupby_demo.groupby('A').D.mode()
File ~/opt/miniconda3/envs/datascience/lib/python3.10/site-packages/pandas/core/groupby/groupby.py:904, in GroupBy.__getattr__(self, attr)
901 if attr in self.obj:
902 return self[attr]
--> 904 raise AttributeError(
905 f"'{type(self).__name__}' object has no attribute '{attr}'"
906 )
AttributeError: 'SeriesGroupBy' object has no attribute 'mode'
'''
df_groupby_demo.groupby('A').D.agg(pd.Series.mode)
'''
The mode points value for one are 7,8,10. #众数
The mode points values for three are 3,7.
The mode points value for two is 2,4,5,7.
A
one [7, 8, 10]
three [3, 7]
two [2, 4, 5, 7]
Name: D, dtype: object
'''
https://www.statology.org/pandas-groupby-mode/
平均平均差(MAD)是用来测量观测数据与其平均值之间的平均距离。MAD 使用数据的原始单位,这简化了解释。较大的值表示数据点远离平均值。相反,较低的值对应于聚集在它附近的数据点。平均平均差也称为平均偏差和平均平均差。(https://statisticsbyjim.com/basics/mean-absolute-deviation/)

5.7 Pandas multi-level indexing
如果你看一下按多列分组的DataFrame的输出,输出的索引看起来与正常情况不同。虽然输出是一个Pandas系列,但它看起来是不同的。造成这种不一样的原因是多级索引。
grouby生成多层索引
np.random.seed(123)
df_groupby_demo = pd.DataFrame(np.random.randint(1,11,size=100).reshape(10,10),columns=[chr(i) for i in range(65,75)])
cate_1 = ['one','two','three']
cate_2 = ['up','down']
cate_3 = ['smile','cry']
df_groupby_demo.A = [cate_1[j] for j in np.random.randint(0,3,size=10)]
df_groupby_demo.B = [cate_2[j] for j in np.random.randint(0,2,size=10)]
df_groupby_demo.C = [cate_3[j] for j in np.random.randint(0,2,size=10)]
df_groupby_demo.at[0,'D']=pd.NaT
df_groupby_demo

The .unstack() function
这个函数将多级索引的外层推送到列中。如果多级索引只有两级,在运行.unstack()之后,它将变成单级。同样地,如果对一个具有多级索引的series运行.unstack()函数,输出将是一个DataFrame,其列是被推送的外层索引
df_groupby_demo.groupby(['A','B']).D.mean()
'''
A B
one down 10.000000
up 7.500000
three down 7.000000
up 3.000000
two down 4.666667
up 4.000000
Name: D, dtype: float64
'''
df_groupby_demo.groupby(['A','B']).D.mean().unstack()
'''
B down up
A
one 10.000000 7.5
three 7.000000 3.0
two 4.666667 4.0
'''
如果有两层以上,多次执行.unstack()会逐一将索引的外层推到列中。
df_groupby_demo.groupby(['A','B','C']).D.sum()
'''
A B C
one down smile 10
up smile 15
three down smile 7
up cry 3
two down cry 9
smile 5
up smile 4
Name: D, dtype: object
'''
df_groupby_demo.groupby(['A','B','C']).D.sum().unstack()

df_groupby_demo.groupby(['A','B','C']).D.sum().unstack().unstack()

由于Pandas中的索引可以是多级的,所以列也可以有多个级别。
mlt_df = df_groupby_demo.groupby(['A','B','C']).D.sum().unstack().unstack()
mlt_df.columns
'''
MultiIndex([( 'cry', 'down'),
( 'cry', 'up'),
('smile', 'down'),
('smile', 'up')],
names=['C', 'B'])
'''
The .stack() function
与.unstack()相反的是.stack(),在这里,列的外层被推到作为索引的外层被添加。
mlt_df.stack()

mlt_df.stack().stack()

Multi-level access
from faker import Faker
from datetime import date, datetime
import pandas as pd
import numpy as np
Faker.seed(0)
np.random.seed(0)
f = Faker(locale='zh_CN')
multi_index_demo = pd.DataFrame(columns=['date','city','gender','money'])
multi_index_demo.date = [f.date_time_between(datetime(2021,9,1),datetime(2022,8,31)).strftime('%Y年%m月') for i in range(200)]
multi_index_demo.city = [f.city_name() for i in range(200)]
gender_lst = ['男','女']
multi_index_demo.gender = [gender_lst[i] for i in np.random.randint(0,2,size=200)]
multi_index_demo.money = np.random.randint(500,1000,size=200)*10
print('date列唯一值数量为:{}\ncity列唯一值数量为:{}'.format(multi_index_demo.date.nunique(),multi_index_demo.city.nunique()))
multi_index_demo.head()

df_groupby_city = multi_index_demo.groupby(['date','city','gender'])['money'].mean()
df_groupby_city

df_groupby_city.loc['2021年09月']

df_groupby_city.loc['2021年09月']['东莞']
'''
gender
男 9440.0
Name: money, dtype: float64
'''
df_groupby_city.loc['2021年09月']['东莞']['男']
# 9440.0
df_groupby_city.iloc[0]
# 9440.0
set_index
df_set_index = multi_index_demo.groupby(['date','city','gender'],as_index=False)['money'].mean()
df_set_index.head()

df_set_index.set_index(['date','city','gender'],inplace=True)
df_set_index.head()

groupby与unstack()
df_unstack = df_groupby_city.loc['2021年09月'].unstack()
type(df_unstack) #pandas.core.frame.DataFrame
df_unstack.head()

5.8Pandas pivot and melt functions
.pivot()和.melt()帮助你在两种形式的二维数据结构之间切换:宽形式和长形式。
下图描述了这两种形式的区别。如果你是一个电子表格用户,你通常会习惯于宽泛的形式。宽表使用许多列来引入数据集的新维度。而长表则使用不同的数据结构逻辑,使用一个索引列来包括所有相关的维度.
.melt()函数,你可能会根据melt这个词的含义在脑海中想象出它的样子,它可以很容易地将一个数据集从广义的形式重塑为长的形式。.pivot()函数可以做相反的事情。

.melt
将数据从宽格式转换为长格式.
id_vars: 这个输入需要识别列。
value_vars: 此处输入的是保存数值的列。
var_name: 这个输入是给将被添加到长格式中的识别列的名称。
value_name: 此处输入的是你想给将添加到长格式中的新值列的名称。
from faker import Faker
from datetime import date, datetime
import pandas as pd
import numpy as np
Faker.seed(0)
f = Faker(locale='zh_CN')
df = pd.DataFrame(columns=['time','city','NO','NO2','PM2.5'])
df.time = [f.date_time_between(datetime(2022,8,18),datetime(2022,12,31)) for i in range(100)]
df.city = [f.city_name() for i in range(100)]
df.NO = np.random.rand(100).round(decimals=1)*4
df.NO2 = np.random.rand(100).round(decimals=1)*40
df['PM2.5'] = np.random.rand(100).round(decimals=1)*40
df.time.nunique() # 100
df.head()

long_df = df.melt(id_vars=['time','city'],value_vars=['NO','NO2','PM2.5'],value_name='values',var_name='species')
long_df.head()

.pivot()
将一个DataFrame从长形重塑为宽形。
index:这个输入需要什么,将是广义的表格的索引(不能重复)。
columns: 此处输入的是长表的列,这些列将被扩展以创建宽表的列。
values:此处输入的是长形表格中保存数值的列。
wide_df = long_df.pivot(index='time',columns='species',values='values')
wide_df.head()

本文使用 文章同步助手 同步