D_Y_大师
类似这样的组合图,通常有多个轴或多个数据区域,我应该如何配置滚动条从而使其控制指定区域的滚动? "图片" (https://wmprod.oss-cn-shanghai.aliyuncs.com/images/20241221/c9cc6770cdc1f879be685987bb631828.png)
清晨我上码
导入数据import pandas as pd df = pd.read_excel('team.xlsx') df这是一个学生各季度成绩总表(节选),各列说明如下。name:学生的姓名,这列没有重复值,一个学生一行,即一条数据,共100条。team:所在的团队、班级,这个数据会重复。Q1~Q4:各个季度的成绩,可能会有重复值。查看数据类型print(type(df)) #查看df类型 <class 'pandas.core.frame.DataFrame'>查看数据df.head() #查看前5条 df.tail() #查看后5条 df.sample(5) #查看随机5条查看数据信息df.shape # (100, 6) 查看行数和列数 (100, 6) df.describe() # 查看数值型列的汇总统计 df.dtypes # 查看各字段类型 df.axes # 显示数据行和列名 df.columns # 列名df.info() # 查看索引、数据类型和内存信息 <class 'pandas.core.frame.DataFrame'> RangeIndex: 100 entries, 0 to 99 Data columns (total 6 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 100 non-null object 1 team 100 non-null object 2 Q1 100 non-null int64 3 Q2 100 non-null int64 4 Q3 100 non-null int64 5 Q4 100 non-null int64 dtypes: int64(4), object(2) memory usage: 4.8+ KBdf.describe() # 查看数值型列的汇总统计df.dtypes # 查看各字段类型 name object team object Q1 int64 Q2 int64 Q3 int64 Q4 int64 dtype: objectdf.columns # 列名 Index(['name', 'team', 'Q1', 'Q2', 'Q3', 'Q4'], dtype='object')建立索引实际上第一列name应当是实际的行索引,下面用代码实现:df.set_index('name', inplace=True) # 建立索引并生效 df team Q1 Q2 Q3 Q4 name Liver E 89 21 24 64 Arry C 36 37 37 57 Ack A 57 60 18 84 Eorge C 93 96 71 78 Oah D 65 49 61 86建立实际行索引之后,数字索引就没有了。数据查找选择列df['team'] name Liver E Arry C Ack A Eorge C Oah D .. Gabriel C Austin7 C Lincoln4 C Eli E Ben E选择行按数字索引选择 df[0:3] # 取前三行 df[0:10:2] # 在前10个中每两个取一个 df.iloc[:10,:] # 前10个按新索引选择df[df.index == 'Liver'] # 指定姓名 team Q1 Q2 Q3 Q4 name Liver E 89 21 24 64同时指定行和列df.loc['Arry','Q1':'Q3'] # 只看Arry的三个季度成绩 df.loc['Liver':'Eorge', 'Q1':'Q4'] #从Liver到Eorge的四个季度成绩 Q1 36 Q2 37 Q3 37 Name: Arry, dtype: object Q1 Q2 Q3 Q4 name Liver 89 21 24 64 Arry 36 37 37 57 Ack 57 60 18 84 Eorge 93 96 71 78条件选择单一条件df[df.Q1 > 94] # Q1列大于94的 team Q1 Q2 Q3 Q4 name Max E 97 75 41 3 Elijah B 97 89 15 46 Aaron A 96 75 55 8 Lincoln4 C 98 93 1 20 df[df.team == 'C'] # team列为'C'的 team Q1 Q2 Q3 Q4 name Arry C 36 37 37 57 Eorge C 93 96 71 78 Harlie C 24 13 87 43 Archie C 83 89 59 68复合查询df[(df['Q1'] > 90) & (df['team'] == 'C')] # and关系 df[df['team'] == 'C'].loc[df.Q1>90] # 多重筛选 team Q1 Q2 Q3 Q4 name Eorge C 93 96 71 78 Alexander C 91 76 26 79 Lincoln4 C 98 93 1 20
清晨我上码
pandas-profilingpandas_profiling 官网(https://pypi.org/project/pandas-profiling/)大概在23年4月前发出如下公告:Deprecated 'pandas-profiling' package, use 'ydata-profiling' instead意味着pandas-profiling不能再用啦,要改用ydata-profiling。所以不用再找更改pandas-profiling版本等相关的教程,直接拥抱新版本的 ydata-profiling即可,功能比原来的更强大。ydata-profilingydata-profiling的主要目标是提供一种简洁而快速的探索性数据分析(EDA)体验。就像pandas中的df.describe()函数一样,ydata-profiling可以对DataFrame进行扩展分析,并允许将数据分析导出为不同格式,例如html和json。该软件包输出了一个简单而易于理解的数据集分析结果,包括时间序列和文本数据。安装pip install ydata-profiling使用方式import numpy as np import pandas as pd from ydata_profiling import ProfileReport df = pd.DataFrame(np.random.rand(100, 5), columns=['a','b','c','d','e']) profile = ProfileReport(df, title="Profiling Report")输出结果一些关键属性:类型推断 (Type inference):自动检测列的数据类型(分类、数值、日期等)警告 (Warning):对数据中可能需要处理的问题/挑战的概要(缺失数据、不准确性、偏斜等)单变量分析 (Univariate analysis):包括描述性统计量(平均值、中位数、众数等)和信息可视化,如分布直方图多变量分析 (Multivariate analysis):包括相关性分析、详细分析缺失数据、重复行,并为变量之间的交互提供视觉支持时间序列 (Time-Series):包括与时间相关的不同统计信息,例如自相关和季节性,以及ACF和PACF图。文本分析 (Text analysis):最常见的类别(大写、小写、分隔符)、脚本(拉丁文、西里尔文)和区块(ASCII、西里尔文)文件和图像分析 (File and Image analysis):文件大小、创建日期、指示截断图像和存在EXIF元数据的指示比较数据集 (Compare datasets):一行命令,快速生成完整的数据集比较报告灵活的输出格式 (Flexible output formats):所有分析结果可以导出为HTML报告,便于与各方共享,也可作为JSON用于轻松集成到自动化系统中,还可以作为Jupyter Notebook中的小部件使用报告还包含三个额外的部分:概述 (Overview):主要提供有关数据集的全局详细信息(记录数、变量数、整体缺失值和重复值、内存占用情况)警告 (Alerts):一个全面且自动的潜在数据质量问题列表(高相关性、偏斜、一致性、零值、缺失值、常数值等)重现 (Reporduction):分析的技术细节(时间、版本和配置)ydata-profiling实际应用iris鸢尾花数据集分析from sklearn.datasets import load_iris iris = load_iris() iris import pandas as pd df = pd.DataFrame(data=iris.data, columns=[name.strip(' (cm)') for name in iris.feature_names]) # DISPLAY FIRST 5 RECORDS OF THE # DATAFRAME df['species'] = iris.target df import ydata_profiling as yp profile = yp.ProfileReport(df.iloc[:,:4], title="Profiling Report") # 通过小部件使用 profile.to_widgets() # 生成嵌入式HTML报告 profile.to_notebook_iframe()ydata_profiling 可以在jupyter notebook中内嵌HTML报告,也可以使用to_file生产HTML或者json格式文件。
清晨我上码
这个例子让我了解一个在实际任务中如何利用数组操作。首先一个最简单的随机漫步:从0开始,步幅为1和-1,以相同的概率出现。下面是纯python的实现方法,1000步:import random position = 0 walk = [position] steps = 1000 for i in range(steps): step = 1 if random.randint(0, 1) else -1 position += step walk.append(position)import matplotlib.pyplot as plt %matplotlib inlinewalk[:5][0, -1, -2, -3, -2]plt.plot(walk[:100])[<matplotlib.lines.Line2D at 0x1062588d0>]随机漫步其实就是一个简单的累加。而用np.random能更快:import numpy as npnsteps = 1000 draws = np.random.randint(0, 2, size=nsteps) steps = np.where(draws > 0, 1, -1)walk = steps.cumsum()我们能直接从中得到一些统计数据,比如最大值和最小值:walk.min()-57walk.max()7一个更复杂的统计值是在哪一步random walk到达了一个指定值。我们想知道从0走出10步用了多久,不论是正方形还是负方向。np.abs(walk) >= 10给我们一个布尔数组告诉我们是否超过10,但我们想要第一次出现的10或-10。因此,我们利用argmax来计算,这个会返回布尔数组中最大值的索引(Ture是最大值):(np.abs(walk) >= 10).argmax()71注意,使用argmax并不总是效率的,因为它总会搜索整个数组。在这里例子里,一旦True被找到了,我们就返回为最大值。Simulating Many Random Walks at Once(一次模拟多个随机漫步)假设我们一次要模拟5000个随机漫步。传入一个2-tuple,np.random会生成一个二维数组,然后我们沿着每行来计算累加,这样就能一次模拟5000个:nwalks = 5000 nsteps = 1000 draws = np.random.randint(0, 2, size=(nwalks, nsteps)) # 0 or 1 steps = np.where(draws > 0, 1, -1) walks = steps.cumsum(1)walksarray([[ -1, -2, -3, ..., -24, -25, -26], [ -1, -2, -1, ..., -10, -9, -8], [ 1, 0, 1, ..., -4, -3, -4], ..., [ 1, 0, 1, ..., 52, 51, 52], [ -1, 0, 1, ..., -26, -25, -26], [ -1, 0, -1, ..., -30, -29, -30]])找到所有漫步中的最大值和最小值:walks.max()115walks.min()-129在这些漫步模拟中,我们想找到30步以上的。用any方法:hits30 = (np.abs(walks) >= 30).any(1) hits30array([ True, False, False, ..., True, True, True], dtype=bool)hits30.sum()3423上面的step只是像翻硬币一样二选一,我们也可以用一个概率函数来生成:steps = np.random.normal(loc=0, scale=0.25, size=(nwalks, nsteps)
清晨我上码
10.4 Pivot Tables and Cross-Tabulation(数据透视表和交叉表)Pivot Tables(数据透视表)是一种常见的数据汇总工具,常见与各种spreadsheet programs(电子表格程序,比如Excel)和一些数据分析软件。它能按一个或多个keys来把数据聚合为表格,能沿着行或列,根据组键来整理数据。数据透视表可以用pandas的groupby来制作,这个本节会进行介绍,除此之外还会有介绍如何利用多层级索引来进行reshape(更改形状)操作。DataFrame有一个pivot_table方法,另外还有一个pandas.pivot_table函数。为了有一个更方便的groupby接口,pivot_table能添加partial totals(部分合计),也被称作margins(边界)。回到之前提到的tipping数据集,假设我们想要计算一个含有组平均值的表格(a table of group means,这个平均值也是pivot_table默认的聚合类型),按day和smoker来分组:import numpy as np import pandas as pdtips = pd.read_csv('../examples/tips.csv') # Add tip percentage of total bill tips['tip_pct'] = tips['tip'] / tips['total_bill']tips.head()tips.pivot_table(index=['day', 'smoker'])这个结果也可以通过groupby直接得到。现在假设我们想要按time分组,然后对tip_pct和size进行聚合。我们会把smoker放在列上,而day用于行:tips.pivot_table(['tip_pct', 'size'], index=['time', 'day'], columns='smoker')我们也快成把这个表格加强一下,通过设置margins=True来添加部分合计(partial total)。这么做的话有一个效果,会给行和列各添加All标签,这个All表示的是当前组对于整个数据的统计值:tips.pivot_table(['tip_pct', 'size'], index=['time', 'day'], columns='smoker', margins=True)这里,对于All列,这一列的值是不考虑吸烟周和非吸烟者的平均值(smoker versus nonsmoker)。对于All行,这一行的值是不考虑任何组中任意两个组的平均值(any of the two levels of grouping)。想要使用不同的聚合函数,传递给aggfunc即可。例如,count或len可以给我们一个关于组大小(group size)的交叉表格:tips.pivot_table('tip_pct', index=['time', 'smoker'], columns='day', aggfunc=len, margins=True)如果一些组合是空的(或NA),我们希望直接用fill_value来填充:tips.pivot_table('tip_pct', index=['time', 'size', 'smoker'], columns='day', aggfunc='mean', fill_value=0)cross-tabulation(交叉表,简写为crosstab),是数据透视表的一个特殊形式,只计算组频率(group frequencies)。这里有个例子:data = pd.DataFrame({'Sample': np.arange(1, 11), 'Nationality': ['USA', 'Japan', 'USA', 'Japan', 'Japan', 'Japan', 'USA', 'USA', 'Japan', 'USA'], 'Handedness': ['Right-handed', 'Left-handed', 'Right-handed', 'Right-handed', 'Left-handed', 'Right-handed', 'Right-handed', 'Left-handed', 'Right-handed', 'Right-handed']}) data作为调查分析(survey analysis)的一部分,我们想要按国家和惯用手来进行汇总。我们可以使用pivot_table来做到这点,不过pandas.crosstab函数会更方便一些:pd.crosstab(data.Nationality, data.Handedness, margins=True)crosstab的前两个参数可以是数组或Series或由数组组成的列表(a list of array)。对于tips数据,可以这么写:pd.crosstab([tips.time, tips.day], tips.smoker, margins=True)
清晨我上码
14.3 US Baby Names 1880–2010(1880年至2010年美国婴儿姓名)这个数据是从1880年到2010年婴儿名字频率数据。我们先看一下这个数据长什么样子:个数据集可以用来做很多事,例如:计算指定名字的年度比例计算某个名字的相对排名计算各年度最流行的名字,以及增长或减少最快的名字分析名字趋势:元音、辅音、长度、总体多样性、拼写变化、首尾字母等分析外源性趋势:圣经中的名字、名人、人口结构变化等之后的教程会涉及到其中一些。另外可以去官网直接下载姓名数据,Popular Baby Names。下载National data之后,会得到names.zip文件,解压后,可以看到一系列类似于yob1880.txt这样名字的文件,说明这些文件是按年份记录的。这里使用Unix head命令查看一下文件的前10行:!head -n 10 ../datasets/babynames/yob1880.txt由于这是一个非常标准的以逗号隔开的格式(即CSV文件),所以可以用pandas.read_csv将其加载到DataFrame中:import pandas as pd# Make display smaller pd.options.display.max_rows = 10names1880 = pd.read_csv('../datasets/babynames/yob1880.txt', names=['names', 'sex', 'births'])names18802000 rows × 3 columns这些文件中仅含有当年出现超过5次以上的名字。为了简单化,我们可以用births列的sex分组小计,表示该年度的births总计:names1880.groupby('sex').births.sum()sex F 90993 M 110493 Name: births, dtype: int64由于该数据集按年度被分割成了多个文件,所以第一件事情就是要将所有数据都组装到一个DataFrame里面,并加上一个year字段。使用pandas.concat可以做到:# 2010是最后一个有效统计年度 years = range(1880, 2011) pieces = [] columns = ['name', 'sex', 'births'] for year in years: path = '../datasets/babynames/yob%d.txt' % year frame = pd.read_csv(path, names=columns) frame['year'] = year pieces.append(frame) # 将所有数据整合到单个DataFrame中 names = pd.concat(pieces, ignore_index=True)这里要注意几件事。第一,concat默认是按行将多个DataFrame组合到一起的;第二,必须指定ignore_index=True,因为我们不希望保留read_csv所返回的原始索引。现在我们得到了一个非常大的DataFrame,它含有全部的名字数据。现在names这个DataFrame看上去是:names1690784 rows × 4 columns有了这些数据后,我们就可以利用groupby或pivot_table在year和sex界别上对其进行聚合了:total_births = names.pivot_table('births', index='year', columns='sex', aggfunc=sum)total_births.tail()import seaborn as sns %matplotlib inlinetotal_births.plot(title='Total births by sex and year', figsize=(15, 8))下面我们来插入一个prop列,用于存放指定名字的婴儿数相对于总出生数的比列。prop值为0.02表示每100名婴儿中有2名取了当前这个名字。因此,我们先按year和sex分组,然后再将新列加到各个分组上:def add_prop(group): group['prop'] = group.births / group.births.sum() return group names = names.groupby(['year', 'sex']).apply(add_prop)names1690784 rows × 5 columns在执行这样的分组处理时,一般都应该做一些有效性检查(sanity check),比如验证所有分组的prop的综合是否为1。由于这是一个浮点型数据,所以我们应该用np.allclose来检查这个分组总计值是否够近似于(可能不会精确等于)1:names.groupby(['year', 'sex']).prop.sum()year sex 1880 F 1.0 M 1.0 1881 F 1.0 M 1.0 1882 F 1.0 ... 2008 M 1.0 2009 F 1.0 M 1.0 2010 F 1.0 M 1.0 Name: prop, Length: 262, dtype: float64这样就算完活了。为了便于实现进一步的分析,我们需要取出该数据的一个子集:每对sex/year组合的前1000个名字。这又是一个分组操作:def get_top1000(group): return group.sort_values(by='births', ascending=False)[:1000] grouped = names.groupby(['year', 'sex']) top1000 = grouped.apply(get_top1000) # Drop the group index, not needed top1000.reset_index(inplace=True, drop=True)如果喜欢DIY的话,也可以这样:pieces =[] for year, group in names.groupby(['year', 'sex']): pieces.append(group.sort_values(by='births', ascending=False)[:1000]) top1000 = pd.concat(pieces, ignore_index=True)top1000261877 rows × 5 columns接下来针对这个top1000数据集,我们就可以开始数据分析工作了1 Analyzing Naming Trends(分析命名趋势)有了完整的数据集和刚才生成的top1000数据集,我们就可以开始分析各种命名趋势了。首先将前1000个名字分为男女两个部分:boys = top1000[top1000.sex=='M'] girls = top1000[top1000.sex=='F']这是两个简单的时间序列,只需要稍作整理即可绘制出相应的图标,比如每年叫做John和Mary的婴儿数。我们先生成一张按year和name统计的总出生数透视表:total_births = top1000.pivot_table('births', index='year', columns='name', aggfunc=sum) total_births131 rows × 6868 columns接下来使用DataFrame中的plot方法:total_births.info()<class 'pandas.core.frame.DataFrame'> Int64Index: 131 entries, 1880 to 2010 Columns: 6868 entries, Aaden to Zuri dtypes: float64(6868) memory usage: 6.9 MBsubset = total_births[['John', 'Harry', 'Mary', 'Marilyn']]subset.plot(subplots=True, figsize=(12, 10), grid=False, title="Number of births per year")array([<matplotlib.axes._subplots.AxesSubplot object at 0x1132a4828>, <matplotlib.axes._subplots.AxesSubplot object at 0x116933080>, <matplotlib.axes._subplots.AxesSubplot object at 0x117d24710>, <matplotlib.axes._subplots.AxesSubplot object at 0x117d70b70>], dtype=object)评价命名多样性的增长上图反应的降低情况可能意味着父母愿意给小孩起常见的名字越来越少。这个假设可以从数据中得到验证。一个办法是计算最流行的1000个名字所占的比例,我们按year和sex进行聚合并绘图:import numpy as nptable = top1000.pivot_table('prop', index='year', columns='sex', aggfunc=sum)table.plot(title='Sum of table1000.prop by year and sex', yticks=np.linspace(0, 1.2, 13), xticks=range(1880, 2020, 10), figsize=(15, 8))从图中可以看出,名字的多样性确实出现了增长(前1000项的比例降低)。另一个办法是计算占总出生人数前50%的不同名字的数量,这个数字不太好计算。我们只考虑2010年男孩的名字:df = boys[boys.year == 2010]df1000 rows × 5 columns对prop降序排列后,我们想知道前面多少个名字的人数加起来才够50%。虽然编写一个for循环也能达到目的,但NumPy有一种更聪明的矢量方式。先计算prop的累计和cumsum,,然后再通过searchsorted方法找出0.5应该被插入在哪个位置才能保证不破坏顺序:prop_cumsum = df.sort_values(by='prop', ascending=False).prop.cumsum()prop_cumsum[:10]260877 0.011523 260878 0.020934 260879 0.029959 260880 0.038930 260881 0.047817 260882 0.056579 260883 0.065155 260884 0.073414 260885 0.081528 260886 0.089621 Name: prop, dtype: float64prop_cumsum.searchsorted(0.5)array([116])由于数组索引是从0开始的,因此我们要给这个结果加1,即最终结果为117。拿1900年的数据来做个比较,这个数字要小得多:df = boys[boys.year == 1900] in1900 = df.sort_values(by='prop', ascending=False).prop.cumsum() in1900[-10:]41853 0.979223 41852 0.979277 41851 0.979330 41850 0.979383 41849 0.979436 41848 0.979489 41847 0.979542 41846 0.979595 41845 0.979648 41876 0.979702 Name: prop, dtype: float64in1900.searchsorted(0.5) + 1array([25])现在就可以对所有year/sex组合执行这个计算了。按这两个字段进行groupby处理,然后用一个函数计算各分组的这个值:def get_quantile_count(group, q=0.5): group = group.sort_values(by='prop', ascending=False) return group.prop.cumsum().searchsorted(q) + 1 diversity = top1000.groupby(['year', 'sex']).apply(get_quantile_count) diversity = diversity.unstack('sex')现在,这个diversity有两个时间序列(每个性别各一个,按年度索引)。通过IPython,可以看到其内容,还可以绘制图标diversity.head()可以看到上面表格中的值为list,如果不加diversity=diversity.astype(float)的话,会报错显示,“no numeric data to plot” error。通过加上这句来更改数据类型,就能正常绘图了:diversity = diversity.astype('float') diversity131 rows × 2 columnsdiversity.plot(title='Number of popular names in top 50%', figsize=(15, 8))从图中可以看出,女孩名字的多样性总是比男孩高,而且还变得越来越高。我们可以自己分析一下具体是什么在驱动这个多样性(比如拼写形式的变化)。“最后一个字母”的变革一位研究人员指出:近百年来,男孩名字在最后一个字母上的分布发生了显著的变化。为了了解具体的情况,我们首先将全部出生数据在年度、性别以及末字母上进行了聚合:# 从name列中取出最后一个字母 get_last_letter = lambda x: x[-1] last_letters = names.name.map(get_last_letter) last_letters.name = 'last_letter' table = names.pivot_table('births', index=last_letters, columns=['sex', 'year'], aggfunc=sum)print(type(last_letters)) print(last_letters[:5])<class 'pandas.core.series.Series'> 0 y 1 a 2 a 3 h 4 e Name: last_letter, dtype: object然后,我们选出具有一个代表性的三年,并输出前几行:subtable = table.reindex(columns=[1910, 1960, 2010], level='year') subtable.head()接下来我们需要安总出生数对该表进行规范化处理,一遍计算出个性别各末字母站总出生人数的比例:subtable.sum()sex year F 1910 396416.0 1960 2022062.0 2010 1759010.0 M 1910 194198.0 1960 2132588.0 2010 1898382.0 dtype: float64letter_prop = subtable / subtable.sum() letter_prop26 rows × 6 columns有了这个字母比例数据后,就可以生成一张各年度各性别的条形图了:import matplotlib.pyplot as pltfig, axes = plt.subplots(2, 1, figsize=(10, 8)) letter_prop['M'].plot(kind='bar', rot=0, ax=axes[0], title='Male') letter_prop['F'].plot(kind='bar', rot=0, ax=axes[1], title='Femal', legend=False)从上图可以看出来,从20世纪60年代开始,以字母'n'结尾的男孩名字出现了显著的增长。回到之前创建的那个完整表,按年度和性别对其进行规范化处理,并在男孩名字中选取几个字母,最后进行转置以便将各个列做成一个时间序列:letter_prop = table / table.sum() letter_prop.head()5 rows × 262 columnsdny_ts = letter_prop.loc[['d', 'n', 'y'], 'M'].T dny_ts.head()有了这个时间序列的DataFrame后,就可以通过其plot方法绘制出一张趋势图:dny_ts.plot(figsize=(10, 8))变成女孩名字的男孩名字(以及相反的情况)另一个有趣的趋势是,早年流行于男孩的名字近年来“变性了”,列入Lesley或Leslie。回到top1000数据集,找出其中以"lesl"开头的一组名字:all_names = pd.Series(top1000.name.unique()) lesley_like = all_names[all_names.str.lower().str.contains('lesl')] lesley_like632 Leslie 2294 Lesley 4262 Leslee 4728 Lesli 6103 Lesly dtype: object然后利用这个结果过滤其他的名字,并按名字分组计算出生数以查看相对频率:filtered = top1000[top1000.name.isin(lesley_like)] filtered.groupby('name').births.sum()name Leslee 1082 Lesley 35022 Lesli 929 Leslie 370429 Lesly 10067 Name: births, dtype: int64接下来,我们按性别和年度进行聚合,并按年度进行规范化处理:table = filtered.pivot_table('births', index='year', columns='sex', aggfunc='sum') table = table.div(table.sum(1), axis=0)table131 rows × 2 columns现在,我们可以轻松绘制一张分性别的年度曲线图了:table.plot(style={'M': 'k-', 'F': 'k--'}, figsize=(10, 8))<matplotlib.axes._subplots.AxesSubplot at 0x11f0640b8>
清晨我上码
导入数据使用pandas官方教程提供的示例数据,导入地址:http://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.pivot_table.htmlimport pandas as pd df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo", "bar", "bar", "bar", "bar"], "B": ["one", "one", "one", "two", "two", "one", "one", "two", "two"], "C": ["small", "large", "large", "small", "small", "large", "small", "small", "large"], "D": [1, 2, 2, 3, 3, 4, 5, 6, 7], "E": [2, 4, 5, 5, 6, 6, 8, 9, 9]}) df参数说明pandas.pivot_table(data, values=None, index=None, columns=None, aggfunc=‘mean’, fill_value=None, margins=False, dropna=True, margins_name=‘All’, observed=False, sort=True)主要参数:data:待操作的 DataFramevalues:被聚合操作的列,可选项index:行分组键,作为结果 DataFrame 的行索引columns:列分组键,作为结果 DataFrame 的列索引aggfunc:聚合函数/函数列表,默认 numpy.mean ,这里要注意如果 aggfunc 中存在函数列表,则返回的 DataFrame 中会显示函数名称fill_value:默认 None,可设定缺省值dropna:默认 True,如果列的所有值都是 NaN,将被删除;False 则保留margins:默认 False,设置为 True 可以添加行/列的总计margins_name:默认显示 ‘ALL’,当 margins = True 时,可以设定 margins 行/列的名称常用操作指定index进行聚合使用pivot_table时必须要指定index,因为计算时要根据index进行聚合。import numpy as np pd.pivot_table(df, index='A', aggfunc=[np.sum]) sum D E A bar 22 32 foo 11 22通过指定value来选择被聚合的列import numpy as np pd.pivot_table(df, values='D', index='A', aggfunc=[np.count_nonzero]) count_nonzero D A bar 4 foo 5当只指定index进行聚合时,其实用groupby可以实现同样的效果。df.groupby(['A'])['D'].count().reset_index() A D 0 bar 4 1 foo 5添加columns参数,对列分组pd.pivot_table(df.head(10), values='D', index='A', columns='B', aggfunc=np.count_nonzero)C large small A bar 2 2 foo 2 3对于上面结果中的空值,使用fill_value参数统一填充为0pd.pivot_table(df.head(10), values='D', index='A', columns='B', fill_value=0, aggfunc=np.count_nonzero)注意此时的aggfunc参数,当参数值包含列表时,在结果DataFrame中就会显示函数名称。添加合计列如果需要添加合计列,只需指定margins=True即可,同时根据需要指定合计名称。pd.pivot_table(df.head(10), values='D', index='A', columns='B', fill_value=0, margins=True, aggfunc=np.count_nonzero)B one two All A bar 2 2 4 foo 3 2 5 All 5 4 9pd.pivot_table(df, values='D', index=['A','B'], columns=['C'], fill_value=0, margins=True, aggfunc=np.count_nonzero)C large small All A B bar one 1 1 2 two 1 1 2 foo one 2 1 3 two 0 2 2 All 4 5 9指定合计名称pd.pivot_table(df.head(10), values='D', index=['A','B'], columns=['C'], fill_value=0, margins=True, margins_name='合计', aggfunc=[np.count_nonzero]) count_nonzero C large small 合计 A B bar one 1 1 2 two 1 1 2 foo one 2 1 3 two 0 2 2 合计 4 5 9当然与groupby类似,对于计算函数我们可以同时指定多种方式。pd.pivot_table(df, values='D', index=['A'], columns=['C'], fill_value=0, margins=True, aggfunc=[max,sum,np.count_nonzero]) max sum count_nonzero C large small All large small All large small All A bar 7 6 7 11 11 22 2 2 4 foo 2 3 3 4 7 11 2 3 5 All 7 6 7 15 18 33 4 5 9
攻城狮无远
第二章 云上的大数据架构大数据可能意味着更多的信息,但也意味着更多的虚假信息。 ---纳西姆·塔勒布正如我们在第1章中学到的那样,关于云数据湖有两个关键要点,为本章奠定了基础:数据湖方法从存储和处理任何类型的数据开始,无论数据的来源、大小或结构如何,从而使组织能够从许多不同的数据源中提取高价值洞察力,这些数据具有可变的价值密度(即信噪比)。在云上构建您的数据湖涉及到一种解聚架构,您将不同的IaaS、PaaS和SaaS组件组合在一起。重要的是要记住,在构建云数据湖解决方案时,您还有很多架构选项,每种架构都有其独特的优势。这篇关于Future.com的文章提供了现代数据架构的综合概述。在本章中,我们将深入探讨一些常见的架构模式,了解它们是什么以及它们各自的优势如何适用于一个名为Klodars Corporation的虚构组织。为什么Klodars Corporation选择迁移到云端Klodars Corporation是一家在太平洋西北地区销售雨具和其他用品的蓬勃发展的公司。其业务的快速增长推动了其迁移到云端的原因如下:在本地系统上运行的数据库无法再与业务的快速增长相适应。随着业务的增长,团队也在扩大。销售和市场团队发现他们的应用程序变得越来越慢,有时甚至会超时,这是因为同时使用系统的用户数量增加。市场部门希望在社交媒体上更好地定位其营销活动,并正在探索利用影响力人物的想法,但不知道从何处开始或如何进行。销售部门无法迅速扩大与分布在三个州的客户的合作,因此很难确定首先要与哪种零售客户和批发分销商合作。投资者对业务增长表示赞赏,并询问首席执行官如何扩展Klodars Corporation的业务范围。首席执行官需要制定扩展战略。软件开发团队的积极领导者Alice向Klodars Corporation的首席执行官和首席技术官提出了一个想法,即探索云计算,并了解其他企业如何利用数据湖方法解决他们所面临的挑战。她还收集了一些数据,展示了云数据湖方法可以带来的机遇,包括以下内容:云计算可以根据公司不断增长的需求进行弹性扩展,由于按消耗付费,因此不需要过度配置硬件以预算应对高峰季节,并且硬件在其他时间不需要闲置。基于云的数据湖和数据仓库可以进行扩展,以支持不断增长的并发用户数量。云数据湖提供了处理来自各种来源的数据的工具和服务,例如网站点击流、零售分析、社交媒体信息,甚至天气数据,因此公司可以更好地了解其营销活动。Klodars Corporation可以雇佣数据分析师和数据科学家来处理市场趋势,以帮助提供有价值的信号,从而协助其扩展战略。首席执行官对这种方法完全认同,并希望尝试云数据湖解决方案。在公司的发展过程中,保持现有业务的运行非常重要,同时开始尝试云计算方法。让我们看看不同的云架构如何为Klodars Corporation带来独特的优势,并帮助满足其快速增长和扩展带来的需求。云数据湖架构的基本原理在部署云数据湖架构之前,重要的是要了解构成云数据湖架构基础并作为构建模块的四个关键组件。这些组件包括:数据本身数据湖存储处理数据的大数据分析引擎云数据仓库对于数据的多样性需要提一下我们已经提到数据湖支持各种类型的数据,但是这种多样性实际上指的是什么呢?让我们以之前提到的数据为例,具体是库存和销售数据集。从逻辑上讲,这些数据是表格化的,也就是由行和列组成,可以在表格中表示。然而,实际上,这些表格数据的表示方式取决于生成数据的源头。大体上来说,在大数据处理中,有三个广泛的数据类别:结构化数据这是一组具有定义结构(行和列)并且遵循严格预定义模式的格式。一个经典的例子是关系数据库(如SQL)中的数据,它看起来像图2-1所示。数据存储在专门定制的二进制格式中,用于关系数据库,并经过优化以存储表格数据(以行和列组织的数据)。这些格式是专有的,为特定系统量身定制。数据的消费者,无论是用户还是应用程序,都了解这个结构和模式,并依赖它们编写他们的应用程序。不符合规则的数据将被丢弃,不会存储在数据库中。关系数据库引擎还将这些数据存储在经过优化的二进制格式中,以便高效存储和处理。半结构化数据这是一组格式,其中存在一定的结构,但它的定义比较宽松,如果需要的话,可以灵活地自定义结构。JSON和XML就是半结构化数据的示例。图2-2展示了销售商品ID的半结构化数据的表示形式。半结构化数据格式的强大之处在于其灵活性。一旦你开始设计一个模式,然后确定需要一些额外的数据,你可以添加带有额外字段的数据,而不会违反任何结构的限制。读取数据的现有引擎将继续正常工作,而新的引擎可以包含新字段。同样,当不同的源发送类似的数据(例如,销售点系统和网站遥测都可以发送销售信息),你可以利用灵活的模式支持这些多个来源。非结构化数据这指的是没有对数据存储方式施加任何限制的格式,可以是类似社交媒体帖子上的评论这样简单的自由形式备注,也可以是复杂的MPEG4视频或PDF文档。非结构化数据可能是最难处理的格式,因为它需要定制编写的解析器才能理解并从数据中提取正确的信息。与此同时,从一般用途的对象存储角度来看,非结构化数据可能是最容易存储的格式,因为它没有任何限制。例如,想象一下社交媒体帖子中的图片,卖家可以为商品添加标签,并在有人购买该商品后添加另一个标签表示已售出。处理引擎需要处理图像以了解已售出的商品,然后处理标签以了解价格和购买者是谁。虽然这并非不可能,但需要花费大量的工作来理解数据,并且质量较低,因为它依赖于人工标记。然而,这扩大了灵活性的视野,可以用于开展各种销售渠道。例如,你可以编写一个引擎来处理社交媒体中的图片,以了解在给定地区以何种价格由哪个房地产经纪人出售的房屋,如图2-3所示。云数据湖存储云数据湖存储的非常简单的定义是一种作为云服务提供的服务,可作为各种数据(结构化、非结构化和半结构化)的中央存储库,并能支持大规模的数据和事务。当我说“大规模”时,可以想象一个支持存储数百PB的数据和每秒数十万个事务的存储系统,并且可以在数据和事务继续增长时实现弹性扩展。在大多数公共云服务提供中,数据湖存储作为PaaS服务提供,也称为对象存储服务。数据湖存储服务提供丰富的数据管理功能,例如分层存储(不同层级的成本不同,可以将很少使用的数据移动到成本较低的层级)、具有不同程度复制的高可用性和灾难恢复以及丰富的安全模型,允许管理员控制各种消费者的访问权限。让我们来看一些最受欢迎的云数据湖存储服务提供:Amazon S3(简单存储服务)亚马逊提供的S3是一个大规模的对象存储服务,建议将其作为构建基于AWS的数据湖架构的存储解决方案。在S3中存储的实体(结构化和非结构化数据集)被称为对象,对象被组织到称为存储桶的容器中。S3还允许用户通过使用共同前缀(将其视为虚拟目录)将对象组合在一起进行组织。管理员可以通过在存储桶或前缀级别应用访问策略来控制对S3的访问权限。此外,数据操作员可以向对象添加标签,这些标签实际上是键值对,可以用作标签或标签,让您可以通过指定标签检索对象。亚马逊S3还提供了丰富的数据管理功能,以管理数据的成本,并增加安全性保证。要了解有关S3的更多信息,您可以访问文档页面。Azure Data Lake Storage(ADLS)ADLS是Microsoft Azure提供的一种存储解决方案,它在其通用对象存储服务(Azure Blob存储)上提供了一个带有层次化命名空间的本地文件系统。根据ADLS产品网站的介绍,ADLS是一个用于摄取、处理和可视化的单一存储平台,支持最常见的分析框架。您可以创建一个ADLS账户,其中将在“启用层次化命名空间”选项中选择是。ADLS提供了一个称为容器的组织单元,以及具有目录和文件来组织数据的本地文件系统。您可以访问文档页面以了解有关ADLS的更多信息。Google Cloud Storage(GCS)GCS是由GCP提供的对象存储服务,并被推荐作为数据湖存储解决方案。类似于S3,Google中的数据被称为对象,并以存储桶进行组织。您可以在文档页面上了解更多关于GCS的信息。云数据存储服务具有从各种来源加载数据的能力,包括本地存储解决方案,并与实时数据摄取服务集成,该服务连接到诸如物联网传感器之类的数据源。它们还与支持传统应用程序的本地系统和服务集成。此外,有许多数据处理引擎可以处理存储在数据湖存储服务中的数据。这些数据处理引擎属于多个类别:公共云提供的PaaS解决方案(例如,AWS的EMR,Azure的HDInsight和Azure Synapse Analytics,以及GCP的Dataproc)其他软件公司开发的PaaS解决方案,例如Databricks、Dremio、Talend、Informatica和ClouderaSaaS解决方案,例如Microsoft Power BI、Tableau和Looker您还可以预配IaaS解决方案,如虚拟机(VMs),并运行自己的软件发行版,如Apache Spark,来查询数据湖。 需要注意的一个重要点是,在数据湖架构中,计算和存储是分离的,您可以在数据湖中运行一个或多个处理引擎,而无需移动数据。一些流行的数据仓库包括Amazon Redshift、Google BigQuery和Snowflake Data Cloud。这些数据仓库既提供计算能力又提供存储空间,虽然某些情况下数据仓库支持查询存储在独立数据湖存储中的数据,但最常见的用例是使用最优化的路径:查询以专有数据格式存储在数据仓库中的数据。最近,数据仓库开始支持开放数据格式,如Apache Iceberg,这是一个非常有前景的趋势,它在方向上支持数据湖仓库架构。在本章中,我们还将更详细地介绍数据湖仓库架构。大数据分析引擎到目前为止,我们了解到大数据分析是对结构化、半结构化和非结构化数据进行处理。现在让我们来探索一下这个处理过程的实际情况。当我们谈论在数据湖上进行大数据分析时,所进行的处理很可能是下面描述的其中一种,或者是它们的派生形式之一。MapReduce大数据和分析处理的起源可以追溯到一个改变我们工作方式的事物的出现:搜索引擎。搜索引擎主要通过从互联网上的所有来源抓取文本数据并构建一个巨大的关键字索引来工作。当用户搜索一个关键字时,搜索引擎会根据这个索引对数据进行排名,并向用户提供一个有序的结果集。虽然搜索引擎的设计本身需要一本书来详细解释,我们在这里不会详细讨论,但它们证明了需要处理大量数据并将其简化为可搜索的索引的需求,从而诞生了一个称为MapReduce的编程模型。MapReduce本质上是一个编程模型及其相关实现,它接受一组键值对作为输入,并生成一组键值对作为输出。听起来很简单,不是吗?问题在于规模——在包含数百万条记录的数据集上进行这种转换。正如Jeffrey Dean和Sanjay Ghemawat在他们的论文《MapReduce: Simplified Data Processing on Large Clusters》中所描述的,MapReduce有两个阶段,顾名思义。在映射阶段,数据按照键进行组织,并使用逻辑将相似的值归为一组,生成中间的键值对。规约阶段处理这些相似的数据集,生成一组经过筛选的结果,同样是键值对。举个例子,让我们以Twitter数据为例,目标是了解在一组大型Feed中每个用户被提及的次数(图2-4中显示了一个较小的样本)。这里有一组计算单元在工作:多个工作单元在分配给它们的数据集上运行,并且有一个主要的编排单元负责协调工作单元之间的操作。计算单元可以是虚拟机、进程或线程,具体取决于实现方式。将大量的Twitter数据Feed分解成较小的集合(在这个例子中是一个Feed)并分配给工作单元,在这里它们将提及映射为计数,并生成一组输出的键值对,如图2-4所示。然后将这些数据值发送给另一组工作单元进行规约,以生成每个用户的提及次数。主要优势在于,这种编程模型可以将大型数据集有效地分布在一组工作单元中,并具有可预测的分布机制。Apache HadoopApache是一个开源组织,他们有一个名为Apache Nutch的开源网络搜索项目,即使在今天仍在使用。2005年,作为Apache Nutch的一部分,Doug Cutting和Mike Cafarella创建了Apache Hadoop,这是一套用于分布式处理大型数据集的工具、库和组件,其核心处理逻辑使用了MapReduce。Apache Hadoop由四个主要组件组成:Hadoop通用模块:支持其他模块的通用库集合Hadoop分布式文件系统(HDFS) :用于大型数据集的分布式存储系统Hadoop MapReduce :用于大规模处理大型数据集的编程模型和实现Hadoop YARN :用于作业调度和资源管理的框架,可以在机群中分发工作和数据Hadoop奠定了一个坚实的基础,孕育了许多其他开源项目,例如Apache Hive、Apache Pig、Apache Storm和Apache Mahout,用于构建更多分布式大数据处理的框架和编程模型。这里提供了所有Hadoop项目和工具的详细索引,示例见图2-5中的Hadoop生态系统表格。Hadoop使大数据处理生态系统商品化,而像Hortonworks和Cloudera这样的供应商销售他们的Hadoop分发版本,客户可以在本地或云上安装。公共云提供商还提供基于Hadoop的处理的打包版本作为PaaS解决方案,例如AWS的Elastic MapReduce(EMR)和Microsoft Azure的HDInsight。在所有这些不同的Hadoop提供中,你可能会想知道该选择哪个。虽然有很多原因,比如对供应商的熟悉程度、销售和市场关系等,但几个技术关键因素对客户的选择起到了重要作用:在混合环境下运行的客户,即既有本地部署又有云部署,或者在多云环境下运行的客户,会选择由独立软件供应商(ISV)提供的Hadoop解决方案,如Cloudera或Hortonworks,以便其实现在所有环境中都能工作。更喜欢将其大数据平台与其他原生云服务紧密集成的客户,选择公共云提供商(如AWS、Azure和GCP)提供的解决方案。愿意投资于强大技术团队并希望节省供应商成本的客户,会通过分叉开源存储库并构建自己的平台来创建自己的Hadoop版本。这在其他开源解决方案(如Apache Spark)中也同样适用。 可以说,Hadoop通过提供一套综合的工具为大数据处理奠定了数据湖架构的基础,包括用于批处理的MapReduce、用于实时处理的Apache Storm以及用于在Hadoop架构上查询数据的Apache Hive。Apache SparkApache Spark孵化于加州大学伯克利分校的AMPLab,专注于大数据分析。Apache Spark的目标是提供一种灵活的编程模型,具有MapReduce的容错性和规模,用于分布式数据处理,同时支持更广泛的应用程序,如依赖于数据的迭代处理和实时处理的机器学习,以提供即时见解。与Hadoop类似,Spark使用底层存储层;然而,并没有规定必须使用HDFS存储;Spark支持云对象存储服务甚至本地存储。同样,Spark使用集群管理器,也支持各种选项,如从Hadoop诞生的YARN和同样孵化于加州大学伯克利分校的Apache Mesos。最近,随着Kubernetes和容器(简单定义为包含代码、应用程序运行时和运行代码所需的其他组件的即用软件包)在云原生开发中的日益流行,Spark在Kubernetes上也得到了广泛应用。Spark的关键区别在于Spark核心引擎,它构建在作为弹性分布式数据集(RDDs)的数据集的基本抽象上,而无需将中间数据集存储到持久性存储中,仍然保持容错性。这种模型极大地提高了基于Spark的应用程序的性能,并为批处理、交互式查询(Spark SQL)、数据科学(MLlib)、实时处理(Spark Streaming)和最近引入的图处理(GraphX)提供了统一的编程模型。Apache Spark的易用性和日益增长的认可度帮助在各个行业中将大数据处理商品化。你可以使用Spark作为独立分发版,也可以利用公共云提供商提供的Spark(如Amazon EMR、Azure Synapse Analytics或Google Cloud Dataproc),或者使用由Spark的发明者创建的Databricks等软件提供商提供的Spark。图2-6演示了Spark的各种技术组件以及它们如何相互层叠,以提供在机器学习、实时和批处理流式处理中的一致的编程模型。实时流处理管道实时流处理指的是对数据进行摄取、处理和消费,重点是追求速度,接近实时的结果。想象一下,当你在旅行时,你从你喜爱的美食评论应用程序收到了关于附近餐厅的实时通知,你可以去探索。这里有一个实时处理管道在工作,它从你的移动设备中获取你的位置信息,并将其与你的个人资料和其他相关数据结合起来,实时提供个性化的推荐。另一个例子是你手机上的导航应用在你通常的路线上有交通拥堵时建议你选择另一条路。在这种情况下,有一个实时处理管道将实时交通数据与地图结合起来,为你的目的地提供最佳路线建议。实时流处理管道涉及到从源头以非常高的速率到达的数据,换句话说,这些数据就像雨水或瀑布一样源源不断地流入系统中。这些数据可能是不断从诸如GPS之类的源头实时流入的,或者可能是由物联网传感器(如家庭自动化系统或工业设备)发出的事件。这些数据往往非常小,通常为几千字节(KB)。 实时流处理管道的处理部分涉及处理实时流数据,有时将其与非实时数据结合,重点是低延迟,通常在毫秒级。实时处理应用程序的典型场景是提供接近实时的洞察力,以便消费者能够迅速采取行动,例如当系统处理系统日志并在出现问题时实时发出警报的系统。实时数据处理技术在处理高速率和吞吐量的数据进入系统时考虑以下几个方面:传递保证实时流处理技术提供传递保证,确定实时数据的处理方式。至少一次保证确保数据将至少被处理一次,可能会多次处理以应对故障。至多一次保证确保数据将最多被处理一次,避免重复处理。精确一次保证确保数据将被精确处理一次,这是非常理想的,但也非常难以实现。容错性实时流处理技术需要确保在集群或底层基础设施发生故障时具有弹性,并能够从故障出现的地方继续处理。状态处理实时流处理框架提供状态管理功能,记录已处理的消息数量或最后处理的消息是什么。实时流数据可以以多种方式进行消费:如展示社交媒体趋势图的可视化、安全事件检测等警报系统,甚至基于浏览模式的实时推荐等智能应用行为。图2-7展示了实时流数据管道的架构。构建实时数据管道的多种技术也可供选择。Apache Kafka是一个重要的技术,用于实时流数据的摄取和存储,具有高吞吐量和可扩展性。Amazon Kinesis和Azure Event Hub是基于Apache Kafka构建的云原生PaaS解决方案。Apache Storm和Apache Flink是流行的开源技术,提供实时数据处理能力。Apache Kafka还提供Kafka Streams用于实时流处理。云数仓云数据仓库是在公共云上以托管服务(PaaS)形式提供的企业数据仓库,具有针对数据摄取、分析处理和商业智能分析的优化集成。商业智能分析指支持可视化和交互式查询功能的工具。云数据仓库的提供旨在通过将基础架构从用户中抽象出来,弹性扩展以满足客户不断增长的需求,并承诺比传统的本地数据仓库具有更快的性能和更低的拥有成本。让我们来看一些最受欢迎的云数据仓库提供商:Amazon RedshiftAmazon Redshift是公共云上首个受欢迎的云数据仓库提供商。您可以配置一个Redshift集群,并指定所需的计算节点数量。根据产品文档,该集群可以支持PB级的数据。您可以使用流行的查询语言PostgreSQL来查询Redshift集群中的数据。要了解更多关于Redshift的信息,您可以访问产品页面。Redshift还宣布了在不复制数据的情况下在不同的Redshift集群之间共享数据的能力,以促进数据和洞察力的共享。Google BigQuery与Redshift不同,您在Google BigQuery中无需配置数据仓库集群,它是一个完全无服务器、高度可扩展的数据仓库解决方案,完全将集群管理的细节与客户隔离开来。此外,BigQuery还具有BigQuery Omni等功能,允许您在其他云(如AWS和Azure)上使用BigQuery计算服务。Azure SynapseAnalytics Azure Synapse Analytics是在Microsoft Azure上提供的统一分析平台。与Redshift类似,您可以配置一个数据仓库集群,并为您的场景指定所需的节点数量。您还可以在同一体验中配置Spark集群以进行分析场景。此外,您还可以在SQL或Spark中运行无服务器查询。使用无服务器查询,您可以简单地提交作业而无需配置集群,类似于BigQuery。Azure Synapse Analytics还在同一体验中与其他Azure服务集成,如Azure Machine Learning、Azure Cognitive Services和Power BI。Snowflake Data CloudSnowflake数据仓库是一种托管的数据仓库解决方案,可在所有公共云(AWS、Amazon和GCP)上使用。Snowflake被设计为一个真正可扩展的解决方案,它作为一个单一服务提供,而实现则在分离的计算和存储架构上运行,使其在计算或存储维度上高度可扩展,而不会增加成本。这种分离还可以让您启动不同的虚拟仓库,这些仓库可以访问相同的数据,为不同的查询场景提供隔离。Snowflake还提供表级和对象级的数据共享给其他Snowflake账户。在本节中,我对云数据湖架构的四个组成部分进行了高级概述:数据、数据湖存储、计算引擎和云数据仓库。我还概述了常用的服务和技术,并提供了深入了解的链接。在下一节中,我将介绍代表这些构建块可以组装成解决方案的不同云数据湖架构。在我们阅读本书的同时,数据湖和数据仓库的提供商正在快速创新,模糊了它们之间的界限。我们将在数据湖仓库架构模式中进一步讨论这一点。云数据湖架构中的数据可以用于多种目的。然而,有两种常见的组织中的消费模式:商业智能数据被商业智能分析师用于创建仪表板或处理交互式查询,以回答明确定义的关键业务问题,并处理高度结构化的数据。数据科学和机器学习数据被数据科学家和机器学习工程师用于进行探索性分析和实验工作,以回答没有明确定义的规则集且需要多次迭代才能改进的复杂问题。在这里涉及的数据假设没有结构。现代数仓架构在现代数据仓库架构中,数据湖和数据仓库和平共处,各自担任不同的角色。数据湖作为低成本存储用于大量数据,并支持数据科学和机器学习等探索性场景。数据仓库存储高价值的数据,并为企业的仪表板提供支持。它也被商业智能用户用于查询高度结构化的数据,以获取有关业务的洞察力。参考架构首先,数据从各种来源(如本地数据库、社交媒体数据源等)被摄取到数据湖中。然后,利用像Hadoop和Spark这样的大数据分析框架对数据进行转换,通过聚合和筛选多个数据集来生成具有高价值的结构化数据。接下来,将这些数据加载到云数据仓库中,用于生成各种仪表盘,包括供商业智能分析师使用的交互式仪表盘,他们可以使用他们非常熟悉的SQL工具进行查询。此外,数据湖还为数据科学家提供了一整套探索性分析以及将机器学习模型反馈到应用程序中的场景。图2-8展示了现代数据仓库架构的简化表示。在这里,你可能会自然而然地提出一些问题:为什么不直接使用云数据仓库?为什么需要在两者之间加入数据湖?如果我只有结构化数据,是否真的需要数据湖?我可以说,这些是很好的问题。以下是为什么在这种架构中需要数据湖的几个原因:数据湖的成本远低于数据仓库,并可作为长期数据存储库。请记住,数据湖通常用于存储大量的数据(数十或数百PB),因此成本差异是实质性的。数据湖支持各种现代化的数据科学和机器学习工具和框架,可以用于实现全新的场景。数据湖可以为未来的扩展需求提供可扩展性设计。例如,您可以使用初始的数据湖架构每晚从本地系统加载数据,并为商业智能用户发布报表或仪表盘。同样的架构可以扩展支持实时数据摄取,而无需重新设计解决方案。各种形式和结构的数据对组织来说越来越重要。即使您今天专注于结构化数据,如前面的示例所示,您可能会发现各种类型的数据(例如天气、社交媒体数据等)都具有价值。如果你还没有注意到,这里有一个需要记住的使用模式的区别:当你将数据加载到数据仓库时,你使用的是提取、转换和加载(ETL)模式,即从源中提取数据,将其转换为数据仓库所需的格式,然后加载到数据仓库中。而在数据湖中,你遵循提取、加载和转换(ELT)模式,即从源中提取数据,按原样加载到数据湖中,然后进行转换处理。现代数据仓库架构的示例应用案例让我们重新考虑我们的模型公司Klodars Corporation。它将利用现代数据仓库架构,开始将数据从其运营数据库加载到数据湖中。它可以停止在本地系统上存储备份,并将每日备份存储在数据湖中,保留一年的备份(或更长时间,如果需要)。在此过程中,Klodars的服务器上的运营数据库将继续为现有应用程序提供服务,从而确保公司运营的连续性。此外,Klodars还计划加载与雨具和冬季装备相关的社交媒体数据,以分析模式。该架构还将使公司能够使用实时摄取技术(如Apache Kafka)将其他数据(如点击流)实时加载到数据湖存储中。准备好数据集后,数据工程团队将使用Apache Spark等工具处理来自数据库转储和网站点击流的结构化数据,生成显示购物和销售趋势的高价值数据。团队还将处理社交媒体数据流,提取与雨具和冬季装备相关的数据,以及这些数据所指示的任何相关购买行为。该架构将使数据工程团队能够定期生成关于销售趋势、库存和供应、网站浏览趋势以及与雨具和冬季装备相关的社交媒体趋势的高价值数据(例如,每日生成)。然后,将这些数据加载到数据仓库中,并定期刷新(例如,每日)。存储在数据仓库中的数据是非常高价值的结构化数据。业务分析师将使用这些高价值数据构建仪表盘,显示按季度或按月的销售趋势,以便销售团队可以了解其销售的趋势并为即将到来的时间段制定预测。业务分析师还可以根据地区、销售人员覆盖范围、合作伙伴和其他属性对数据进行分析,以便领导团队了解增长驱动因素,并根据数据制定关于公司扩张策略的决策。营销团队通过在数据仓库上运行交互式查询来使用社交媒体和网站浏览趋势,以了解下一轮有针对性的营销活动的发展方向。团队还可以通过将营销活动与销售结果相关联来了解其营销活动的影响。影响并不止于此。Klodars现在已经组建了一个数据科学团队,他们可以基于现有数据集(如销售、社交媒体趋势和网站浏览趋势)寻找有趣的关联和影响,这些关联和影响无法通过手动分析进行处理。团队可以将其他数据集引入数据湖中,例如天气数据、关于滑雪等冬季活动的数据等,以向领导团队展示有趣的见解。这些数据可以反馈给数据工程团队,加载到数据仓库中,供领导、营销和销售团队使用。图2-9展示了Klodars Corporation的现代数据仓库架构的表示。借助现代数据仓库架构,Klodars Corporation通过依靠数据,能够根据客户的增长需求进行适当的重点领域扩展。其现代数据仓库战略使公司能够在保持现有业务运作的同时进行创新工作。将现有应用逐步迁移到现代化的云架构中,使团队有时间来深思熟虑地设计和实施这一转变。现代数仓架构的好处现代数据仓库具有重要优势,可以帮助业务分析师利用熟悉的商业智能工具集(基于SQL)进行数据消费,并实现原本在本地数据仓库实现中无法实现的更现代化的数据科学和机器学习场景。这主要通过数据湖来实现,数据湖作为一个非孤立的数据存储库,支持使用云原生服务进行高级数据科学和机器学习场景,同时保留了熟悉的数据仓库,如面向商业智能用户的基于SQL的接口。此外,数据管理员可以通过数据仓库将对数据的访问隔离给商业智能团队,使用熟悉的数据仓库访问控制方法。运行在本地的应用程序也可以逐步迁移到云端,完全消除维护两套基础设施的需求。此外,通过将运营数据备份到数据湖中,企业可以降低总体成本,并延长数据备份的时间。然而,这种方法也存在一些挑战。数据工程师和管理员仍然需要维护两套基础设施:一个数据湖和一个数据仓库。在数据湖中存储各种类型的数据的灵活性也带来了挑战。管理数据湖并确保数据质量的保证是数据工程师和管理员现在必须解决的重大问题,这是他们之前没有遇到的问题。如果数据没有得到妥善管理,数据湖也有可能变成一个数据沼泽,就像在一堆干草中找针一样隐藏了洞察力。如果商业智能用户或业务决策者需要新的数据集,他们必须依赖数据工程师来处理这些数据并将其加载到数据仓库中,这引入了一个关键路径。此外,如果数据科学家在数据仓库中发现了一个有趣的数据片段,他们想要将其包括在探索性分析中,他们需要将其重新加载到数据湖中,以不同的数据格式和不同的数据存储方式,增加了共享的复杂性。数据湖仓架构数据湖仓(Data Lakehouse),是由Databricks广泛使用的一个行业热词。根据451 Research的研究分析师Malav Parekh的博客文章,Amazon在发布Redshift Spectrum时首次使用了“lake house”(湖宅)这个术语,将“lake”和“house”之间加了一个空格。该术语在行业中逐渐流行起来是在2020年1月Databricks的一篇博客文章中,称数据湖仓是一种新的开放式架构,结合了数据湖和数据仓库的最佳元素。我清楚地记得2020年的Data and AI Summit的主题演讲,Ali Ghodsi宣布了数据湖仓作为一种新的范式,并介绍了Delta Lake。有多个关于Delta Lake的会议,参会者排起长队沿着会议大厅的走廊。数据湖仓架构的不断增长的受欢迎程度和生态系统支持了这种新范式的主张。数据湖仓架构可以简单解释为一个单一平台,结合了两个功能:数据湖用于分析处理、数据科学和机器学习场景。数据仓库用于SQL交互查询和商业智能场景。换句话说,它指的是在数据湖上运行SQL和商业智能场景。这个概念具有以下三个吸引人的特点:数据湖比数据仓库便宜得多,使得数据湖仓更具成本效益。无需将数据从数据湖复制或移动到数据仓库,无需进行数据移动。通过消除分离的体验和平台,数据科学家和商业智能团队可以自由共享数据集。数据湖仓架构的出现在行业中引起了广泛关注,并为组织提供了更加灵活和高效的数据分析和查询方案。数据湖仓的参考架构图2-10展示了数据湖仓架构的简化表示。请注意,现在您可以在单一平台上运行所有场景,包括商业智能和数据科学,并且无需使用云数据仓库。那么,如果我们已经有了在数据湖上运行商业智能场景的选项,为什么一开始就没有这样做呢?简单的答案是因为数据湖本身并不真正适合支持商业智能查询,而且有各种技术使得数据湖仓成为现实。请记住,数据仓库依赖于高度结构化的数据以实现更快的查询处理和支持涉及连接和聚合的复杂查询,而数据湖则是高度可扩展的对象存储服务,用于存储和处理数据,对结构不做任何假设。让我们更详细地看一下,数据仓库具有以下优势:ACID兼容的事务数据仓库确保事务符合ACID的标准,这一特性对于保证通常存储在仓库中的高价值数据的完整性至关重要。这种完整性非常重要,因为这些数据用于支持涉及公司收入和运营的关键操作的查询和仪表板。例如,销售预测仪表板为组织设定收入目标。ACID是指事务的四个关键属性:原子性确保当事务完成时,整个事务作为一个单元成功。例如,如果您在查询中请求客户的详细信息,并要求包括姓名、年龄、位置和收入潜力,您将获得所有的详细信息,而不仅仅是年龄和位置。一致性确保遵循所有适当的数据验证规则,并且只写入允许的数据。如果验证不成功,则数据库将在事务之前回滚到之前的状态。例如,如果您想要将新的客户记录添加到数据库中,您有正确的姓名和年龄,但位置无效,整个事务将失败。隔离性确保在处理并发事务时,一个事务不会影响另一个事务。例如,如果两个用户尝试向数据库中添加相同的客户,第一个用户成功,而第二个用户会因为客户已经存在而收到错误。持久性确保在成功的事务之后,数据是可用的。例如,当您成功地将客户添加到数据库中时,即使发生断电或硬件故障,您也可以确保客户数据是完整的。针对SQL进行优化大多数商业智能和数据分析工具及生态系统都针对SQL进行了优化,而数据仓库提供了一个针对SQL进行优化的查询引擎,支持这些场景。数据湖提供以下优势:存储和处理非结构化数据的能力大多数涉及高级分析、数据科学和机器学习的新兴场景都依赖于处理非结构化数据。数据湖对数据的结构或模式不做任何假设。在从数据湖读取数据时,您可以在读取时定义数据的模式。低成本数据湖是高度优化的存储系统,为用户提供了低成本的拥有权,并且可以存储任意量的数据,无需担心不断增长的费用。丰富的数据管理数据湖提供了一系列功能来帮助管理数据,正如我们在前面的部分所看到的。这些功能包括分层存储、数据复制和数据共享能力。尽管将数据湖和数据仓库统一为一个体系结构具有吸引力,但数据湖的优势是数据仓库的不足之处,反之亦然,这长期以来一直妨碍着数据湖架构的发展。然而,随着组织中对数据湖的日益采用以及在数据湖上运行的各种场景的增加,人们开始积极关注并为实现数据湖架构的关键技术做出贡献。其中一些技术包括源于Databricks的Delta Lake、源于Netflix的Apache Iceberg和源于Uber的Apache Hudi。尽管这些技术本身不同,并从不同的角度解决问题,但它们有一个共同点:它们定义了存储在数据湖中的数据。这种数据格式为在数据湖上提供数据仓库的保证(接近ACID的一致性、元数据处理、模式强制和演化)奠定了基础。它们通过三个关键组件来实现这一点:开放的文件格式定义数据的元数据层理解这些文件格式和元数据层的计算引擎有了这些组件,它们可以将存储在数据湖中的非结构化数据作为对象或文件,并将其以表格的新逻辑形式呈现出来。表格是指以逻辑行和列组织的数据,如图2-11所示。数据格式我们已经确定数据格式对于数据湖架构至关重要,但为什么会这样呢?正如我们之前所看到的,数据仓库中的数据具有关于完整性的强大保证;为了使数据湖中的数据具备类似的保证,将数据限制在一些关键规则下是非常重要的。可以类比为,在教室里,孩子需要遵守一定的规则,以创造一个有利于学习的环境,但同样的孩子可以在公园里自由探索。想象一下,如果你在公园里建立一个教室,你需要做些什么;开放的数据格式试图确保数据在非结构化环境中受到一定规则的限制,而这种环境在这里就是数据湖存储。数据格式对于数据湖架构至关重要,原因如下:存储的数据需要遵守由元数据定义的模式(描述数据集的表格结构的数据)。这里的模式指的是数据的定义表示或描述。存储的数据针对查询进行了优化,特别是为了支持主要使用类似SQL查询的BI用例。这种优化对于支持与数据仓库相当的查询性能至关重要。实际上,解决这些需求还有一个非常好的好处,即这些数据往往具有高度可压缩性,从而实现更快的性能和更低的成本,这意味着你可以同时拥有这两方面的好处。数据湖架构中使用了诸如Delta Lake、Apache Iceberg和Apache Hudi之类的专用格式。我们将在第6章中对它们进行更详细的讨论。它们都源自一种基本的数据格式,即Apache Parquet,这是Apache Hadoop生态系统中使用的列存储数据格式。让我们进行一个小的绕道,了解一下列格式意味着什么。请记住,我们正在讨论的是表格数据,其中数据按行和列组织,如图2-12所示。当涉及到如何在数据湖中存储这些数据时,直觉上可能会认为你将一个记录(即一行)一起存储。而在列格式中,数据以列为导向的方式进行存储,具有相似列值的数据被存储在一起。正是这种对相似数据的捆绑使得列格式(如Apache Parquet)具有高度可压缩性。图2-12提供了相同数据在行导向和列导向结构中存储的表示形式。我们将在第4章中更详细地讨论Apache Parquet。开放数据技术使用Apache Parquet作为其底层数据格式,以便利用Apache Parquet的优化来优化查询。元数据元数据简单地指的是关于数据的数据。例如,如果您有一个包含1000行的表,存储为每个数据集的100行块,那么每个块都与描述存储的数据相关的元数据相关联,比如这个块包含第101至200行,其中包含以A-B开头的姓氏列的值。此外,还在表级别存储了指向不同块的指针的元数据。这些元数据对于最终用户来说并不是很重要,但对于操作数据的计算引擎非常重要。这些计算引擎读取元数据,然后获取相关的数据。像Apache Iceberg、Delta Lake和Apache Hudi这样的技术都有自己的元数据版本,用于确定数据在不同的Parquet文件中的存储和组织方式,哪些数据正在进行更新以及何时进行更新,以便提供数据完整性和一致性,并与计算引擎进行握手,以优化特定的场景。虽然它们都是合适的选择,但每个选项都是根据特定目的进行设计的,因此在设计架构时您需要考虑这一点。Databricks的Delta Lake针对在数据湖上运行高性能的SQL查询进行了优化,利用元数据进行智能数据跳过,只读取为提供查询所需的数据。Uber开源的Apache Hudi主要设计用于支持增量更新,并具有列格式的快速查询性能。Netflix开源的Apache Iceberg主要用于刷新数据集(例如,支持对像S3这样的追加存储系统上的现有数据进行更新),并可由众多计算引擎(如Apache Spark、Trino(Presto SQL)、Apache Hive和Apache Flink)进行读取,程度各不相同。计算引擎与数据仓库不同,数据湖架构中的计算和存储优化并一起作为一个服务提供。在运行数据湖架构时,需要选择合适的计算引擎,以利用用于优化数据存储的数据格式和元数据提供的优化功能。换句话说,数据被优化为以表格形式写入存储,您需要一个计算引擎来理解和读取表格,以有效地查询数据。例如,对于由Databricks开发的Delta Lake格式,其计算组件(即Spark引擎)针对操作Delta表格进行了优化,并通过缓存提供更快的性能以及通过Bloom过滤器索引实现有效的数据跳过。我们将在第6章中对这些计算引擎进行更详细的讨论。数据湖架构的示例应用场景Klodars Corporation将利用数据湖从其操作数据源加载数据到数据湖存储中,类似于我们在现代数据仓库架构中所看到的。让我们更详细地看看这种架构对业务的影响。数据工程团队将使用诸如Apache Spark之类的工具处理来自数据库转储和网站点击流的结构化数据,以生成随时间变化的购物和销售趋势等高价值数据。团队还将处理社交媒体信息流,提取与雨衣和冬季装备相关的数据,以及这些信息流指示的任何相关购买信息。现在让我们来看看如何处理这些提取出的数据。数据工程团队将按计划(例如每日)生成关于销售趋势、库存和供应、网站浏览趋势以及雨衣和冬季装备周围的社交媒体趋势等高价值数据。现在,业务分析师可以开始使用他们熟悉的基于SQL的工具以及像Presto这样的现代查询工具来查询这些数据,而无需移动数据。类似于现代数据仓库模式,数据科学家可以带上自己的数据集,例如天气数据,并探索已经存在于数据湖中的数据。数据湖架构通过消除存储相同数据的两个位置,相比现代数据仓库,提供了一个重要优势。假设数据科学团队利用其新的数据集(例如天气数据)构建了一个将销售与天气相关联的新数据集。由于每个人都使用相同的数据存储和可能相同的数据格式,因此业务分析师可以立即开始进行更深入的分析。同样,如果业务分析生成了特定的筛选数据集,数据科学家可以开始使用该数据集进行分析。花点时间思考一下这意味着什么以及其影响是什么。这完全扩展了不同类别的数据平台使用者之间的洞察力交叉,促进了洞察力的交流。共享平台意味着BI分析师和数据科学家生成的数据对于两者都可用,以进一步创新,从而将数据的价值提升数倍。Klodars Corporation的数据湖架构示例如图2-13所示。数据湖架构的优势和挑战数据湖仓提供了一个关键优势,即能够直接在数据湖上运行高性能的BI/SQL场景,与其他探索性数据科学和机器学习场景并行进行。正如我们在用例中看到的那样,这也促进了数据平台的各个用户部门之间的共享,产生了新的场景。此外,与数据仓库相比,数据湖具有非常高的成本效益。然而,也存在挑战。正如我们在架构部分看到的那样,构建数据湖仓需要精心设计和架构,选择合适的数据格式和计算引擎以实现最优化的解决方案,从而提供更快的性能。如果没有正确的规划,许多问题可能会出现,我们将在第4章中详细讨论这些问题。数据仓库可以直接提供这种优化路径,但它们并不是真正开放的。尽管我没有预测未来的能力,但鉴于数据湖仓架构的快速发展速度,我相信在未来几年中,这个领域将会迅速创新,从而实现简化的端到端体验。数仓和非结构化数据如果数据湖可以开始支持数据仓库场景,那么数据仓库是否也可以开始支持数据湖场景呢?令人惊讶的答案是肯定的。正如我们在前面的部分中看到的那样,Azure Synapse Analytics为Spark、机器学习和SQL提供了统一的数据平台。Google BigQuery支持存储非结构化数据,并原生支持Parquet格式;它还支持查询存储在GCS中的数据。Snowflake最近也推出了对非结构化数据的支持。无论是数据湖支持数据仓库,还是数据仓库支持数据湖,我们当前的创新明确表明,统一的数据平台和无障碍的数据平台是未来的发展方向。Data Mesh在2019年,Thoughtworks的新兴技术总监Zhamak Dehghani撰写了一篇关于数据网格的文章,奠定了数据网格架构的基础。数据网格架构使得组织能够以分散化的方式运行数据基础设施和操作,从而在整个组织中实现数据和洞察力的民主化。让我们看看为什么这种分散化的数据网格对于组织来说如此重要或相关。到目前为止,我们已经讨论了数据湖作为组织的数据和智能的中央存储库,以及技术选择。在架构中的体现方式是作为一个由中央团队管理的基础设施。现在让我们看看在组织中谁负责设计和操作数据湖。数据的提取和处理由一个中央团队管理,通常被称为数据平台团队、数据基础设施团队、数据工程团队或者其他类似的名称。在本节中,我们将称这个团队为数据平台团队。数据平台团队通常拥有以下角色:数据平台架构设计满足组织需求的计算和存储组件的基础设施。数据管理在云上组织数据集;应用数据管理策略,确保数据符合组织在数据保留和数据驻留方面的合规需求。数据治理控制谁可以访问哪些数据,提供目录,使数据平台的使用者可以发现数据集,并管理审计。数据摄取通常负责从各种源头(如本地系统、物联网等)摄取数据,以及可能的数据准备,使其可以被使用。在某些情况下,数据平台团队倾向于将这种摄取工作委托给数据湖的使用者们。换句话说,数据基础设施是由一个中央团队管理的整体单元,而组织的其他部门则专注于消费场景:商业智能、数据科学或其他需求。随着基于数据的场景数量的增加和组织的扩大,这个通常是一个精简团队的数据平台团队很容易被来自整个组织的请求淹没,并成为数据的关键路径,从而引入了瓶颈。数据网格架构呼吁进行一种文化转变,将数据视为可以在组织间共享的产品,而不是需要收集和处理的资产/实体数据。这种文化转变意味着什么呢?在这一点上,我想引用Zhamak Dehghani在她的书《数据网格》(O'Reilly)中提出的一组重要原则:在组织上,责任的转变从一个做所有事情的中央数据平台组织到一个分散的模型,其中每个业务领域都有专门的人员专注于数据需求。在架构上,从一个庞大的中央数据仓库或数据湖的单体实现转变为数据湖和数据仓库的分布式网格,通过共享洞察和数据仍然对数据进行单一逻辑表示。在技术上,从将数据视为独立实体和平台转变为将数据和业务代码作为一个整体进行集成的解决方案。在运营上,从中央运营模型对数据治理的自上而下指令转变为一种联邦模型,其中每个业务领域都拥有并尊重组织的政策。在原则上,从将数据视为需要收集的资产转变为作为产品为用户服务和提供愉悦体验的产品。参考架构数据网格依赖于分布式架构,由多个领域组成。每个领域都是数据及其相关存储和计算组件的独立单元。当一个组织包含多个产品单位,每个单位都有自己的数据需求时,每个产品团队拥有一个由产品团队独立运营和管理的领域。其角色和责任包括以下内容:中央数据平台团队制定并维护计算、存储和数据组件架构的一套蓝图/参考模式。产品团队实施这些蓝图,以使其领域能够运营。这使得产品团队/领域可以使用其选择的基础设施或技术。例如,一个单位可以在AWS上使用数据湖架构,另一个单位可以在Microsoft Azure上实现现代数据仓库架构,同时仍然共享数据和洞察力。关键原则在于,领域中的数据在组织内共享,并在符合合规和治理要求的边界内,遵循无隔离逻辑数据湖的原则,仍然促进数据和洞察力的共享。数据网格架构的示意图如图2-15所示。数据网格架构的示例用例Klodars Corporation 在其软件产品和团队较小的时候运营良好。然而,随着业务的增长和在更多地区的推出,团队和组织规模显著扩大,中央数据平台无法再满足需求的扩展。此外,由于 Klodars Corporation 收购了使用不同技术堆栈的其他公司,将它们整合成一个统一的单位变得困难。Alice 和她的团队在中央数据平台上决定实施数据网格架构。Klodars Corporation 的中央数据平台团队发布了架构,连同部署和配置脚本,以自动设置数据领域,并建立数据治理、合规和数据共享基础设施。Klodars Corporation 拥有销售、营销和客户成功团队,它们实施自己的数据领域,并与其他组织共享洞察力。销售团队发现现代数据仓库架构适合他们的需求,因为他们大量使用操作数据库;客户成功团队发现数据湖架构更适合他们的需求,因为多样化的数据来源可以使他们的商业智能和数据科学团队受益。数据网格模式使 Klodars Corporation 能够给予其数据领域选择的自由,同时促进数据的共享,保持统一数据平台的特点。此外,Klodars Corporation 收购的公司能够保留其现有的数据湖,只需进行微调。当 Klodars Corporation 想要扩展到冬季装备时,它可以与合作伙伴的滑雪公司共享洞察力,以促进更好的合作关系,进一步扩展数据网格架构。Klodars Corporation 正在迅速增长,并希望将业务扩展到欧洲,该地区具有独特的数据驻留和其他合规要求。它可以设置特定于欧盟(EU)的领域,同时遵守 EU 的特定要求,而无需进行大量的开发或重建工作。在未来,当 Klodars Corporation 收购其他公司时,它可以将所收购公司的数据平台作为领域引入到现有的数据网格中。Klodars Corporation 的数据网格架构示例如图2-16所示。数据网格架构的挑战和优势数据网格具有独特的价值主张,不仅提供基础设施和场景的规模扩展,还有助于改变组织围绕数据的文化,正如我们在前面的部分所看到的。数据网格架构提供以下优点,正如使用案例所示:实现自助架构,能够适应组织的增长和数据多样性为领域提供架构和平台的选择灵活性在整个组织中推广数据文化,而不仅仅是小团队的角色,避免瓶颈的产生当然,这种方法也存在挑战。首先,这依赖于各个产品团队是否有熟练的软件开发人员可用,而这并不总是情况。其次,数据湖架构本身就带来了数据和生态系统多样性的复杂性;添加分布式层级增加了这种复杂性。尽管如此,提前投资于这个领域可以为组织的成功打下基础,并且基于数据网格的不断增长的流行度,我可以做出一个有根据的猜测,即在简化此架构的部署和管理方面将会有快速创新。那对我而言什么才是合适的架构?在本节中,我们讨论了三种主要的热门云数据湖架构:现代数据仓库架构,这在组织中普遍存在数据湖仓架构,使得可以直接在数据湖上运行 BI 场景数据网格架构,提供了一种分散式的管理和操作云数据湖的方法那么,你如何确定选择哪种架构?你如何知道你是正确的?尽管我们都是在实践中不断学习,但有一套基本原则可以帮助你朝着正确的方向前进。了解你的客户就像每个项目一样,首先确定你必须按照优先顺序满足的目标以及你的客户群体。根据你的组织需求,你可以从以下一个或多个客户群体开始:BI/数据分析师:为他们准备数据集以供在数据湖上进行分析。这可以通过运行定期作业从各种来源获取数据并进行数据处理生成数据集来实现。数据科学家/探索性分析:建立一个基础设施,使数据科学家可以将自己的数据集带到数据湖进行分析。你还可以选择管理来自已知来源的数据摄取,并在数据湖上为他们提供数据集。我知道有些客户在继续运行数据仓库的同时开始他们的数据湖之旅;他们没有技术负债或现有的兼容性,并从头开始建立数据湖来支持组织的第一批场景。我也知道有些客户通过解决数据湖上的 BI 用户需求开始他们的数据湖之旅;在这种情况下,他们的目标是现代化数据基础架构以支持新的场景,同时保持对现有流程的支持,因此他们有一定的余地进行重构,但优先级是确保系统正常运行。我还见过一些客户将数据湖用作备份计划,同时继续运行他们的本地系统;在这种情况下,他们的云架构必须是本地系统的复制品,并且他们将在以后的阶段考虑现代化。你可以符合其中一种情况,或者有自己的场景。最重要的是,了解你的客户是第一步:与你的客户和业务决策者交流,了解数据在你的组织中的作用,并展示其潜力。了解你的业务驱动因素虽然新技术非常迷人,也是我继续从事我的工作的原因,但我们始终需要记住,技术只是达到目标的手段,每个决策都需要以你试图为组织解决的问题为基础。有许多业务驱动因素会引导组织采用云数据湖。让我们来看看其中的几个:成本迁移到云数据湖可以保证你的总拥有成本(TCO)降低,根据我的经验,这仍然是组织采用云数据湖方法的主要动因之一。确保在架构决策上与降低成本的目标保持一致。新场景尽管一些组织已经有现有的数据基础设施,但它们有动力转向云数据湖,以利用不断增长的现代技术生态系统,如机器学习或实时分析,以区分其业务和产品。如果这是你的动力,那么你倾向于通过这些新场景提供价值,并应相应地定义目标。你是要通过新的营销活动增加采用率,还是通过智能产品提供价值?再次,将你的技术选择与这些目标相衡量。时间尽管组织迁移到云的动机可能是成本、现代化场景和其他因素,但时间有时会决定技术和架构选择。我见过一些客户制定了迁移到云的路线图,同时他们的本地硬件或软件许可证的支持即将终止。然后你的技术/架构选择将受到时间限制的制约。考虑你的增长和未来场景尽管客户需求和业务驱动力定义了你技术和架构决策的优先级,你需要确保所选择的设计不会束缚你的发展空间。举个例子,如果你的数据湖基础设施是由市场部门的需求驱动的,他们需要运行个性化的营销活动并更好地了解你的客户细分,那么你将设计第一个版本的数据湖架构以满足这些需求。确保你专注于从客户系统和社交媒体数据源中获取数据,并生成可供业务分析师使用的数据集,以便他们选择他们想要为其定制活动的优先级较高的细分群体。然而,你的设计应该预见到当这个第一个场景取得成功后,会有更多的场景和更多的客户。我曾经与一些客户合作,他们总是假定数据工程团队将是唯一能访问数据湖中数据的团队,并没有实施正确的安全和访问控制,结果发现各种场景迅速增长,每个人都能访问所有数据,并造成了意外的数据删除。因此,即使你目前只有一个客户,也要考虑如何设计一个多用户系统,重点关注数据组织、安全和治理。我们将在第三章中详细讨论这些问题。设计考虑因素当我与客户讨论他们的数据湖解决方案时,他们经常问我推荐最便宜或最快的方法,我的回答总是“这取决于情况”,我带着微笑给出这个回答。鉴于云数据湖解决方案的灵活性和多样性,以及软件和平台的生态系统,选择合适的方案和方法几乎就像规划你的家庭预算一样。虽然我们可以做一些笼统的陈述,比如“Costco的价格很优惠!”,但不太被理解的含义是“它依赖于您确保不浪费您大量购买的物品。”云数据湖提供了灵活性和较低的成本,但它们依赖于数据平台团队来确保它们以最优化的方式运行。在表格2-1中,我试图对这些架构在几个可预见的维度上进行评估,以便您可以将其作为确定适合您的合适方案的起点。让我们以另一种方式来看这些数据。图表2-17显示了在不同架构之间成本和复杂性之间的权衡关系。混合方式根据组织需求、方案成熟度和数据平台战略的不同,您可能会采用混合方法来管理您的数据湖。例如,虽然大部分组织使用云数据仓库作为中央数据存储库,但有一个创新中心正在使用数据湖架构进行一组精选场景的工作,并逐步将其扩展到整个公司。或者,虽然大部分组织采用数据湖仓架构,但一些团队仍然依赖需要数年才能迁移的传统基础设施。 您的场景的细微差别可能是如此独特或特定于您的组织,以至于超出了本书的范围。然而,本章讨论的原则将帮助您提出正确的问题并做出明智的数据湖架构选择。 大数据生态系统和云数据湖架构是一个快速创新的领域。我敢肯定,当我完成这一章时,某些方面已经发生了变化。总结在本章中,我们更深入地了解了云数据湖的三种关键架构,并与传统云数据仓库架构进行了比较。首先,我们介绍了现代数据仓库架构,在该架构中,您收集和处理数据,并将相对价值密度较低的原始数据转化为高价值的结构化数据,然后将高价值数据加载到云数据仓库以支持BI场景。接下来,我们介绍了数据湖仓架构,该架构支持在数据湖上直接进行BI场景(以及数据工程和数据科学场景),无需云数据仓库。然后,我们探讨了数据网格架构,它提供了一种分散化的管理和操作数据湖的方法,为组织内不断增长的需求和数据快速增加的情况提供可持续的扩展方式。最后,我们综合考虑了组织的成熟度、技能组合和规模等因素,帮助您制定适合您组织的云数据湖架构。在第三章中,我们将更多关注云数据湖中的“数据”部分:数据组织、管理、安全和治理的考虑因素。
清晨我上码
14.4 USDA Food Database(美国农业部食品数据库)这个数据是关于食物营养成分的。存储格式是JSON,看起来像这样:{ "id": 21441, "description": "KENTUCKY FRIED CHICKEN, Fried Chicken, EXTRA CRISPY, Wing, meat and skin with breading", "tags": ["KFC"], "manufacturer": "Kentucky Fried Chicken", "group": "Fast Foods", "portions": [ { "amount": 1, "unit": "wing, with skin", "grams": 68.0 } ... ], "nutrients": [ { "value": 20.8, "units": "g", "description": "Protein", "group": "Composition" }, ... ] } 每种食物都有一系列特征,其中有两个list,protions和nutrients。我们必须把这样的数据进行处理,方便之后的分析。这里使用python内建的json模块:import pandas as pd import numpy as np import jsonpd.options.display.max_rows = 10db = json.load(open('../datasets/usda_food/database.json')) len(db)6636db[0].keys()dict_keys(['manufacturer', 'description', 'group', 'id', 'tags', 'nutrients', 'portions'])db[0]['nutrients'][0]{'description': 'Protein', 'group': 'Composition', 'units': 'g', 'value': 25.18}nutrients = pd.DataFrame(db[0]['nutrients']) nutrients162 rows × 4 columns当把由字典组成的list转换为DataFrame的时候,我们可以吹创业提取的list部分。这里我们提取食品名,群(group),ID,制造商:info_keys = ['description', 'group', 'id', 'manufacturer'] info = pd.DataFrame(db, columns=info_keys) info[:5] info.info()<class 'pandas.core.frame.DataFrame'> RangeIndex: 6636 entries, 0 to 6635 Data columns (total 4 columns): description 6636 non-null object group 6636 non-null object id 6636 non-null int64 manufacturer 5195 non-null object dtypes: int64(1), object(3) memory usage: 207.5+ KB我们可以看到食物群的分布,使用value_counts:pd.value_counts(info.group)[:10]Vegetables and Vegetable Products 812 Beef Products 618 Baked Products 496 Breakfast Cereals 403 Legumes and Legume Products 365 Fast Foods 365 Lamb, Veal, and Game Products 345 Sweets 341 Pork Products 328 Fruits and Fruit Juices 328 Name: group, dtype: int64这里我们对所有的nutrient数据做一些分析,把每种食物的nutrient部分组合成一个大表格。首先,把每个食物的nutrient列表变为DataFrame,添加一列为id,然后把id添加到DataFrame中,接着使用concat联结到一起:# 先创建一个空DataFrame用来保存最后的结果 # 这部分代码运行时间较长,请耐心等待 nutrients_all = pd.DataFrame() for food in db: nutrients = pd.DataFrame(food['nutrients']) nutrients['id'] = food['id'] nutrients_all = nutrients_all.append(nutrients, ignore_index=True)译者:虽然作者在书中说了用concat联结在一起,但我实际测试后,这个concat的方法非常耗时,用时几乎是append方法的两倍,所以上面的代码中使用了append方法。一切正常的话出来的效果是这样的:nutrients_all389355 rows × 5 columns这个DataFrame中有一些重复的部分,看一下有多少重复的行:nutrients_all.duplicated().sum() # number of duplicates14179把重复的部分去掉:nutrients_all = nutrients_all.drop_duplicates() nutrients_all375176 rows × 5 columns为了与info_keys中的group和descripton区别开,我们把列名更改一下:col_mapping = {'description': 'food', 'group': 'fgroup'}info = info.rename(columns=col_mapping, copy=False) info.info()<class 'pandas.core.frame.DataFrame'> RangeIndex: 6636 entries, 0 to 6635 Data columns (total 4 columns): food 6636 non-null object fgroup 6636 non-null object id 6636 non-null int64 manufacturer 5195 non-null object dtypes: int64(1), object(3) memory usage: 207.5+ KBcol_mapping = {'description' : 'nutrient', 'group': 'nutgroup'}nutrients_all = nutrients_all.rename(columns=col_mapping, copy=False) nutrients_all375176 rows × 5 columns上面所有步骤结束后,我们可以把info和nutrients_all合并(merge):ndata = pd.merge(nutrients_all, info, on='id', how='outer') ndata.info()<class 'pandas.core.frame.DataFrame'> Int64Index: 375176 entries, 0 to 375175 Data columns (total 8 columns): nutrient 375176 non-null object nutgroup 375176 non-null object units 375176 non-null object value 375176 non-null float64 id 375176 non-null int64 food 375176 non-null object fgroup 375176 non-null object manufacturer 293054 non-null object dtypes: float64(1), int64(1), object(6) memory usage: 25.8+ MBndata.iloc[30000]nutrient Glycine nutgroup Amino Acids units g value 0.04 id 6158 food Soup, tomato bisque, canned, condensed fgroup Soups, Sauces, and Gravies manufacturer Name: 30000, dtype: object我们可以对食物群(food group)和营养类型(nutrient type)分组后,对中位数进行绘图:result = ndata.groupby(['nutrient', 'fgroup'])['value'].quantile(0.5)%matplotlib inlineresult['Zinc, Zn'].sort_values().plot(kind='barh', figsize=(10, 8))我们还可以找到每一种营养成分含量最多的食物是什么:by_nutrient = ndata.groupby(['nutgroup', 'nutrient']) get_maximum = lambda x: x.loc[x.value.idxmax()] get_minimum = lambda x: x.loc[x.value.idxmin()] max_foods = by_nutrient.apply(get_maximum)[['value', 'food']] # make the food a little smaller max_foods.food = max_foods.food.str[:50]因为得到的DataFrame太大,这里只输出'Amino Acids'(氨基酸)的营养群(nutrient group):max_foods.loc['Amino Acids']['food']nutrient Alanine Gelatins, dry powder, unsweetened Arginine Seeds, sesame flour, low-fat Aspartic acid Soy protein isolate Cystine Seeds, cottonseed flour, low fat (glandless) Glutamic acid Soy protein isolate ... Serine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Threonine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Tryptophan Sea lion, Steller, meat with fat (Alaska Native) Tyrosine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Valine Soy protein isolate, PROTEIN TECHNOLOGIES INTE... Name: food, Length: 19, dtype: object
清晨我上码
时间序列(TimeSeries)#创建时间序列数据 rng = pd.date_range('1/1/2012', periods=300, freq='S') ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng) ts 2012-01-01 00:00:00 44 2012-01-01 00:00:01 54 2012-01-01 00:00:02 132 2012-01-01 00:00:03 70 2012-01-01 00:00:04 476 ... 2012-01-01 00:04:55 178 2012-01-01 00:04:56 83 2012-01-01 00:04:57 184 2012-01-01 00:04:58 223 2012-01-01 00:04:59 179 Freq: S, Length: 300, dtype: int32时间频率转换的参数如下resample重采样ts.resample('1T').sum() #按一分钟重采样之后求和 ts.resample('1T').mean() #按一分钟重采样之后求平均 ts.resample('1T').median() #按一分钟重采样之后求中位数 2023-01-01 00:00:00 275.5 2023-01-01 00:01:00 245.0 2023-01-01 00:02:00 233.5 2023-01-01 00:03:00 284.0 2023-01-01 00:04:00 245.5 Freq: T, dtype: float64执行多个聚合使用agg函数执行多个聚合。ts.resample('1T').agg(['min','max', 'sum']) min max sum 2023-01-01 00:00:00 0 492 15536 2023-01-01 00:01:00 3 489 15840 2023-01-01 00:02:00 3 466 14282 2023-01-01 00:03:00 2 498 15652 2023-01-01 00:04:00 6 489 15119上采样和填充值上采样是下采样的相反操作。它将时间序列数据重新采样到一个更小的时间框架。例如,从小时到分钟,从年到天。结果将增加行数,并且附加的行值默认为NaN。内置的方法ffill()和bfill()通常用于执行前向填充或后向填充来替代NaN。rng = pd.date_range('1/1/2023', periods=200, freq='H') ts = pd.Series(np.random.randint(0, 200, len(rng)), index=rng) ts 2023-01-01 00:00:00 16 2023-01-01 01:00:00 19 2023-01-01 02:00:00 170 2023-01-01 03:00:00 66 2023-01-01 04:00:00 33 ... 2023-01-09 03:00:00 31 2023-01-09 04:00:00 61 2023-01-09 05:00:00 28 2023-01-09 06:00:00 67 2023-01-09 07:00:00 137 Freq: H, Length: 200, dtype: int32#下采样到分钟 ts.resample('30T').asfreq() 2023-01-01 00:00:00 16.0 2023-01-01 00:30:00 NaN 2023-01-01 01:00:00 19.0 2023-01-01 01:30:00 NaN 2023-01-01 02:00:00 170.0 ... 2023-01-09 05:00:00 28.0 2023-01-09 05:30:00 NaN 2023-01-09 06:00:00 67.0 2023-01-09 06:30:00 NaN 2023-01-09 07:00:00 137.0 Freq: 30T, Length: 399, dtype: float64通过apply传递自定义功能import numpy as np def res(series): return np.prod(series) ts.resample('30T').apply(res) 2023-01-01 00:00:00 16 2023-01-01 00:30:00 1 2023-01-01 01:00:00 19 2023-01-01 01:30:00 1 2023-01-01 02:00:00 170 ... 2023-01-09 05:00:00 28 2023-01-09 05:30:00 1 2023-01-09 06:00:00 67 2023-01-09 06:30:00 1 2023-01-09 07:00:00 137 Freq: 30T, Length: 399, dtype: int32DataFrame对象对于DataFrame对象,关键字on可用于指定列而不是重新取样的索引df = pd.DataFrame(data=9*[range(4)], columns=['a', 'b', 'c', 'd']) df['time'] = pd.date_range('1/1/2000', periods=9, freq='T') df.resample('3T', on='time').sum() Out[81]: a b c d time 2000-01-01 00:00:00 0 3 6 9 2000-01-01 00:03:00 0 3 6 9 2000-01-01 00:06:00 0 3 6 9