python:统计分析应用和案例

文章目录


——24年更新第3版

版本信息:

python 3.11
pandas 2.2.2
numpy 1.23.5

适合群体:

  1. 入门python的小白
  2. 纯小白但是想学习数据分析但没有合适的项目案例&覆盖最常见卡点

内容特点:

  1. 从excel使用习惯入手
  2. 最终结果是实现excel透视表功能 + 透视表不方便做的功能需求
  3. 以及为了实现透视表,所需要的过程。包括复杂的文件拼接合并、查找、替换、中间过程字段标签的生成。

整体介绍:

围绕数据的统计和透视的需求,包括数据准备处理阶段、数据透视阶段、和透视表复杂的计算和优化3阶。数据kaggle下载链接

  1. 要了解什么是Pandas.Datafame?以csv为例。(主要了解datafram的index和columns。建议通过excel透视表来理解)
  2. 数据准备阶段:如何快速合并N个文件 + 如何拆分成N个文件
  3. 如何进一步的视线excel的查找/筛选,替换,新建字段
  4. 如何实现exce透视表的统计分析 & 进阶

附注
现象:线下数据文档通常以csv格式存在,无论是kaggle竞赛 还是公司数据库返回的数据结果。
原因:csv读写速度比excel快,虽然相同文件excel的体积要小上不少。

文件电脑配置PythonPandas
320MB foo.csv,16MB foo.xlsxi7 - 7700k, SSDAnacond Python 3.5.3Pandas 0.19.2
用时
读取df = pd.read_csv('foo.csv')2 秒
读取df = pd.read_excel('foo.xlsx)15.3秒
写入 df.to_csv('hehe.csv', index=False)10.5 秒
写入 df.to_excel('hehe.xlsx', sheet_name='sheet1', index='False')34.5 秒

一、数据准备阶段:如何快速合并N个文件 + 如何拆分成N个文件

df_store =pd.read_csv(file_path+'stores.csv')
df_train =pd.read_csv(file_path+'train.csv')
# 文件store的前5行展示
print(df_stores.head())
Store	Type	Size
0	1	A	151315
1	2	A	202307
2	3	B	37392
3	4	A	205863
4	5	B	34875

# 文件train的前5行展示
print(df_train.head())
	Store	Dept	Date	Weekly_Sales	IsHoliday
0	1	1	2010-02-05	24924.50	False
1	1	1	2010-02-12	46039.49	True
2	1	1	2010-02-19	41595.55	False
3	1	1	2010-02-26	19403.54	False
4	1	1	2010-03-05	21827.90	False

需求分类: 一般是2种:
1.1 excel多个文件合并1个。 e.g.分月的文件合并成一年的需求:
1.2 excel的vlook的功能。e.g. 按照store分别储存 & 给到对应的销售侧同事

1.1 N个文件合并

1.1.1 excel常见的有 e.g.分月的文件合并成一年的需求:
my_df_list = [df_train.head(), df_train.head()]
pd.concat(my_df_list)

# output
	Store	Dept	Date	Weekly_Sales	IsHoliday
0	1	1	2010-02-05	24924.50	False
1	1	1	2010-02-12	46039.49	True
2	1	1	2010-02-19	41595.55	False
3	1	1	2010-02-26	19403.54	False
4	1	1	2010-03-05	21827.90	False
0	1	1	2010-02-05	24924.50	False
1	1	1	2010-02-12	46039.49	True
2	1	1	2010-02-19	41595.55	False
3	1	1	2010-02-26	19403.54	False
4	1	1	2010-03-05	21827.90	False
pandas.concat(objs,    # 通常为list对象
				axis=0,  # concat操作沿着哪个axis的方向来。参数0/行,1/列 任选其一;
				jion='outer',  # 参数'inner'和'outer'任选其一
				**)
1.1.2 excel类似mult - vlook的拼接功能
train_merge_store = df_train.merge(df_store, on='Store', how='left')
train_merge_store.head()

# output
	Store	Dept	Date	Weekly_Sales	IsHoliday	Type	Size
0	1	1	2010-02-05	24924.50	False	A	151315
1	1	1	2010-02-12	46039.49	True	A	151315
2	1	1	2010-02-19	41595.55	False	A	151315
3	1	1	2010-02-26	19403.54	False	A	151315
4	1	1	2010-03-05	21827.90	False	A	151315
1.1.3 假如我有N个文件,堆在1个文件夹。我怎么快速地识别&合并呢?
# 显示某文件夹所有文件
file_path = 'your_path_name'
for file in os.listdir(file_path):
    print(file)

# outpot
## 我的文件夹下所有的文件
python_统计分析应用和案例_xfhan2024M7.pdf
stores.csv
train - 副本.csv
train.csv
train_透视表展示.xlsx
其它.xlsx

file_path = 'your_path_name'
df_list = []    # 空list用于concat
for file in os.listdir(file_path):   # 逐个读取文件
    if file.endswith('.csv'):   # 只选择csv格式文件
        print(file)
        
        df = pd.read_csv(file_path+f'/{file}', low_memory=False)
        df_list.append(df.head(3))   # 为了测试的目的,只取每个df的前3行合并
        print(file+'is done. \n')
    print(file)

# output
stores.csv
stores.csv is done. 

train - 副本.csv
train - 副本.csv is done. 

train.csv
train.csv is done.
pd.concat(df_list)

# output
Store	Type	Size	Dept	Date	Weekly_Sales	IsHoliday
0	1	A	151315.0	NaN	NaN	NaN	NaN
1	2	A	202307.0	NaN	NaN	NaN	NaN
2	3	B	37392.0	NaN	NaN	NaN	NaN
0	1	NaN	NaN	1.0	2010-02-05	24924.50	False
1	1	NaN	NaN	1.0	2010-02-12	46039.49	True
2	1	NaN	NaN	1.0	2010-02-19	41595.55	False
0	1	NaN	NaN	1.0	2010-02-05	24924.50	False
1	1	NaN	NaN	1.0	2010-02-12	46039.49	True
2	1	NaN	NaN	1.0	2010-02-19	41595.55	False

1.2 文件拆分N个

需求:按照sotre的编码拆分,然后各自保存
步骤1 查看Stroe有哪些值 & 类型

df_train.Store.unique()  # unique查看唯一值

# output

array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
       18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
       35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45])

步骤2 选用query函数或者mask方法 & 测试编写代码是否OK

store_num=1
mask = df_train['Store'] == store_num
df_train[mask].Store.unique() # 等价于df_train.query('Store==1')['Store'].unique()

# output
array([1])  # 可见底码OK

步骤3 终版代码编写来解决问题

store_num_list = df_train.Store.unique()
for store_num in store_num_list:
	print(df_train[ df['Store']==store_num ].Store.unique()  # 等价于df_train.query(f'Store=={store_num}').Store.unique()
	# df_train[ df['Store']==store_num ].to_csv(your_file_path + f'{store_num}.csv', encoding='utf-8-sig', index=False) 
	# 注意: encoding='utf-8-sig'为中文储存编码

二、如何进一步的实现excel的查找/筛选,替换,新建字段

train_merge_store.head()

# output
Store	Dept	Date	Weekly_Sales	IsHoliday	Type	Size
0	1	1	2010-02-05	24924.50	False	A	151315
1	1	1	2010-02-12	46039.49	True	A	151315
2	1	1	2010-02-19	41595.55	False	A	151315
3	1	1	2010-02-26	19403.54	False	A	151315
4	1	1	2010-03-05	21827.90	False	A	151315

2.1 按照列查找/筛选

train_merge_store.loc[:, ['Store', 'Weekly_Sales', 'Dept', 'Type']]  # 等价于train_merge_store[['Store', 'Weekly_Sales', 'Dept', 'Type']]

2.2 按照行查找/筛选

train_merge_store.loc[train_merge_store['Store']==store_num]  # 等价于train_merge_store.query(f'Store=={store_num}')

2.3 多条件查询/筛选

e.g.1 按行 & 列
train_merge_store.loc[train_merge_store['Store']==store_num, ['Store', 'Weekly_Sales', 'Dept', 'Type']] 
e.g.2 下钻
mask_1 = train_merge_store['Store'] == 1
mask_2 = train_merge_store['IsHoliday'] == True
train_merge_store.loc[mask_1 & mask_2 ] 

# output 
	Store	Dept	Date	Weekly_Sales	IsHoliday	Type	Size
1	1	1	2010-02-12	46039.49	True	A	151315
31	1	1	2010-09-10	18194.74	True	A	151315
42	1	1	2010-11-26	18820.29	True	A	151315
47	1	1	2010-12-31	19124.58	True	A	151315
53	1	1	2011-02-11	37887.17	True	A	151315
...	...	...	...	...	...	...	...
10216	1	99	2011-09-09	40.00	True	A	151315
10217	1	99	2011-11-25	2400.00	True	A	151315
10221	1	99	2011-12-30	400.00	True	A	151315
10223	1	99	2012-02-10	1070.00	True	A	151315
10240	1	99	2012-09-07	0.05	True	A	151315
723 rows × 7 columns

2.4 替换/更新目标条件下的值

e.g. Store为1 同时 IsHoliday为True部分,对应的Weekly_Sales替换为0

a = train_merge_store.copy()  # 这样保持train_merge_store数据不变,便于后续使用
a.loc[mask_1 & mask_2, 'Weekly_Sales']=1

# output a.head()

	Store	Dept	Date	Weekly_Sales	IsHoliday	Type	Size
0	1	1	2010-02-05	24924.50	False	A	151315
1	1	1	2010-02-12	1.00	True	A	151315
2	1	1	2010-02-19	41595.55	False	A	151315
3	1	1	2010-02-26	19403.54	False	A	151315
4	1	1	2010-03-05	21827.90	False	A	151315

2.5 生成新的字段的标签呢?

e.g. 把store的Trype为A的打标为“大商超”,B的“超市”,C的“小便利店”

方法1。思路:先生成在修改。适合替换频次较少条件的情况
train_merge_store['Type中文名'] = train_merge_store.Type.copy() 
train_merge_store.loc[train_merge_store['Type中文名']=='A', 'Type中文名']='大商超'
train_merge_store.loc[train_merge_store['Type中文名']=='B', 'Type中文名']='超市'
train_merge_store.loc[train_merge_store['Type中文名']=='C', 'Type中文名']='小便利店'
方法2。思路:用numpy.select。适合多条件&复杂条件,多种判定逻辑的情况。并且由于使用numpy处理速度回更快
mask_dsc = train_merge_store['Type']=='A'
mask_cs = train_merge_store['Type']=='B'

train_merge_store['Type中文名']=np.select(
		
		[mask_dsc, mask_cs], # 条件列表
		['大商超', '超市'],  # 结果列表
		default='小便利店'  # 兜底逻辑。这是很有用的地方。
		)
train_merge_store[['Type中文名', 'Type']].drop_duplicates()  # 检查结果

# output
	Type中文名	Type
0	大商超	A
20482	超市	B
286548	小便利店	C

三、如何实现excel透视表的统计分析

首先进一步的进行数据预处理

train_merge_store['Date'] = pd.to_datetime(train_merge_store['Date'], format='%Y-%m-%d')
train_merge_store['Year'] = train_merge_store['Date'].dt.year
train_merge_store['quarter'] = train_merge_store['Date'].dt.quarter
train_merge_store.head()

# output
	Store	Dept	Date	Weekly_Sales	IsHoliday	Type	Size	Type中文名	Year	quarter
0	1	1	2010-02-05	24924.50	False	A	151315	大商超	2010	1
1	1	1	2010-02-12	46039.49	True	A	151315	大商超	2010	1
2	1	1	2010-02-19	41595.55	False	A	151315	大商超	2010	1
3	1	1	2010-02-26	19403.54	False	A	151315	大商超	2010	1
4	1	1	2010-03-05	21827.90	False	A	151315	大商超	2010	1

3.1 基础透视表

pt = train_merge_store.pivot_table(index=['Type中文名', 'quarter'], columns='Year', values='Weekly_Sales', aggfunc='sum')

在这里插入图片描述

3.2 增加行总计

pt['All'] = pt.sum(axis=1)

在这里插入图片描述

3.3 增加行总计百分比

pt['%_total_quarter'] = pt['All']/ pt['All'].sum()

在这里插入图片描述

3.4 增加按照Type大小的分组 行总计百分比

pt['%_level_quarter'] = pt['All'] / pt.groupby(level=0)['All'].sum()

在这里插入图片描述

3.5 筛选出type里的销售额前2的季度

pt['All'].groupby(level=0, group_keys=False).nlargest(2)

# output
Type中文名  quarter
大商超      3          2.370451e+09
         2          2.349429e+09
小便利店     3          2.248494e+08
         2          2.224036e+08
超市       3          1.088404e+09
         2          1.081397e+09
Name: All, dtype: float64

附注
该函数该如何理解?
首先 level=0什么意思?答:在grouby函数中,必须指定一个维度进行group,可以是按照index的直接的字段名,也可是按照index的level层级
其次 不设置groupby_keys=False会怎样?会出现2个’Type中文名’index。
还有另外一种方法,即通过sort_values和head函数实现,具体见<链接>

3.6 进行排名

pt['rank'] = pt['All'].groupby('Type中文名').rank(method='first', ascending=True)

在这里插入图片描述

附注 pandas.DataFrame.rank() method参数

first:类似SQL的 ROW_NUMBER(),即使有相同的数字也会一次升序/降序反馈排名
dense:类似SQL的 DENSE_RANK(),
min:类似SQL的 RANK(),
average:

df = pd.DataFrame(data={'Animal': ['cat', 'penguin','dog', 'spider', 'snake'],
                       'Number_legs': [4,2,4,8,np.nan]})
                       
df['default_rank'] =df['Number_legs'].rank()  # 'average'
df['max_rank'] =df['Number_legs'].rank(method='max')
df['min_rank'] =df['Number_legs'].rank(method='min')
df['dense_rank'] =df['Number_legs'].rank(method='dense')
df['first_rank'] =df['Number_legs'].rank(method='first')

在这里插入图片描述

3.7 获取最大值&新建最大值列

pt['max'] = pt.groupby('Type中文名')['All'].transform('max')

在这里插入图片描述

3.8 获取&新建sub level total

subtotal = pt.groupby('Type中文名').sum()
subtotal.index = pd.MultiIndex.from_tuples( (i, 'subtotal') for i in subtotal.index)
pd.concat([pt, subtotal])

在这里插入图片描述

3.9 如何优化排序?

pd.concat([pt, subtotal]).sort_index(ascending=True) # ascending=True即升序排序
# 除了按照index排序外,pandas还提供了sort_values,可以按照某行/某些行进行升序降序排序

在这里插入图片描述

四、以上即为python在替换excel统计统计分析上的常用逻辑和代码。此外还有一些常见的疑问解析

4.1 axis=1或者axis=0的易混淆点
test = pd.DataFrame({'col1': [42,13, np.nan, np.nan],
                    'col2': [4,12, np.nan, np.nan],
                    'col3': [25,61, np.nan, np.nan]})
print(test, '\n', '*'*10, '\n', 
      test.sum(axis=0), '\n', '*'*10, '\n', 
      test.dropna(axis=0, how='any'), '\n', '*'*10, '\n', 
     )
## 情况一 数字运算,axis=0意味着along the rows(column wise)
## 情况二 删减,axis=0意味着row wise
# output
   col1  col2  col3
0  42.0   4.0  25.0
1  13.0  12.0  61.0
2   NaN   NaN   NaN
3   NaN   NaN   NaN 
 ********** 
 col1    55.0
col2    16.0
col3    86.0
dtype: float64 
 ********** 
    col1  col2  col3
0  42.0   4.0  25.0
1  13.0  12.0  61.0 
 ********** 
4.2 apply和transform的区别
4.2.1 transfrom无法进行聚合,apply不一定

pt.groupby('Type中文名')['All'].apply('max')   # 作为对比pt.groupby('Type中文名')['All'].transform('max')返回的行数则和pt一样


## 所以transform可以这样使用,但apply不行。用于获取最大值
pt[ pt.groupby('Type中文名')['All'].transform('max') == pt['All'] ]

# output
		Year	2010	2011	2012	All	%_total_quarter	%_level_quarter	rank	max
Type中文名	quarter								
大商超	3	3.826937e+08	4.133643e+08	3.891675e+08	2.370451e+09	0.175922	0.273660	4.0	2.370451e+09
小便利店	3	3.629038e+07	3.849747e+07	3.763684e+07	2.248494e+08	0.016687	0.277247	4.0	2.248494e+08
超市	3	1.785041e+08	1.874967e+08	1.782014e+08	1.088404e+09	0.080775	0.272006	4.0	1.088404e+09
4.2.2 apply允许对dataframe的多个列进行操作,而transform不行
def mu_func(df):
     return df['All'] - df['max']
pt.groupby(level=0).apply(mu_func)

在这里插入图片描述

4.3 pivot_table透视表还可以方面的同时进行包括但不限于 非重复计数、汇总等操作

train_merge_store.pivot_table(index=['Type中文名', 'quarter'],
                             columns='Year',
                             values=['Weekly_Sales', 'IsHoliday', 'Date', 'Type'],
                             aggfunc={'Weekly_Sales': 'sum',  # 求和
                                     'IsHoliday': set,  # 求不重复值
                                     'Date': 'count',  # 求频次/计数
                                     'Type': 'nunique'})  # 求非重复统计值

在这里插入图片描述

五、 后续思考

1)在透视表中,是否可以把字段’IsHoliday’由现在单行单个cell的N个值,展开成N行的1个cell1个值? 参考答案<链接>
2)在文本datafram中,是否可以标注出是否包含某些关键词?例如某列的值’ABCD’是否包含关键词’AB’和’CD’?
总结:快速准确的做到统计分析,是更集中地思考数据分析应用和可视化呈现的基础。也是跳出工具人的必要前提。

  • 6
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值