<jupyter_start><jupyter_text>Pandas DataFrame DataFrame 结构<jupyter_code>import numpy as np
import pandas as pd
# 创建一个符合正态分布的 10 支股票 5 天内的涨跌幅数据
stock_change = np.random.normal(0, 1, (10, 5))
stock_change
# 用 pandas 创建
pd.DataFrame(stock_change)
# 添加行（index）索引
label_index = ['股票{}'.format(i) for i in range(10)]
label_index
pd.DataFrame(stock_change, index=label_index)
# 添加列（columns）索引
date = pd.date_range(start='20220101', periods=5, freq='B')
date
stock_change = pd.DataFrame(stock_change, index=label_index, columns=date)
stock_change<jupyter_output><empty_output><jupyter_text>DataFrame 属性<jupyter_code># 形状
stock_change.shape  # (10, 5)
# 行索引
stock_change.index  # Index(['股票0', '股票1', '股票2', '股票3', '股票4', '股票5', '股票6', '股票7', '股票8', '股票9'], dtype='object')
# 列索引
stock_change.columns  
# 去除行列索引（ndarry）
stock_change.values
# 转置
stock_change.T<jupyter_output><empty_output><jupyter_text>DataFrame 的方法<jupyter_code># 返回前 n 行数据，参数中如果不指定，默认是 5 行
stock_change.head(3)
# 返回后 n 行数据，参数中如果不指定，默认是 5 行
stock_change.tail(2)<jupyter_output><empty_output><jupyter_text>DatatFrame 索引的设置<jupyter_code>stock_change
data = pd.DataFrame(stock_change)

data<jupyter_output><empty_output><jupyter_text>- 修改行列索引值<jupyter_code># 必须全部修改
label = ['股票_{}'.format(i) for i in range(10)]
label
data.index = label
data
# 不能直接用列表索引的方式修改！
data.index[3] = '股票四'<jupyter_output><empty_output><jupyter_text>- **重设索引**<jupyter_code>data.reset_index()
data
data.reset_index(drop=True)<jupyter_output><empty_output><jupyter_text>- **将某列设置为新索引**<jupyter_code># 【重点】如果以字典的形式传入，字典的 value 默认会作为列名出现！
data = pd.DataFrame({'month': [1, 4, 7, 10],
                    'year': [2012, 2014, 2013, 2014],
                    'sale':[55, 40, 84, 31]})

data
# 将 month 设置为新索引
tmp = data.set_index('month')
tmp
tmp.index
data.set_index('month', drop=False)<jupyter_output><empty_output><jupyter_text>** 与此同时，如果设置多个列为索引，这样DataFrame就变成了一个具有MultiIndex的DataFrame。**<jupyter_code>type(data.index)
# 	**与此同时，如果设置多个列为索引，这样DataFrame就变成了一个具有MultiIndex的DataFrame。**
tmp = data.set_index(['month', 'year'])
tmp
tmp.index<jupyter_output><empty_output><jupyter_text>MultiIndex 与 Panel MultiIndexMultiIndex 是**三维的数据结构**;**多级索引（也称层次化索引）是 pandas 的重要功能，可以在 Series、DataFrame 对象上拥有 2个以及2个以上的索引。**<jupyter_code>tmp.index
tmp.index.names
tmp.index.levels<jupyter_output><empty_output><jupyter_text>**MultiIndex 的创建**<jupyter_code>arrays = [[1, 1, 2, 2], ['red', 'blue', 'red', 'blue']]
pd.MultiIndex.from_arrays(arrays, names=('number', 'color'))<jupyter_output><empty_output><jupyter_text>SeriesSeries 是一个类似于**一维数组**的数据结构，它能够保存任何类型的数据，比如整数、字符串、浮点数等，**主要由一组数据和与之相关的索引两部分构成。** Series 的创建```python 导入pandasimport pandas as pdpd.Series(data=None, index=None, dtype=None)```- 参数：    - `data`：传入的数据，可以是ndarray、list等    - `index`：索引，必须是唯一的，且与数据的长度相等。如果没有传入索引参数，则默认会自动创建一个从0-N的整数索引。    - `dtype`：数据的类型 **通过已有数据创建**<jupyter_code>pd.Series(np.arange(10))<jupyter_output><empty_output><jupyter_text>**指定索引**<jupyter_code>pd.Series([6.7,5.6,3,10,2], index=[1,2,3,4,5])<jupyter_output><empty_output><jupyter_text>**通过字典数据创建**<jupyter_code>color_count = pd.Series({'red':100, 'blue':200, 'green': 500, 'yellow':1000})
color_count<jupyter_output><empty_output><jupyter_text>Series 的属性为了更方便地操作 Series 对象中的索引和数据，**Series 中提供了两个属性 index 和 values**<jupyter_code>color_count.index
color_count.values<jupyter_output><empty_output><jupyter_text>**使用索引来获取数据**<jupyter_code>color_count[2]<jupyter_output><empty_output><jupyter_text>基本数据操作 索引Numpy 当中我们已经讲过使用索引选取序列和切片选择，pandas 也支持类似的操作，也可以直接使用列名、行名称，甚至组合使用。| 方法                                                         | 描述                                                || ------------------------------------------------------------ | --------------------------------------------------- || `DataFrame['列名']['行名']`                                  | 直接使用行列名称索引**注意：先列后行**        || `DataFrame.loc['行名']['列名']``DataFrame.loc['行名', '列名']` | 如果使用名称索引且**先行后列**                      || `DataFrame.iloc[index][colnums]``DataFrame.iloc['行名', '列名']` | 使用**下标**索引且**先行后列**                      || `DataFrame.ix[下标或名称, 下标或名称]`                       | **ix组合索引**；注意，0.20.0 之后的版本可能不再支持 |---<jupyter_code># 读取文件
data = pd.read_csv('./data/stock_day/stock_day.csv')

# 删除一些列，让数据更简单些，再去做后面的操作，axis= 表示按列删除
data = data.drop(["ma5","ma10","ma20","v_ma5","v_ma10","v_ma20"], axis=1)
data<jupyter_output><empty_output><jupyter_text>直接使用行列名称索引**注意：先列后行**<jupyter_code>data['close']['2018-02-23']
# data['close', '2018-02-23'] ERROR<jupyter_output><empty_output><jupyter_text>使用 loc 只能指定行列索引的名字<jupyter_code>data.loc['2018-02-27':'2018-02-22']['open']
data.loc['2018-02-27':'2018-02-22', 'open']<jupyter_output><empty_output><jupyter_text>使用**下标**索引且**先行后列**<jupyter_code># 使用iloc可以通过索引的下标去获取
# 获取前3天数据,前5列的结果
data.iloc[:3][:5] 但是这种好像有点问题，输出不是前 5 列
# 使用iloc可以通过索引的下标去获取
# 获取前3天数据,前5列的结果
data.iloc[:3, :5]<jupyter_output><empty_output><jupyter_text>【已废弃】使用ix组合索引Warning:Starting in 0.20.0, the .ix indexer is deprecated, in favor of the more strict .iloc and .loc indexers.获取行第1天到第4天，['open', 'close', 'high', 'low']这个四个指标的结果<jupyter_code># 使用ix进行下表和名称组合做引
data.ix[0:4, ['open', 'close', 'high', 'low']]
# 推荐使用loc和iloc来获取的方式
data.loc[data.index[0:4], ['open', 'close', 'high', 'low']]
data.iloc[0:4, data.columns.get_indexer(['open', 'close', 'high', 'low'])]<jupyter_output><empty_output><jupyter_text>赋值操作- 将 DataFrame 中的某一列都修改为一个指定的值<jupyter_code>data.head()
data['open']
# 方法 1
data['open'] = 66
data.head()

# 方法 2
data.open = 77
data.head()<jupyter_output><empty_output><jupyter_text>排序排序有两种形式，一种对于索引进行排序，一种对于内容进行排序- DataFrame 排序    `DataFrame.sort_values(by='列名', ascending=Bool值)` 按照单个建或多个键进行排序    - `by` 按哪一列进行排序，可以使用列表来指定按多个列进行排序    - `ascending` 默认为 True 按升序排列；如果指定为 False 则按降序排列<jupyter_code>data
data.sort_values('high', ascending=False).head()
# 按照多个键进行排序
data.sort_values(by=['open', 'high']).head()<jupyter_output><empty_output><jupyter_text>**按照索引 (index) 排序**这个股票的日期索引原来是从大到小，现在重新排序，从小到大<jupyter_code>data.sort_index().head()<jupyter_output><empty_output><jupyter_text>Series 排序因为 Series 只有一列，所以在排序时不需要参数<jupyter_code>data.volume.sort_values().head()
data['p_change'].sort_index().head()<jupyter_output><empty_output><jupyter_text>逻辑运算- **基本操作**    可以直接使用运算符进行操作，返回对应布尔值的结果<jupyter_code>data
data['high'] > 20
# 逻辑判断的结果可以作为筛选的依据
data[data['high'] > 20].head()
# 完成多个逻辑判断
data[(data['high'] > 20) & (data['high'] < 23)].head()<jupyter_output><empty_output><jupyter_text>**逻辑运算函数**- `query(expr)` exper 查询字符串<jupyter_code>data.query('high > 20 & high < 23').head()<jupyter_output><empty_output><jupyter_text>`data.isin(values)` 值是否在 values 中- values 值或值的列表<jupyter_code>data['turnover'].isin([4.19, 2.39])
data[data['turnover'].isin([4.19, 2.39])].head()<jupyter_output><empty_output><jupyter_text>统计运算> 注意：是按列（columns）进行运算的，因为按行（index）是没有意义的- **DataFrame.describe()**    综合分析: 能够直接得出很多统计结果 `count`, `mean`, `std`, `min`, `max` 等<jupyter_code>data.describe()<jupyter_output><empty_output><jupyter_text>- **累计统计函数**    | **函数**  | **作用**                      |    | --------- | ----------------------------- |    | `cumsum`  | 计算前 1/2/3/…/n 个数的和     |    | `cummax`  | 计算前 1/2/3/…/n 个数的最大值 |    | `cummin`  | 计算前 1/2/3/…/n 个数的最小值 |    | `cumprod` | 计算前 1/2/3/…/n 个数的积     |<jupyter_code># 先对时间进行排序
data['p_change'].sort_index().cumsum().plot()<jupyter_output><empty_output><jupyter_text>自定义运算- `apply(func, axis=0)`    - `func`:自定义函数    - `axis=0`:默认是列，`axis=1` 为行进行运算<jupyter_code>data[['open', 'close']].apply(lambda x: x.max() - x.min(), axis=0)<jupyter_output><empty_output><jupyter_text>Pandas 画图- DataFrame 画图`DataFrame.plot(x=None, y=None, kind='line')`- `kind` string 类型    - `'line'` 折线图    - `'bar'`柱状图    - `'scatter'`散点图    - `'barh'`水平条形图 （http://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.plot.barh.html）    - `'hist'`直方图    - `'pie'`饼图> 更多细节：https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.plot.html?highlight=plotpandas.DataFrame.plot<jupyter_code>data.head()
data.plot(x='p_change', y='low', kind='scatter')
data.plot(x='p_change', y='turnover', kind='scatter')<jupyter_output><empty_output><jupyter_text>--- 文件的读取与存储我们的数据大部分存在于文件当中，Pandas 支持复杂的IO操作，Pandas 的 API 支持众多的文件格式，如 CSV、SQL、XLS、JSON、HDF5。> 注：最常用的 HDF5 和 CSV 文件 CSA- **CSV 文件的读取**    `pandas.read_csv(filepath_or_buffer, sep =',', usecols=[])`    - `filepath_or_buffer` 文件路径    - `sep`  分隔符，默认用 `,`隔开    - `usecols` 指定读取的列名，列表形式<jupyter_code># 举例：读取之前的股票的数据
# 读取文件,并且指定只获取'open', 'close'指标
data = pd.read_csv('./data/stock_day/stock_day.csv', usecols=['open', 'close'])

data<jupyter_output><empty_output><jupyter_text>- **CSV 文件的写入**    `DataFrame.to_csv(path_or_buf=None, sep=',', columns=None, header=True, index=True, mode='w', encoding=None)`    - `path_or_buf` 文件路径    - `sep` 分隔符，默认用 `,` 隔开    - `columns` 选择需要写入文件的列索引    - `header` 默认为 True，是否写进**列（columns）**索引值    - `index` 是否写进**行（index）**索引    - `mode` 'w'：重写；'a' 追加在文件末尾<jupyter_code># 选取10行数据保存,便于观察数据
data[:10].to_csv('./data/test/test.csv')
# 选取10行数据保存,便于观察数据
data[:10].to_csv('./data/test/test.csv', columns=['open'])
# 会发现将索引存入到文件当中，变成单独的一列数据。如果需要删除，可以指定index参数,删除原来的文件，重新保存一次。
# 选取10行数据保存,便于观察数据
data[:10].to_csv('./data/test/test.csv', columns=['open'], index=False)<jupyter_output><empty_output><jupyter_text>--- HDF5- **HDF5 特点**    - **【重点】HDF5 存储的是三维数据，可以说是同时存放多个二维数据**    - HDF5 是二进制文件    - 文件后缀是 `h5`**注意：优先选择使用 HDF5 文件存储**- HDF5 在存储的时候支持压缩，**使用的方式是 blosc，这个是速度最快**的也是 pandas 默认支持的- 使用压缩可以**提磁盘利用率，节省空间**- HDF5 还是跨平台的，可以轻松迁移到 hadoop 上面- **HDF5 文件的读取**    `pandas.read_hdf(path_or_buf，key=None，**kwargs)`    - `path_or_buffer` 文件路径    - `key` 读取的键（二维数据的名称），如果一个 h5 文件中有**多个**二维数据，则需指定此键。否则报错<jupyter_code># 读取
data = pd.read_hdf('./data/stock_day/hdf5/day_high.h5')
data.head()
# 写入, 写入的时候必须指定 key ！
data[:10].to_hdf('./data/test/test.h5', key='key1')
# 后 10 行
data[-10:]
# 后 10 行
# 写入, 写入的时候必须指定 key ！
data[-10:].to_hdf('./data/test/test.h5', key='key2')
# 此时文件中有多个二维数据，如果不指定 key 会报错
data = pd.read_hdf('./data/test/test.h5')
# 此时文件中有多个二维数据，如果不指定 key 会报错
data = pd.read_hdf('./data/test/test.h5', key='key2')
data<jupyter_output><empty_output><jupyter_text>JSONJSON 是我们常用的一种数据交换格式，前面在**前后端的交互**经常用到，也会在存储的时候选择这种格式。所以我们需要知道 Pandas 如何进行读取和存储 JSON 格式。- **JSON 文件的读取**`json_read = pd.read_json("./data/Sarcasm_Headlines_Dataset.json", orient="records", lines=True)`将 JSON 格式转换成默认的Pandas DataFrame 格式- `orient` 指定存储的 json 格式，通常使用 `records`- `lines` 指定按照行去变成一个样本> 这里使用一个新闻标题讽刺数据集，格式为json。>> `is_sarcastic`：1讽刺的，否则为0；>> `headline`：新闻报道的标题；>> `article_link`：链接到原始新闻文章<jupyter_code>import pandas as pd

json_read = pd.read_json('./data/Sarcasm_Headlines_Dataset.json', orient='records', lines=True)
json_read
json_read.head(10).to_json('./data/test/test.json', orient='records', lines=True)<jupyter_output><empty_output><jupyter_text>缺失值处理处理缺失值的思路：- 删除带有缺失值的样本- 插补/替换**当我们读取的数据中有缺失值时，pandas 中表示为为 `NaN` (Not a Number)。在处理缺失值时，需要有一下步骤：**1. **判断数据中是否存在** `NaN`    - `pandas.isnull(DataFrame)` 会在**是**缺失值的地方标记为 True    - `pandas.notnull(DataFrame)` 会在**不是**缺失值的地方标记为 True2. **处理缺失值**    - **删除含有缺失值的样本**        `DataFrame.dropna(inplace=False)`        `inplace` 如果标记为 True 会直接修改本 DataFrame，而如果为 False 则不会直接修改本数组，而是**返回**新数组（默认即为 False）            - **替换/插补**        `DataFrame.fillna(value=要填补的值, inplace=False)`        `inplace` 如果标记为 True 会直接修改本 DataFrame，而如果为 False 则不会直接修改本数组，而是**返回**新数组（默认即为 False）        3. **如果缺失值没有标记为 `NaN`，比如空格、逗号等**<jupyter_code>import pandas as pd
# 1. 读取数据
data = pd.read_csv('./data/IMDB-Movie-Data.csv')
data
# 2. 判断缺失值是否存在（法一）
pd.isna(data)  # 在是 NaN | None 的地方标记为 True
# 2. 判断缺失值是否存在（法二）
pd.notna(data)  # 在不是 NaN | None 的地方标记为 True
import numpy as np
np.any(pd.isna(data))  # 借助 numpy 的 any 函数进行判断
np.all(pd.notna(data))
pd.isna(data).any()  # 会按照列进行返回，那些列中有 True
pd.notna(data).all()
# 删除带有缺失值的样本, 参数 inplace 默认为 False 即返回新 DataFrame
data.dropna()
# 3. 处理缺失值
# 使用插补法分别处理含有缺失值的两列（采用平均值）
data['Revenue (Millions)'].fillna(value=data['Revenue (Millions)'].mean(), inplace=True)
pd.isnull(data).any()
# 使用插补法分别处理含有缺失值的两列（采用平均值）
data['Metascore'].fillna(value=data['Metascore'].mean(), inplace=True)
pd.isnull(data).any()<jupyter_output><empty_output><jupyter_text>3. **如果缺失值没有标记为 `NaN`，比如空格、问号等**    - 将其他字符换成 `NaN`，也就是 `numpy.nan` 再进行替换；**注意：是 `numpy` 不是 `pandas`！**        替换的 API：`DataFrame.replace(to_place='要替换的字符', value=NUMPY.nan)`<jupyter_code># 全局取消证书验证
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

data = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/breast-cancer-wisconsin.data')
data
# 把一些其它值标记的缺失值，替换成 np.nan
data = data.replace(to_replace='?', value=np.nan)

# 这里采用删除缺失值的方式
data = data.dropna()<jupyter_output><empty_output><jupyter_text>数据离散化**pandas 中进行数据离散化的步骤：**1. **分组**    - 自动分组：`Series = pandas.pcut(data, q=分成几组)`    - 自定义分组：`Series = pandas.cut(data, [])`        > 使用 `Series.value_counts()` 方法可以查看每个分组中元素的数量          2. **将分组好的结果转换为 one-hot 编码或哑变量**    - `pandas.get_dummies(Series, prefix='前缀')`<jupyter_code>import pandas as pd
# 1. 读取数据
data = pd.read_csv('./data/stock_day/stock_day.csv', usecols=['p_change'])
type(data)
# 2. 进行分组
sr = pd.qcut(x=data['p_change'], q=10)  # qcut 自动分组
sr
sr = pd.cut(x=data['p_change'], bins=[-100, -7, -5, -3, 0, 3, 5, 7, 100])  # cut 自定义分组
sr
sr.value_counts()  # 查看每个分组中元素的数量
# 3. 将分组好的结果转换为 one-hot 编码或哑变量
df = pd.get_dummies(data=sr, prefix='股票')
df
type(df)<jupyter_output><empty_output><jupyter_text>合并处理> 回顾 numpy 拼接方法：>> - 水平拼接>>     `numpy.hstack()`>> - 竖直拼接>>     `numpy.vstack`>> - 指定拼接方向>>     `numpy.concatnate()`**pandas 拼接：**- `pandas.concat([data1, data2, ...], axis=0)` 默认 axis=0 即竖直拼接，指定为 1 可按行拼接<jupyter_code># 比如拼接前五行和后五行
pd.concat([sr.head(), sr.tail()], axis=0)
pd.concat([sr.head(), sr.tail()], axis=1)<jupyter_output><empty_output><jupyter_text>`pandas.merge(左表, 右表, how='合并方式', on=['键1', '键2', ...])` 将表按照两组数据的共同键值对合并（类似数据库中连接）- `how` 表示按什么方式连接，可以有：`left` | `right` | `inner` | `outer`，默认为 `inner`<jupyter_code>tb_left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
                        'key2': ['K0', 'K1', 'K0', 'K1'],
                        'A': ['A0', 'A1', 'A2', 'A3'],
                        'B': ['B0', 'B1', 'B2', 'B3']})

tb_right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
                        'key2': ['K0', 'K0', 'K0', 'K0'],
                        'C': ['C0', 'C1', 'C2', 'C3'],
                        'D': ['D0', 'D1', 'D2', 'D3']})
# 默认内连接
pd.merge(left=tb_left, right=tb_right, on=['key1', 'key2'])
# 左连接
pd.merge(left=tb_left, right=tb_right, how='left', on=['key1', 'key2'])
# 右连接
pd.merge(left=tb_left, right=tb_right, how='right', on=['key1', 'key2'])
# 外连接
pd.merge(left=tb_left, right=tb_right, how='outer', on=['key1', 'key2'])<jupyter_output><empty_output><jupyter_text>交叉表和透视表**应用场景：找到两个变量之间的关系**- **交叉表：**    交叉表用于**计算一列数据对于另外一列数据的分组个数(用于统计分组频率的特殊透视表)**    - `pd.crosstab(value1, value2)`    - **透视表：**    透视表是将原有的 **DataFrame 的列分别作为行索引和列索引，然后对指定的列应用聚集函数**    - `DataFrame.pivot_table([], index=[])`比如：探究股票的涨跌与星期几有关？以下图当中表示，week 代表星期几，1,0 代表这一天股票的涨跌幅是好还是坏，里面的数据代表比例可以理解为所有时间为星期一等等的数据当中涨跌幅好坏的比例<jupyter_code># 目的：星期几以及涨跌幅是否好坏
# 最终形式：`pd.crosstab(星期数据列, 涨跌幅数据列)`

#  1. 准备数据
stock_day = pd.read_csv('./data/stock_day/stock_day.csv')
stock_day

# 2. 将日期处理为星期几
date = pd.to_datetime(stock_day.index)
weekday = date.weekday + 1  # 注意，这里的星期是从 0 开始的
weekday
# 3. 添加星期几为新数据列
stock_day['weekday'] = weekday
stock_day
# 4. 将涨跌处理为 0 或 1（0 位跌，1 位涨），并添加为新列
stock_day['pona'] = np.where(stock_day.p_change > 0, 1, 0)
stock_day
# 5. 调用交叉表 API，输出涨的数量和跌的数量
ct = pd.crosstab(stock_day.weekday, stock_day.pona)
ct
# 6.转换为百分比（每行的总数 / 每行）
ct_div = ct.sum(axis=1)
ct_div
result = ct.div(ct_div, axis=0)  # 注意行列
result
# 7. 绘制图像
result.plot(kind='bar', stacked=True)  # 第二个参数为是否堆叠显示
# 使用透视表，相当于

#  1. 准备数据
stock_day
stock_day.pivot_table(['pona'], index=['weekday'])  # 透视表直接能出值为 1 所占的比例<jupyter_output><empty_output><jupyter_text>分组与聚合交叉表和透视表其实也是一种分组与聚合。**pandas 中的数据很像数据库总的** `GROUP BY`，可以对 DataFrame 和 Series 进行操作- `DataFrame.groupby(by=[分组的数据], as_index=False)`  **分组**    **注意！分组后只会返回一个 groupBy 对象，并不会返回 DataFrame 或者 Series，必须再调用聚合函数才可以进行显示！**，聚合函数也就是 `min() | max() | count() | sum() ...` 这些<jupyter_code># 案例:不同颜色的不同笔的价格数据
col =pd.DataFrame({'color': ['white','red','green','red','green'], 'object': ['pen','pencil','pencil','ashtray','pen'],'price1':[5.56,4.20,1.30,0.56,2.75],'price2':[4.75,4.12,1.60,0.75,3.15]})
col
col.groupby(by=['color'])
# 按照 DataFrame 的方式分组（建议使用此方式）
col.groupby(by=['color'])['price1'].sum()
# 按照 Series 的方式分组，先取出需要的列
col['price1'].groupby(by=col['color']).mean()<jupyter_output><empty_output><jupyter_text>**【案例】星巴克零售数据**现在我们有一组关于全球星巴克店铺的统计数据，如果我想知道美国的星巴克数量和中国的哪个多，或者我想知道中国每个省份星巴克的数量的情况，那么应该怎么办？<jupyter_code>starbucks = pd.read_csv('./data/starbucks.csv')
starbucks.head()
# 按照国家分组，求出每个国家的星巴克零售店数量
sb_count = starbucks.groupby(by=['Country'])['Brand'].count()
# 进行画图
sb_count.sort_values(ascending=False).plot(kind='bar', figsize=(20, 8))
# 假设我们加入省市一起进行分组
sb_sount2 = starbucks.groupby(by=['State/Province', 'City']).count()  # 【重点】会返回类似 MultIndex 的结构

sb_sount2<jupyter_output><empty_output><jupyter_text>案例（电影数据分析）现在我们有一组从2006年到2016年1000部最流行的电影数据数据来源：https://www.kaggle.com/damianpanek/sunday-eda/data问题1：我们想知道这些电影数据中评分的平均分，导演的人数等信息，我们应该怎么获取？问题2：对于这一组电影数据，如果我们想rating，runtime的分布情况，应该如何呈现数据？问题3：对于这一组电影数据，如果我们希望统计电影分类(genre)的情况，应该如何处理数据？<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
import pandas as pd<jupyter_output><empty_output><jupyter_text>问题1：我们想知道这些电影数据中评分的平均分，导演的人数等信息，我们应该怎么获取？<jupyter_code># 1. 导入数据
movies = pd.read_csv('./data/IMDB-Movie-Data.csv')
movies.head()
# 2. 获取平均分
movies.Rating.mean()
# 3. 获取导演人数
len(movies.Director.unique())  # 方法一
np.unique(movies.Director).shape[0]  # 方法二<jupyter_output><empty_output><jupyter_text>对于这一组电影数据，如果我们想Rating，Runtime (Minutes)的分布情况，应该如何呈现数据？直接呈现，以直方图的形式<jupyter_code>movies.head()
movies['Rating'].plot(kind='hist', figsize=(20, 8))
# 使用 Matplotlib 进行绘制
plt.figure(figsize=(20, 8), dpi=80)
plt.hist(x=movies['Rating'], bins=20)

# 修改刻度问题
ticks = np.linspace(movies['Rating'].min(), movies['Rating'].max(), 21)
plt.xticks(ticks=ticks)
plt.grid(linestyle='--', alpha=0.5)
plt.show()<jupyter_output><empty_output><jupyter_text>问题三：对于这一组电影数据，如果我们希望统计电影分类(genre)的情况，应该如何处理数据？思路分析思路1、创建一个全为0的dataframe，列索引置为电影的分类，temp_df2、遍历每一部电影，temp_df中把分类出现的列的值置为13、求和<jupyter_code># 1. 拿到电影类型的 Series
movies['Genre']
# 2. 转换为列表格式
# 可以使用 for 循环，也可以使用列表生成式
genre = [i.split(',') for i in movies['Genre']]
genre
# 3. 统计电影种类共有多少
[j for i in genre for j in i]
movies_types = np.unique([j for i in genre for j in i])
movies_types
num_type = len(movies_types)
num_type
# 4. 创建一个全为 0 的 DataFrame，然后把每一个电影（行）对应的种类（列）置为 1
tmp_zeros_ndarry = np.zeros(shape=(movies.shape[0], num_type), dtype=np.int32)

df_gener = pd.DataFrame(tmp_zeros_ndarry, index=movies['Title'], columns=movies_types)
df_gener
# 5. 遍历每一部电影，genre 中把分类出现的列的值置为1
# df_gener.loc[[i for i in movies['Title']], genre] + 1 ERROR，因为一次只能索引一行

for i in range(movies.shape[0]):
    df_gener.loc[movies['Title'][i]][genre[i]] = 1

df_gener
# 6. 按列统计数量
df_gener.sum().sort_values(ascending=False)
# 7. 绘图
df_gener.sum().sort_values(ascending=False).plot(kind='bar', figsize=(20, 8), fontsize=22, colormap='cool')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.19308267552970504, 'fraction_numerical': 0.02518695471541338, 'mean_word_length': 5.90462531373252, 'pattern_counts': {'":': 0, '<': 191, '<?xml version=': 0, '>': 218, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '47', 'dir': 'jupyter-structured-clean-dedup', 'id': '194436', 'license': 'Apache-2.0', 'n_tokens_mistral': 10556, 'n_tokens_neox': 10993, 'n_words': 1118, 'path': '003 - Pandas/Pandas.ipynb', 'repo_name': 'NekoSilverFox/Data-Mining', 'size': '19256'}