清晨我上码
IP:
0关注数
20粉丝数
0获得的赞
工作年
编辑资料
链接我:

创作·109

全部
问答
动态
项目
学习
专栏
清晨我上码

pandas使用教程:导入数据和查找行和列,条件选择

导入数据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
0
0
0
浏览量2020
清晨我上码

pandas教程:Time Series Basics 时间序列基础

11.2 Time Series Basics(时间序列基础)在pandas中,一个基本的时间序列对象,是一个用时间戳作为索引的Series,在pandas外部的话,通常是用python 字符串或datetime对象来表示的:import pandas as pd import numpy as np from datetime import datetimedates = [datetime(2011, 1, 2), datetime(2011, 1, 5), datetime(2011, 1, 7), datetime(2011, 1, 8), datetime(2011, 1, 10), datetime(2011, 1, 12)]ts = pd.Series(np.random.randn(6), index=dates) ts2011-01-02 0.384868 2011-01-05 0.669181 2011-01-07 2.553288 2011-01-08 -1.808783 2011-01-10 1.180570 2011-01-12 -0.928942 dtype: float64上面的转化原理是,datetime对象被放进了DatetimeIndex:ts.indexDatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07', '2011-01-08', '2011-01-10', '2011-01-12'], dtype='datetime64[ns]', freq=None)像其他的Series一行,数值原色会自动按时间序列索引进行对齐:ts[::2]2011-01-02 0.384868 2011-01-07 2.553288 2011-01-10 1.180570 dtype: float64ts + ts[::2]2011-01-02 0.769735 2011-01-05 NaN 2011-01-07 5.106575 2011-01-08 NaN 2011-01-10 2.361140 2011-01-12 NaN dtype: float64ts[::2]会在ts中,每隔两个元素选一个元素。pandas中的时间戳,是按numpy中的datetime64数据类型进行保存的,可以精确到纳秒的级别:ts.index.dtypedtype('<M8[ns]')DatetimeIndex的标量是pandas的Timestamp对象:stamp = ts.index[0] stampTimestamp('2011-01-02 00:00:00')Timestamp可以在任何地方用datetime对象进行替换。1 Indexing, Selection, Subsetting(索引,选择,取子集)当我们基于标签进行索引和选择时,时间序列就像是pandas.Series:ts2011-01-02 0.384868 2011-01-05 0.669181 2011-01-07 2.553288 2011-01-08 -1.808783 2011-01-10 1.180570 2011-01-12 -0.928942 dtype: float64stamp = ts.index[2]ts[stamp]2.5532875030792592为了方便,我们可以直接传入一个字符串用来表示日期:ts['1/10/2011']1.1805698813038874ts['20110110']1.1805698813038874对于比较长的时间序列,我们可以直接传入一年或一年一个月,来进行数据选取:longer_ts = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000)) longer_ts2000-01-01 -0.801668 2000-01-02 -0.325797 2000-01-03 0.047318 2000-01-04 0.239576 2000-01-05 -0.467691 2000-01-06 1.394063 2000-01-07 0.416262 2000-01-08 -0.739839 2000-01-09 -1.504631 2000-01-10 -0.798753 2000-01-11 0.758856 2000-01-12 1.163517 2000-01-13 1.233826 2000-01-14 0.675056 2000-01-15 -1.079219 2000-01-16 0.212076 2000-01-17 -0.242134 2000-01-18 -0.318024 2000-01-19 0.040686 2000-01-20 -1.342025 2000-01-21 -0.130905 2000-01-22 -0.122308 2000-01-23 -0.924727 2000-01-24 0.071544 2000-01-25 0.483302 2000-01-26 -0.264231 2000-01-27 0.815791 2000-01-28 0.652885 2000-01-29 0.203818 2000-01-30 0.007890 ... 2002-08-28 -2.375283 2002-08-29 0.843647 2002-08-30 0.069483 2002-08-31 -1.151590 2002-09-01 -2.348154 2002-09-02 -0.309723 2002-09-03 -1.017466 2002-09-04 -2.078659 2002-09-05 -1.828568 2002-09-06 0.546299 2002-09-07 0.861304 2002-09-08 -0.823128 2002-09-09 -0.150047 2002-09-10 -1.984674 2002-09-11 0.468010 2002-09-12 -0.066440 2002-09-13 -1.629502 2002-09-14 0.044870 2002-09-15 0.007970 2002-09-16 0.812104 2002-09-17 -1.835575 2002-09-18 -0.218055 2002-09-19 -0.271351 2002-09-20 -1.852212 2002-09-21 0.546462 2002-09-22 0.776960 2002-09-23 -1.140997 2002-09-24 -2.213685 2002-09-25 -0.586588 2002-09-26 -1.472430 Freq: D, dtype: float64longer_ts['2001']2001-01-01 0.588405 2001-01-02 -3.027909 2001-01-03 -0.492280 2001-01-04 -0.807809 2001-01-05 -0.124139 2001-01-06 -0.198966 2001-01-07 2.015447 2001-01-08 1.454119 2001-01-09 0.157505 2001-01-10 1.077689 2001-01-11 -0.246538 2001-01-12 -0.865122 2001-01-13 -0.082186 2001-01-14 1.928050 2001-01-15 0.320741 2001-01-16 0.473770 2001-01-17 0.036649 2001-01-18 1.405034 2001-01-19 0.560502 2001-01-20 -0.695138 2001-01-21 3.318884 2001-01-22 1.704966 2001-01-23 0.145167 2001-01-24 0.366667 2001-01-25 -0.565675 2001-01-26 0.940406 2001-01-27 -1.468772 2001-01-28 0.098759 2001-01-29 0.267449 2001-01-30 -0.221643 ... 2001-12-02 0.002522 2001-12-03 -0.046712 2001-12-04 1.825249 2001-12-05 -1.000655 2001-12-06 -0.807582 2001-12-07 0.750439 2001-12-08 1.531707 2001-12-09 -0.195239 2001-12-10 -0.087465 2001-12-11 -0.041450 2001-12-12 1.992200 2001-12-13 -0.294916 2001-12-14 1.215363 2001-12-15 0.029039 2001-12-16 -0.165380 2001-12-17 1.192535 2001-12-18 0.737760 2001-12-19 0.044022 2001-12-20 0.582560 2001-12-21 -0.213569 2001-12-22 -0.024512 2001-12-23 -1.140873 2001-12-24 -1.351333 2001-12-25 0.725253 2001-12-26 -0.943740 2001-12-27 -2.134039 2001-12-28 -0.548597 2001-12-29 1.497907 2001-12-30 -0.594708 2001-12-31 0.068177 Freq: D, dtype: float64这里,字符串’2001’就直接被解析为一年,然后选中这个时期的数据。我们也可以指定月份:longer_ts['2001-05']2001-05-01 -0.560227 2001-05-02 2.160259 2001-05-03 -0.826092 2001-05-04 -0.183020 2001-05-05 -0.294708 2001-05-06 -1.210785 2001-05-07 0.609260 2001-05-08 -1.155377 2001-05-09 -0.127132 2001-05-10 0.576327 2001-05-11 -0.955206 2001-05-12 -2.002019 2001-05-13 -0.969865 2001-05-14 0.820993 2001-05-15 0.557336 2001-05-16 -0.262222 2001-05-17 -0.086760 2001-05-18 0.151608 2001-05-19 1.097604 2001-05-20 0.212148 2001-05-21 -1.164944 2001-05-22 -0.100020 2001-05-23 0.734738 2001-05-24 1.730438 2001-05-25 1.352858 2001-05-26 0.644984 2001-05-27 0.997554 2001-05-28 1.434452 2001-05-29 0.395946 2001-05-30 -0.142523 2001-05-31 1.205485 Freq: D, dtype: float64利用datetime进行切片(slicing)也没问题:ts[datetime(2011, 1, 7)]2.5532875030792592因为大部分时间序列是按年代时间顺序来排列的,我们可以用时间戳来进行切片,选中一段范围内的时间:ts2011-01-02 0.384868 2011-01-05 0.669181 2011-01-07 2.553288 2011-01-08 -1.808783 2011-01-10 1.180570 2011-01-12 -0.928942 dtype: float64ts['1/6/2011':'1/11/2011']2011-01-07 2.553288 2011-01-08 -1.808783 2011-01-10 1.180570 dtype: float64记住,这种方式的切片得到的只是原来数据的一个视图,如果我们在切片的结果上进行更改的的,原来的数据也会变化。有一个相等的实例方法(instance method)也能切片,truncate,能在两个日期上,对Series进行切片:ts.truncate(after='1/9/2011')2011-01-02 0.384868 2011-01-05 0.669181 2011-01-07 2.553288 2011-01-08 -1.808783 dtype: float64所有这些都适用于DataFrame,我们对行进行索引:dates = pd.date_range('1/1/2000', periods=100, freq='W-WED')long_df = pd.DataFrame(np.random.randn(100, 4), index=dates, columns=['Colorado', 'Texas', 'New York', 'Ohio'])long_df.loc['5-2001']2 Time Series with Duplicate Indices(重复索引的时间序列)在某些数据中,可能会遇到多个数据在同一时间戳下的情况:dates = pd.DatetimeIndex(['1/1/2000', '1/2/2000', '1/2/2000', '1/2/2000', '1/3/2000'])dup_ts = pd.Series(np.arange(5), index=dates) dup_ts2000-01-01 0 2000-01-02 1 2000-01-02 2 2000-01-02 3 2000-01-03 4 dtype: int64我们通过is_unique属性来查看index是否是唯一值:dup_ts.index.is_uniqueFalse对这个时间序列取索引的的话, 要么得到标量,要么得到切片,这取决于时间戳是否是重复的:dup_ts['1/3/2000'] # not duplicated4dup_ts['1/2/2000'] # duplicated2000-01-02 1 2000-01-02 2 2000-01-02 3 dtype: int64假设我们想要聚合那些有重复时间戳的数据,一种方法是用groupby,设定level=0:grouped = dup_ts.groupby(level=0) grouped.mean()2000-01-01 0 2000-01-02 2 2000-01-03 4 dtype: int64grouped.count()2000-01-01 1 2000-01-02 3 2000-01-03 1 dtype: int64
0
0
0
浏览量1403
清晨我上码

pandas教程:Essential Functionality 索引 过滤 映射 排序

5.2 Essential Functionality(主要功能)接下来介绍pandas中的一些主要功能,这里只介绍一些经常用到的。1 Reindexing(重新索引)pandas中一个重要的方法是reindex,已实施在创建object的时候遵照一个新的index。如下例:import pandas as pdobj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c']) objd 4.5 b 7.2 a -5.3 c 3.6 dtype: float64在series上调用reindex能更改index,如果没有对应index的话会引入缺失数据:obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e']) obj2a -5.3 b 7.2 c 3.6 d 4.5 e NaN dtype: float64在处理时间序列这样的数据时,我们可能需要在reindexing的时候需要修改值。method选项能做到这一点,比如设定method为ffill:obj3 = pd.Series(['bule', 'purple', 'yellow'], index=[0, 2, 4]) obj30 bule 2 purple 4 yellow dtype: objectobj3.reindex(range(6), method='ffill')0 bule 1 bule 2 purple 3 purple 4 yellow 5 yellow dtype: object对于DataFrame,reindex能更改row index,或column index。reindex the rows:import numpy as npframe = pd.DataFrame(np.arange(9).reshape(3, 3), index=['a', 'c', 'd'], columns=['Ohio', 'Texas', 'California'])frameframe2 = frame.reindex(['a', 'b', 'c', 'd']) frame2更改columns index:states = ['Texas', 'Utah', 'California']frame.reindex(columns=states)还可以使用loc更简洁的reindex:frame.loc[['a', 'b', 'c', 'd'], states]2 Dropping Entries from an Axis (按轴删除记录)对于series,drop回返回一个新的object,并删去你制定的axis的值:obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e']) obja 0.0 b 1.0 c 2.0 d 3.0 e 4.0 dtype: float64new_obj = obj.drop('c') new_obja 0.0 b 1.0 d 3.0 e 4.0 dtype: float64obj.drop(['d', 'c'])a 0.0 b 1.0 e 4.0 dtype: float64对于DataFrame,index能按行或列的axis来删除:data = pd.DataFrame(np.arange(16).reshape(4, 4), index=['Ohio', 'Colorado', 'Utah', 'New York'], columns=['one', 'two', 'three', 'four']) data行处理:如果a sequence of labels(一个标签序列)来调用drop,会删去row labels(axis 0):data.drop(['Colorado', 'Ohio'])列处理:drop列的话,设定axis=1或axis='columns':data.drop('two', axis=1)data.drop(['two', 'four'], axis='columns')drop也可以不返回一个新的object,而是直接更改series or dataframe in-place:obj.drop('c', inplace=True) obja 0.0 b 1.0 d 3.0 e 4.0 dtype: float643 Indexing, Selection, and Filtering(索引,选择,过滤)series indexing(obj[...]) 相当于numpy的array indexing, 而且除了整数,还可以使用series的index:obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd']) obja 0.0 b 1.0 c 2.0 d 3.0 dtype: float64obj['b']1.0obj[1]1.0obj[2:4]c 2.0 d 3.0 dtype: float64# 选中行 obj[['b', 'a', 'd']]b 1.0 a 0.0 d 3.0 dtype: float64obj[[1, 3]]b 1.0 d 3.0 dtype: float64obj[obj < 2]a 0.0 b 1.0 dtype: float64用label来slicing(切片)的时候,和python的切片不一样的在于,会包括尾节点:obj['b':'c'] 1 b 1.0 c 2.0 dtype: float64可以直接给选中的label更改值:obj['b':'c'] = 5 obja 0.0 b 5.0 c 5.0 d 3.0 dtype: float64而对于DataFrame,indexing可以通过一个值或序列,选中一个以上的列:data = pd.DataFrame(np.arange(16).reshape((4, 4)), index=['Ohio', 'Colorado', 'Utah', 'New York'], columns=['one', 'two', 'three', 'four']) datadata['two']Ohio 1 Colorado 5 Utah 9 New York 13 Name: two, dtype: int64data[['three', 'one']]dataframe的indexing有一些比较特别的方式。比如通过布尔数组:data[:2]data[data['three'] > 5]行选择的语法格式data[:2]是很方便的。给[]里传入一个list的话,可以选择列。另一种方法是用boolean dataframe:data < 5data[data < 5] = 0 dataSelection with loc and iloc(用loc和iloc来选择)对于label-indexing on rows, 我们介绍特别的索引符,loc and iloc. 这两个方法能通过axis labels(loc)或integer(iloc),来选择行或列。一个列子,选中一行多列by label:datadata.loc['Colorado', ['two', 'three']]two 5 three 6 Name: Colorado, dtype: int64同iloc实现相同的效果:data.iloc[2, [3, 0, 1]]four 11 one 8 two 9 Name: Utah, dtype: int64data.iloc[2] # 一行one 8 two 9 three 10 four 11 Name: Utah, dtype: int64data.iloc[[1, 2], [3, 0, 1]]indexing函数也能用于切片,不论是single labels或lists of labels:data.loc[:'Utah', 'two']Ohio 0 Colorado 5 Utah 9 Name: two, dtype: int64data.iloc[:, :3][data.three > 5]注意:当设计pandas的时候,作者发现frame[:, col]这样的语法是比较冗长的,因为这是会被经常用到的一个功能。作者把一些indexing的功能(lable or integer)集成在了ix这个方法上。实际中,因为这种label和integer都可以用的方式很方便,于是pandas team设计了loc和iloc来实现label-based和integer-based indexing.虽然ix indexing依然存在,但是已经过时,不推荐使用。4 Integer Indexes(整数索引)一些新手再用integer来index的时候,总是会被绊倒。因为这种方法和python用于list和tuple的indexing方法不同。比如,你不希望下面的代码出现error:ser = pd.Series(np.arange(3.))ser0 0.0 1 1.0 2 2.0 dtype: float64ser[-1]--------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-61-3cbe0b873a9e> in <module>() ----> 1 ser[-1] /Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/pandas/core/series.py in __getitem__(self, key) 581 key = com._apply_if_callable(key, self) 582 try: --> 583 result = self.index.get_value(self, key) 584 585 if not lib.isscalar(result): /Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/pandas/indexes/base.py in get_value(self, series, key) 1978 try: 1979 return self._engine.get_value(s, k, -> 1980 tz=getattr(series.dtype, 'tz', None)) 1981 except KeyError as e1: 1982 if len(self) > 0 and self.inferred_type in ['integer', 'boolean']: pandas/index.pyx in pandas.index.IndexEngine.get_value (pandas/index.c:3332)() pandas/index.pyx in pandas.index.IndexEngine.get_value (pandas/index.c:3035)() pandas/index.pyx in pandas.index.IndexEngine.get_loc (pandas/index.c:4018)() pandas/hashtable.pyx in pandas.hashtable.Int64HashTable.get_item (pandas/hashtable.c:6610)() pandas/hashtable.pyx in pandas.hashtable.Int64HashTable.get_item (pandas/hashtable.c:6554)() KeyError: -1看到了,pandas在整数索引上可能会出错。这里我们有一个index包括0,1,2,但是猜测用户想要什么是很困难的:ser0 0.0 1 1.0 2 2.0 dtype: float64另一方面,如果用非整数来做index,就没有歧义了:ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c']) ser2[-1]2.0为了保持连贯性,如果axis index里包含integer,那么选择数据的时候,就会是label-oriented. 为了更精确地选择,使用loc(for label)或ilco(for integers):ser[:1]0 0.0 dtype: float64ser.loc[:1]0 0.0 1 1.0 dtype: float64ser.iloc[:1]0 0.0 dtype: float64rithmetic and Data Alignment (算数和数据对齐)pandas一个有用的feature就是,不同index的obejct之间的算数计算。如果两个object相加,但他们各自的index并不相同,最后结果得到的index是这两个index的合集:s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])s2 = pd.Series([2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])s1a 7.3 c -2.5 d 3.4 e 1.5 dtype: float64s2a 2.1 c 3.6 e -1.5 f 4.0 g 3.1 dtype: float64s1 + s2a 9.4 c 1.1 d NaN e 0.0 f NaN g NaN dtype: float64这种数据对齐的方式(internal data alignment)引入了很多缺失值在没有的位置上。这些缺失值会被用在之后的算数计算中。在DataFrame中,数据对齐同时发生在行和列上:df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'), index=['Ohio', 'Texas', 'Colorado'])df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])df1df2相加的结果就是两个DataFrame,行和列的合集:df1 + df2因为'c'和'e'列都不在两个DataFrame里,所有全是缺失值。对于行,即使有相同的,但列不一样的话也会是缺失值。如果两个DataFrame相加,而且没有column和row,结果会全是null:df1 = pd.DataFrame({'A': [1, 2]}) df2 = pd.DataFrame({'B': [3, 4]})df1df2df1 - df2Arithmetic methods with fill values (带填充值的算数方法)对于上面那些缺失值,我们想要填上0:df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)), columns=list('abcd')) df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)), columns=list('abcde')) df2.loc[1, 'b'] = np.nandf1df2不使用添加方法的结果:df1 + df2使用fill_value:df1.add(df2, fill_value=0)每一个都有一个配对的,以 r 开头,意思是反转:1 / df1df1.rdiv(1)在reindex(重建索引)的时候,也可以使用fill_value:df1.reindex(columns=df2.columns, fill_value=0)Operations between DataFrame and Series (DataFrame和Series之间的操作)先举个numpy的例子帮助理解,可以考虑成一个二维数组和它的一行:arr = np.arange(12.).reshape((3, 4)) arrarray([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.]])arr[0]array([ 0., 1., 2., 3.])arr - arr[0]array([[ 0., 0., 0., 0.], [ 4., 4., 4., 4.], [ 8., 8., 8., 8.]])可以看到,这个减法是用在了每一行上。这种操作叫broadcasting,在Appendix A有更详细的解释。DataFrame和Series的操作也类似:frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon']) series = frame.iloc[0]frameseriesb 0.0 d 1.0 e 2.0 Name: Utah, dtype: float64可以理解为series的index与dataframe的列匹配,broadcasting down the rows(向下按行广播):frame - series如果一个index既不在DataFrame的column中,也不再series里的index中,那么结果也是合集:series2 = pd.Series(range(3), index=['b', 'e', 'f']) frame + series2如果想要广播列,去匹配行,必须要用到算数方法:series3 = frame['d'] frameseries3Utah 1.0 Ohio 4.0 Texas 7.0 Oregon 10.0 Name: d, dtype: float64frame.sub(series3, axis='index')axis参数就是用来匹配轴的。在这个例子里是匹配dataframe的row index(axis='index or axis=0),然后再广播。6 Function Application and Mapping (函数应用和映射)numpy的ufuncs(element-wise数组方法)也能用在pandas的object上:frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon']) framenp.abs(frame)另一个常用的操作是把一个用在一维数组上的函数,应用在一行或一列上。要用到DataFrame中的apply函数:f = lambda x: x.max() - x.min() frame.apply(f)b 1.334579 d 0.609748 e 2.417783 dtype: float64这里函数f,计算的是一个series中最大值和最小值的差,在frame中的每一列,这个函数被调用一次。作为结果的series,它的index就是frame的column。如果你传入axis='column'用于apply,那么函数会被用在每一行:frame.apply(f, axis='columns')Utah 1.004883 Ohio 1.953030 Texas 0.349825 Oregon 1.799333 dtype: float64像是sum, mean这样的数组统计方法,DataFrame中已经集成了,所以没必要用apply。apply不会返回标量,只会返回一个含有多个值的series:def f(x): return pd.Series([x.min(), x.max()], index=['min', 'max'])frameframe.apply(f)element-wise的python函数也能用。假设想要格式化frame中的浮点数,变为string。可以用apply map:format = lambda x: '%.2f' % xframe.applymap(format)applymap的做法是,series有一个map函数,能用来实现element-wise函数:frame['e'].map(format)Utah -0.71 Ohio 1.07 Texas -0.16 Oregon -1.35 Name: e, dtype: object7 Sorting and Ranking (排序)按row或column index来排序的话,可以用sort_index方法,会返回一个新的object:obj = pd.Series(range(4), index=['d', 'a', 'b', 'c']) obj.sort_index()a 1 b 2 c 3 d 0 dtype: int64在DataFrame,可以用index或其他axis来排序:frame = pd.DataFrame(np.arange(8).reshape((2, 4)), index=['three', 'one'], columns=['d', 'a', 'b', 'c']) frameframe.sort_index()frame.sort_index(axis=1)默认是升序,可以设置降序:frame.sort_index(axis=1, ascending=False)通过值来排序,用sort_values方法:obj = pd.Series([4, 7, -3, 2]) obj.sort_values()2 -3 3 2 0 4 1 7 dtype: int64缺失值会被排在最后:obj = pd.Series([4, np.nan, 7, np.nan, -3, 2]) obj.sort_values()4 -3.0 5 2.0 0 4.0 2 7.0 1 NaN 3 NaN dtype: float64对于一个DataFrame,可以用一列或多列作为sort keys。这样的话,只需要把一列多多列的名字导入到sort_values即可:frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]}) frameframe.sort_values(by='b')多列排序的话,传入一个list of names:frame.sort_values(by=['a', 'b'])ranking(排名)是给有效的数据分配数字。rank方法能用于series和DataFrame,rank方法默认会给每个group一个mean rank(平均排名)。rank 表示在这个数在原来的Series中排第几名,有相同的数,取其排名平均(默认)作为值:obj = pd.Series([7, -5, 7, 4, 2, 0, 4]) obj0 7 1 -5 2 7 3 4 4 2 5 0 6 4 dtype: int64obj.sort_values()1 -5 5 0 4 2 3 4 6 4 0 7 2 7 dtype: int64obj.rank()0 6.5 1 1.0 2 6.5 3 4.5 4 3.0 5 2.0 6 4.5 dtype: float64在obj中,4和4的排名是第4名和第五名,取平均得4.5。7和7的排名分别是第六名和第七名,则其排名取平均得6.5。rank也可以根据数据被观测到的顺序来设定:obj0 7 1 -5 2 7 3 4 4 2 5 0 6 4 dtype: int64obj.rank(method='first')0 6.0 1 1.0 2 7.0 3 4.0 4 3.0 5 2.0 6 5.0 dtype: float64这里没有给0和2(指两个数字7)赋予average rank 6.5,而是给第一个看到的7(label 0)设置rank为6,第二个看到的7(label 2)设置rank为7。也可以设置降序:# Assign tie values the maximum rank in the group obj.rank(ascending=False, method='max')0 2.0 1 7.0 2 2.0 3 4.0 4 5.0 5 6.0 6 4.0 dtype: float64dataframe 可以根据行或列来计算rank:frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1], 'c': [-2, 5, 8, -2.5]}) frameframe.rank(axis='columns') # columns表示列与列之间的排序(即每一行里数据间的排序)8 Axis Indexes with Duplicate Labels (有重复label的轴索引)我们看到的所有例子都有unique axis labels(index values),唯一的轴标签(索引值)。一些pandas函数(reindex),需要label是唯一的,但这并是不强制的。比如下面有一个重复的索引:obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c']) obja    0a 1 b 2 b 3 c 4 dtype: int64index的is_unique特性能告诉我们label是否是唯一的:obj.index.is_uniqueFalse数据选择对于重复label则表现有点不同。如果一个label有多个值,那么就会返回一个series, 如果是label只对应一个值的话,会返回一个标量:obj['a']a 0 a 1 dtype: int64obj['c']4这个选择的逻辑也应用于DataFrame:df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b']) dfdf.loc['b']
0
0
0
浏览量471
清晨我上码

numpy教程:Example Random Walks 随机漫步

这个例子让我了解一个在实际任务中如何利用数组操作。首先一个最简单的随机漫步:从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)
0
0
0
浏览量1990
清晨我上码

pandas教程:时区计数 USA.gov Data from Bitly USA.gov数据集

14.1 USA.gov Data from Bitly(USA.gov数据集)2011年,短链接服务(URL shortening service)商Bitly和美国政府网站USA.gov合作,提供了一份从用户中收集来的匿名数据,这些用户使用了结尾为.gov或.mail的短链接。在2011年,这些数据的动态信息每小时都会保存一次,并可供下载。不过在2017年,这项服务被停掉了。数据是每小时更新一次,文件中的每一行都用JOSN(JavaScript Object Notation)格式保存。我们先读取几行看一下数据是什么样的:path = '../datasets/bitly_usagov/example.txt'open(path).readline()'{ "a": "Mozilla\\/5.0 (Windows NT 6.1; WOW64) AppleWebKit\\/535.11 (KHTML, like Gecko) Chrome\\/17.0.963.78 Safari\\/535.11", "c": "US", "nk": 1, "tz": "America\\/New_York", "gr": "MA", "g": "A6qOVH", "h": "wfLQtf", "l": "orofrog", "al": "en-US,en;q=0.8", "hh": "1.usa.gov", "r": "http:\\/\\/www.facebook.com\\/l\\/7AQEFzjSi\\/1.usa.gov\\/wfLQtf", "u": "http:\\/\\/www.ncbi.nlm.nih.gov\\/pubmed\\/22415991", "t": 1331923247, "hc": 1331822918, "cy": "Danvers", "ll": [ 42.576698, -70.954903 ] }\n'python有很多内置的模块能把JSON字符串转换成Python字典对象。这里我们用JSON模块:import json path = '../datasets/bitly_usagov/example.txt' records = [json.loads(line) for line in open(path)]上面这种方法叫做列表推导式, list comprehension, 在一组字符串上执行一条相同操作(比如这里的json.loads)。结果对象records现在是一个由dict组成的list:records[0]{'a': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.78 Safari/535.11', 'al': 'en-US,en;q=0.8', 'c': 'US', 'cy': 'Danvers', 'g': 'A6qOVH', 'gr': 'MA', 'h': 'wfLQtf', 'hc': 1331822918, 'hh': '1.usa.gov', 'l': 'orofrog', 'll': [42.576698, -70.954903], 'nk': 1, 'r': 'http://www.facebook.com/l/7AQEFzjSi/1.usa.gov/wfLQtf', 't': 1331923247, 'tz': 'America/New_York', 'u': 'http://www.ncbi.nlm.nih.gov/pubmed/22415991'}records[0]['tz']'America/New_York'1 Counting Time Zones in Pure Python(用纯python代码对时区进行计数)我们想知道数据集中出现在哪个时区(即tz字段)time_zones = [rec['tz'] for rec in records]--------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-10-db4fbd348da9> in <module>() ----> 1 time_zones = [rec['tz'] for rec in records] <ipython-input-10-db4fbd348da9> in <listcomp>(.0) ----> 1 time_zones = [rec['tz'] for rec in records] KeyError: 'tz'看来并不是所有的记录都有时区字段。那么只需要在推导式的末尾加一个if 'tz' in rec判断即可time_zones = [rec['tz'] for rec in records if 'tz' in rec]time_zones[:10]['America/New_York', 'America/Denver', 'America/New_York', 'America/Sao_Paulo', 'America/New_York', 'America/New_York', 'Europe/Warsaw', '', '', '']在这10条时区信息中,可以看到有些是空字符串,现在先留着。为了对时区进行计数,我们用两种方法:一个用纯python代码,比较麻烦。另一个用pandas,比较简单。 这里我们先介绍使用纯python代码的方法:遍历时区的过程中将计数值保存在字典中:def get_counts(sequence): counts = {} for x in sequence: if x in counts: counts[x] += 1 else: counts[x] = 1 return counts使用python标准库的话,能把代码写得更简洁一些:from collections import defaultdict def get_counts2(sequence): counts = defaultdict(int) # 所有的值均会被初始化为0 for x in sequence: counts[x] += 1 return counts(译者:下面关于defaultdict的用法是我从Stack Overflow上找到的,英文比较多,简单的说就是通常如果一个字典里不存在一个key,调用的时候会报错,但是如果我们设置了了default,就不会被报错,而是会新建一个key,对应的value就是我们设置的int,这里int代表0)defaultdict means that if a key is not found in the dictionary, then instead of a KeyError being thrown, a new entry is created. The type of this new entry is given by the argument of defaultdict.somedict = {} print(somedict[3]) # KeyError someddict = defaultdict(int) print(someddict[3]) # print int(), thus 0Usually, a Python dictionary throws a KeyError if you try to get an item with a key that is not currently in the dictionary. The defaultdict in contrast will simply create any items that you try to access (provided of course they do not exist yet). To create such a “default” item, it calls the function object that you pass in the constructor (more precisely, it’s an arbitrary “callable” object, which includes function and type objects). For the first example, default items are created using int(), which will return the integer object 0. For the second example, default items are created using list(), which returns a new empty list object.someddict = defaultdict(int) print(someddict[3])0someddict[3]0上面用函数的方式写出来是为了有更高的可用性。要对它进行时区处理,只需要将time_zones传入即可:counts = get_counts(time_zones)counts['America/New_York']1251len(time_zones)3440如何想要得到前10位的时区及其计数值,我们需要一些有关字典的处理技巧:def top_counts(count_dict, n=10): value_key_pairs = [(count, tz) for tz, count in count_dict.items()] value_key_pairs.sort() return value_key_pairs[-n:]top_counts(counts)[(33, 'America/Sao_Paulo'), (35, 'Europe/Madrid'), (36, 'Pacific/Honolulu'), (37, 'Asia/Tokyo'), (74, 'Europe/London'), (191, 'America/Denver'), (382, 'America/Los_Angeles'), (400, 'America/Chicago'), (521, ''), (1251, 'America/New_York')]如果用python标准库里的collections.Counter类,能让这个任务变得更简单from collections import Countercounts = Counter(time_zones)counts.most_common(10)[('America/New_York', 1251), ('', 521), ('America/Chicago', 400), ('America/Los_Angeles', 382), ('America/Denver', 191), ('Europe/London', 74), ('Asia/Tokyo', 37), ('Pacific/Honolulu', 36), ('Europe/Madrid', 35), ('America/Sao_Paulo', 33)]2 Counting Time Zones with pandas(用pandas对时区进行计数)从一组原始记录中创建DataFrame是很简单的,直接把records传递给pandas.DataFrame即可:import pandas as pd import numpy as npframe = pd.DataFrame(records)frame.info()<class 'pandas.core.frame.DataFrame'> RangeIndex: 3560 entries, 0 to 3559 Data columns (total 18 columns): _heartbeat_ 120 non-null float64 a 3440 non-null object al 3094 non-null object c 2919 non-null object cy 2919 non-null object g 3440 non-null object gr 2919 non-null object h 3440 non-null object hc 3440 non-null float64 hh 3440 non-null object kw 93 non-null object l 3440 non-null object ll 2919 non-null object nk 3440 non-null float64 r 3440 non-null object t 3440 non-null float64 tz 3440 non-null object u 3440 non-null object dtypes: float64(4), object(14) memory usage: 500.7+ KBframe['tz'][:10]0 America/New_York 1 America/Denver 2 America/New_York 3 America/Sao_Paulo 4 America/New_York 5 America/New_York 6 Europe/Warsaw 7 8 9 Name: tz, dtype: object这里frame的输出形式是summary view, 主要用于较大的dataframe对象。frame['tz']所返回的Series对象有一个value_counts方法,该方法可以让我们得到想要的信息:tz_counts = frame['tz'].value_counts()tz_counts[:10]America/New_York 1251 521 America/Chicago 400 America/Los_Angeles 382 America/Denver 191 Europe/London 74 Asia/Tokyo 37 Pacific/Honolulu 36 Europe/Madrid 35 America/Sao_Paulo 33 Name: tz, dtype: int64我们能利用matplotlib为这段数据生成一张图片。这里我们先给记录中未知或缺失的时区填上一个替代值。fillna函数可以替代缺失值(NA),而未知值(空字符串)则可以通过布尔型数组索引,加以替换:clean_tz = frame['tz'].fillna('Missing')clean_tz[clean_tz == ''] = 'Unknown'tz_counts = clean_tz.value_counts()tz_counts[:10]America/New_York 1251 Unknown 521 America/Chicago 400 America/Los_Angeles 382 America/Denver 191 Missing 120 Europe/London 74 Asia/Tokyo 37 Pacific/Honolulu 36 Europe/Madrid 35 Name: tz, dtype: int64利用counts对象的plot方法即可得到一张水平条形图:%matplotlib inline tz_counts[:10].plot(kind='barh', rot=0)当然,我们也可以使用之前介绍的seaborn来画一个水平条形图(horizontal bar plot):import seaborn as snssubset = tz_counts[:10] sns.barplot(y=subset.index, x=subset.values)我们还可以对这种数据进行更多的处理。比如a字段含有执行URL操作的浏览器、设备、应用程序的相关信息:frame['a'][1]'GoogleMaps/RochesterNY'frame['a'][50]'Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2'frame['a'][51]'Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; LG-P925/V10e Build/FRG83G) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1'frame['a'][:5]0 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKi... 1 GoogleMaps/RochesterNY 2 Mozilla/4.0 (compatible; MSIE 8.0; Windows NT ... 3 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8)... 4 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKi... Name: a, dtype: object将这些USER_AGENT字符串中的所有信息都解析出来是一件挺郁闷的工作。不过只要掌握了Python内置的字符串函数和正则表达式,就方便了。比如,我们可以把字符串的第一节(与浏览器大致对应)分离出来得到另一份用户行为摘要:results = Series([x.split()[0] for x in frame.a.dropna()])results[:5]0 Mozilla/5.0 1 GoogleMaps/RochesterNY 2 Mozilla/4.0 3 Mozilla/5.0 4 Mozilla/5.0 dtype: objectresults.value_counts()[:8]Mozilla/5.0 2594 Mozilla/4.0 601 GoogleMaps/RochesterNY 121 Opera/9.80 34 TEST_INTERNET_AGENT 24 GoogleProducer 21 Mozilla/6.0 5 BlackBerry8520/5.0.0.681 4 dtype: int64现在,假设我们想按Windows和非Windows用户对时区统计信息进行分解。为了简单期间,我们假定只要agent字符串中含有“windows”就认为该用户是windows用户。由于有的agent缺失,所以先将他们从数据中移除:cframe = frame[frame.a.notnull()] cframe.head()其次根据a值计算出各行是否是windows:cframe['os'] = np.where(cframe['a'].str.contains('Windows'), 'Windows', 'Not Windows')/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/ipykernel/__main__.py:2: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy from ipykernel import kernelapp as appcframe['os'][:5]0 Windows 1 Not Windows 2 Windows 3 Not Windows 4 Windows Name: os, dtype: object接下来就可以根据时区和新得到的操作系统列表对数据进行分组了:by_tz_os = cframe.groupby(['tz', 'os'])by_tz_os.size()tz os Not Windows 245 Windows 276 Africa/Cairo Windows 3 Africa/Casablanca Windows 1 Africa/Ceuta Windows 2 Africa/Johannesburg Windows 1 Africa/Lusaka Windows 1 America/Anchorage Not Windows 4 Windows 1 America/Argentina/Buenos_Aires Not Windows 1 America/Argentina/Cordoba Windows 1 America/Argentina/Mendoza Windows 1 America/Bogota Not Windows 1 Windows 2 America/Caracas Windows 1 America/Chicago Not Windows 115 Windows 285 America/Chihuahua Not Windows 1 Windows 1 America/Costa_Rica Windows 1 America/Denver Not Windows 132 Windows 59 America/Edmonton Not Windows 2 Windows 4 America/Guayaquil Not Windows 2 America/Halifax Not Windows 1 Windows 3 America/Indianapolis Not Windows 8 Windows 12 America/La_Paz Windows 1 ... Europe/Madrid Not Windows 16 Windows 19 Europe/Malta Windows 2 Europe/Moscow Not Windows 1 Windows 9 Europe/Oslo Not Windows 2 Windows 8 Europe/Paris Not Windows 4 Windows 10 Europe/Prague Not Windows 3 Windows 7 Europe/Riga Not Windows 1 Windows 1 Europe/Rome Not Windows 8 Windows 19 Europe/Skopje Windows 1 Europe/Sofia Windows 1 Europe/Stockholm Not Windows 2 Windows 12 Europe/Uzhgorod Windows 1 Europe/Vienna Not Windows 3 Windows 3 Europe/Vilnius Windows 2 Europe/Volgograd Windows 1 Europe/Warsaw Not Windows 1 Windows 15 Europe/Zurich Not Windows 4 Pacific/Auckland Not Windows 3 Windows 8 Pacific/Honolulu Windows 36 Length: 149, dtype: int64上面通过size对分组结果进行计数,类似于value_counts函数,并利用unstack对计数结果进行重塑为一个表格:agg_counts = by_tz_os.size().unstack().fillna(0)agg_counts[:10]最后,我们来选取最常出现的时区。为了达到这个目的,根据agg_counts中的行数构造了一个简洁索引数组:indexer = agg_counts.sum(1).argsort() indexer[:10]tz 24 Africa/Cairo 20 Africa/Casablanca 21 Africa/Ceuta 92 Africa/Johannesburg 87 Africa/Lusaka 53 America/Anchorage 54 America/Argentina/Buenos_Aires 57 America/Argentina/Cordoba 26 America/Argentina/Mendoza 55 dtype: int64indexer = agg_counts.sum(1).argsort() indexer[:10]tz 24 Africa/Cairo 20 Africa/Casablanca 21 Africa/Ceuta 92 Africa/Johannesburg 87 Africa/Lusaka 53 America/Anchorage 54 America/Argentina/Buenos_Aires 57 America/Argentina/Cordoba 26 America/Argentina/Mendoza 55 dtype: int64然后通过take按照这个顺序截取了最后10行:count_subset = agg_counts.take(indexer)[-10:] count_subsetpandas有一个很方便的方法叫nlargest,可以实现相同效果:agg_counts.sum(1).nlargest(10)tz America/New_York 1251.0 521.0 America/Chicago 400.0 America/Los_Angeles 382.0 America/Denver 191.0 Europe/London 74.0 Asia/Tokyo 37.0 Pacific/Honolulu 36.0 Europe/Madrid 35.0 America/Sao_Paulo 33.0 dtype: float64上面的输出结果可以画成条形图;通过给seaborn的barplot函数传递一个参数,来画出堆积条形图(stacked bar plot):# Rearrange the data for plotting count_subset = count_subset.stack() count_subset.head()tz os America/Sao_Paulo Not Windows 13.0 Windows 20.0 Europe/Madrid Not Windows 16.0 Windows 19.0 Pacific/Honolulu Not Windows 0.0 dtype: float64count_subset.name = 'total' count_subset = count_subset.reset_index() count_subset[:10]sns.barplot(x='total', y='tz', hue='os', data=count_subset)由于这张图中不太容易看清楚较小分组中windows用户的相对比例,因此我们可以将各行规范化为“总计为1”并重新绘图:def norm_total(group): group['normed_total'] = group.total / group.total.sum() return group results = count_subset.groupby('tz').apply(norm_total)sns.barplot(x='normed_total', y='tz', hue='os', data=results)我们还可以使用transform和groupby,来更有效率地计算规范化的和:g = count_subset.groupby('tz') results2 = count_subset.total / g.total.transform('sum')译者:下面的内容是不适用seaborn的画图方法,这种画法是2013年第一版中的内容:count_subset = agg_counts.take(indexer)[-10:] count_subset这里也可以生成一张条形图。我们使用stacked=True来生成一张堆积条形图:count_subset.plot(kind='barh', stacked=True)由于这张图中不太容易看清楚较小分组中windows用户的相对比例,因此我们可以将各行规范化为“总计为1”并重新绘图:normed_subset = count_subset.div(count_subset.sum(1), axis=0)normed_subset.plot(kind='barh', stacked=True)
0
0
0
浏览量320
清晨我上码

Date Ranges, Frequencies, and Shifting 日期范围,频度,和位移

11.3 Date Ranges, Frequencies, and Shifting(日期范围,频度,和位移)普通的时间序列通常是不规律的,但我们希望能有一个固定的频度,比如每天,每月,或没15分钟,即使有一些缺失值也没关系。幸运的是,pandas中有一套方法和工具来进行重采样,推断频度,并生成固定频度的日期范围。例如,我们可以把样本时间序列变为固定按日的频度,需要调用resample:import pandas as pd import numpy as np from datetime import datetime dates = [datetime(2011, 1, 2), datetime(2011, 1, 5), datetime(2011, 1, 7), datetime(2011, 1, 8), datetime(2011, 1, 10), datetime(2011, 1, 12)] ts = pd.Series(np.random.randn(6), index=dates) ts2011-01-02 2.005739 2011-01-05 -0.265967 2011-01-07 -0.353966 2011-01-08 -0.646626 2011-01-10 1.599440 2011-01-12 -0.407854 dtype: float64resampler = ts.resample('D')这里的’D’表示按日的频度(daily frequency)。关于频度(frequency)和重采样(resampling)的转换,会在11.6进行具体介绍,这里我们展示一些基本的用法。1 Generating Date Ranges(生成日期范围)之前虽然用过,但没有做解释,其实pandas.date_range是用来生成DatetimeIndex的,使用时要根据频度来指明长度:index = pd.date_range('2012-04-01', '2012-06-01') indexDatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04', '2012-04-05', '2012-04-06', '2012-04-07', '2012-04-08', '2012-04-09', '2012-04-10', '2012-04-11', '2012-04-12', '2012-04-13', '2012-04-14', '2012-04-15', '2012-04-16', '2012-04-17', '2012-04-18', '2012-04-19', '2012-04-20', '2012-04-21', '2012-04-22', '2012-04-23', '2012-04-24', '2012-04-25', '2012-04-26', '2012-04-27', '2012-04-28', '2012-04-29', '2012-04-30', '2012-05-01', '2012-05-02', '2012-05-03', '2012-05-04', '2012-05-05', '2012-05-06', '2012-05-07', '2012-05-08', '2012-05-09', '2012-05-10', '2012-05-11', '2012-05-12', '2012-05-13', '2012-05-14', '2012-05-15', '2012-05-16', '2012-05-17', '2012-05-18', '2012-05-19', '2012-05-20', '2012-05-21', '2012-05-22', '2012-05-23', '2012-05-24', '2012-05-25', '2012-05-26', '2012-05-27', '2012-05-28', '2012-05-29', '2012-05-30', '2012-05-31', '2012-06-01'], dtype='datetime64[ns]', freq='D')默认,date_range会生成按日频度的时间戳。如果我们只传入一个开始或一个结束时间,还必须传入一个数字来表示时期:pd.date_range(start='2012-04-01', periods=20)DatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04', '2012-04-05', '2012-04-06', '2012-04-07', '2012-04-08', '2012-04-09', '2012-04-10', '2012-04-11', '2012-04-12', '2012-04-13', '2012-04-14', '2012-04-15', '2012-04-16', '2012-04-17', '2012-04-18', '2012-04-19', '2012-04-20'], dtype='datetime64[ns]', freq='D')pd.date_range(end='2012-06-01', periods=20)DatetimeIndex(['2012-05-13', '2012-05-14', '2012-05-15', '2012-05-16', '2012-05-17', '2012-05-18', '2012-05-19', '2012-05-20', '2012-05-21', '2012-05-22', '2012-05-23', '2012-05-24', '2012-05-25', '2012-05-26', '2012-05-27', '2012-05-28', '2012-05-29', '2012-05-30', '2012-05-31', '2012-06-01'], dtype='datetime64[ns]', freq='D')开始和结束的日期,严格指定了用于生成日期索引(date index)的边界。例如,如果我们希望日期索引包含每个月的最后一个工作日,我们要设定频度为’BM’(business end of month,每个月的最后一个工作日,更多频度可以看下面的表格),而且只有在这个日期范围内的日期会被包含进去:pd.date_range('2000-01-01', '2000-12-01', freq='BM')DatetimeIndex(['2000-01-31', '2000-02-29', '2000-03-31', '2000-04-28', '2000-05-31', '2000-06-30', '2000-07-31', '2000-08-31', '2000-09-29', '2000-10-31', '2000-11-30'], dtype='datetime64[ns]', freq='BM')date_range会默认保留开始或结束的时间戳:pd.date_range('2012-05-02 12:56:31', periods=5)DatetimeIndex(['2012-05-02 12:56:31', '2012-05-03 12:56:31', '2012-05-04 12:56:31', '2012-05-05 12:56:31', '2012-05-06 12:56:31'], dtype='datetime64[ns]', freq='D')有些时候我们的时间序列数据带有小时,分,秒这样的信息,但我们想要让这些时间戳全部归一化到午夜(normalized to midnight, 即晚上0点),这个时候要用到normalize选项:nor_date = pd.date_range('2012-05-02 12:56:31', periods=5, normalize=True) nor_dateDatetimeIndex(['2012-05-02', '2012-05-03', '2012-05-04', '2012-05-05', '2012-05-06'], dtype='datetime64[ns]', freq='D')nor_date[0]Timestamp('2012-05-02 00:00:00', offset='D')可以看到小时,分,秒全部变为02 Frequencies and Date Offsets(频度和日期偏移)pandas中的频度由一个基本频度(base frequency)和一个乘法器(multiplier)组成。基本频度通常用一个字符串别名(string alias)来代表,比如’M’表示月,'H’表示小时。对每一个基本频度,还有一个被称之为日期偏移(date offset)的对象。例如,小时频度能用Hour类来表示:from pandas.tseries.offsets import Hour, Minutehour = Hour() hour<Hour>通过传入一个整数,我们可以定义一个乘以偏移的乘法(a multiple of an offset):four_hours = Hour(4) four_hours<4 * Hours>在很多情况下,我们不需要创建这些对象,而是使用字符串别名,比如’H’或’4H’。在频度前加一个整数,就能作为一个乘法器:pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4H')DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 04:00:00', '2000-01-01 08:00:00', '2000-01-01 12:00:00', '2000-01-01 16:00:00', '2000-01-01 20:00:00', '2000-01-02 00:00:00', '2000-01-02 04:00:00', '2000-01-02 08:00:00', '2000-01-02 12:00:00', '2000-01-02 16:00:00', '2000-01-02 20:00:00', '2000-01-03 00:00:00', '2000-01-03 04:00:00', '2000-01-03 08:00:00', '2000-01-03 12:00:00', '2000-01-03 16:00:00', '2000-01-03 20:00:00'], dtype='datetime64[ns]', freq='4H')很多偏移(offset)还能和加法结合:Hour(2) + Minute(30)<150 * Minutes>同样的,我们可以传入频度字符串,比如’1h30min’,这种表达也能被解析:pd.date_range('2000-01-01', periods=10, freq='1h30min')DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 01:30:00', '2000-01-01 03:00:00', '2000-01-01 04:30:00', '2000-01-01 06:00:00', '2000-01-01 07:30:00', '2000-01-01 09:00:00', '2000-01-01 10:30:00', '2000-01-01 12:00:00', '2000-01-01 13:30:00'], dtype='datetime64[ns]', freq='90T')Week of month dates(月中的第几周日期)一个有用的类(class)是月中的第几周(Week of month),用WOM表示。丽日我们想得到每个月的第三个星期五:rng = pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI') rngDatetimeIndex(['2012-01-20', '2012-02-17', '2012-03-16', '2012-04-20', '2012-05-18', '2012-06-15', '2012-07-20', '2012-08-17'], dtype='datetime64[ns]', freq='WOM-3FRI')list(rng)[Timestamp('2012-01-20 00:00:00', offset='WOM-3FRI'), Timestamp('2012-02-17 00:00:00', offset='WOM-3FRI'), Timestamp('2012-03-16 00:00:00', offset='WOM-3FRI'), Timestamp('2012-04-20 00:00:00', offset='WOM-3FRI'), Timestamp('2012-05-18 00:00:00', offset='WOM-3FRI'), Timestamp('2012-06-15 00:00:00', offset='WOM-3FRI'), Timestamp('2012-07-20 00:00:00', offset='WOM-3FRI'), Timestamp('2012-08-17 00:00:00', offset='WOM-3FRI')]3 Shifting (Leading and Lagging) Data (偏移(提前与推后)数据)偏移(shifting)表示按照时间把数据向前或向后推移。Series和DataFrame都有一个shift方法实现偏移,索引(index)不会被更改:ts = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2000', periods=4, freq='M')) ts2000-01-31 -0.050276 2000-02-29 0.080201 2000-03-31 1.548324 2000-04-30 0.510664 Freq: M, dtype: float64ts.shift(2)2000-01-31 NaN 2000-02-29 NaN 2000-03-31 -0.050276 2000-04-30 0.080201 Freq: M, dtype: float64ts.shift(-2)2000-01-31 1.548324 2000-02-29 0.510664 2000-03-31 NaN 2000-04-30 NaN Freq: M, dtype: float64当我们进行位移的时候,就像上面这样会引入缺失值。shift的一个普通的用法是计算时间序列的百分比变化,可以表示为:ts / ts.shift(1) - 12000-01-31 NaN 2000-02-29 -2.595227 2000-03-31 18.305554 2000-04-30 -0.670183 Freq: M, dtype: float64因为普通的shift不会对index进行修改,一些数据会被丢弃。因此如果频度是已知的,可以把频度传递给shift,这样的话时间戳会自动变化:ts2000-01-31 -0.050276 2000-02-29 0.080201 2000-03-31 1.548324 2000-04-30 0.510664 Freq: M, dtype: float64ts.shift(2)2000-01-31 NaN 2000-02-29 NaN 2000-03-31 -0.050276 2000-04-30 0.080201 Freq: M, dtype: float64ts.shift(2, freq='M')2000-03-31 -0.050276 2000-04-30 0.080201 2000-05-31 1.548324 2000-06-30 0.510664 Freq: M, dtype: float64其他一些频度也可以导入,能让我们前后移动数据:ts.shift(3, freq='D')2000-02-03 -0.050276 2000-03-03 0.080201 2000-04-03 1.548324 2000-05-03 0.510664 dtype: float64ts.shift(1, freq='90T')2000-01-31 01:30:00 -0.050276 2000-02-29 01:30:00 0.080201 2000-03-31 01:30:00 1.548324 2000-04-30 01:30:00 0.510664 Freq: M, dtype: float64T表示分钟。Shifting dates with offsets(用偏移量来移动日期)pandas的日期偏移(date offset)能被用于datetime或Timestamp对象:from pandas.tseries.offsets import Day, MonthEndnow = datetime(2011, 11, 17)now + 3 * Day()Timestamp('2011-11-20 00:00:00')如果我们添加一个像MonthEnd这样的anchored offset(依附偏移;锚点位置),日期会根据频度规则进行递增:now + MonthEnd()Timestamp('2011-11-30 00:00:00')now + MonthEnd(2)Timestamp('2011-12-31 00:00:00')依附偏移可以让日期向前或向后滚动,利用rollforward和rollback方法:offset = MonthEnd()offset.rollforward(now)Timestamp('2011-11-30 00:00:00')offset.rollback(now)Timestamp('2011-10-31 00:00:00')一个比较创造性的日期偏移(date offset)用法是配合groupby一起用:ts = pd.Series(np.random.randn(20), index=pd.date_range('1/15/2000', periods=20, freq='4d')) ts2000-01-15 0.362927 2000-01-19 -1.107020 2000-01-23 -0.629370 2000-01-27 -0.730651 2000-01-31 0.251607 2000-02-04 0.002611 2000-02-08 -0.049611 2000-02-12 -0.170408 2000-02-16 -1.512385 2000-02-20 1.335117 2000-02-24 -0.393943 2000-02-28 0.087478 2000-03-03 0.441593 2000-03-07 -0.940983 2000-03-11 -1.399163 2000-03-15 0.901478 2000-03-19 0.392408 2000-03-23 -0.512613 2000-03-27 0.026952 2000-03-31 1.200684 Freq: 4D, dtype: float64ts.groupby(offset.rollforward).mean()2000-01-31 -0.370501 2000-02-29 -0.100163 2000-03-31 0.013794 dtype: float64一个简单且快捷的方式是用resample(11.6会进行更详细的介绍):ts.resample('M').mean()2000-01-31 -0.370501 2000-02-29 -0.100163 2000-03-31 0.013794 Freq: M, dtype: float64
0
0
0
浏览量258
清晨我上码

pandas教程:Time Zone Handling 时区处理

11.4 Time Zone Handling(时区处理)格林威治标准时间GMT十七世纪,格林威治皇家天文台为了海上霸权的扩张计画而进行天体观测。1675年旧皇家观测所(Old Royal Observatory) 正式成立,到了1884年决定以通过格林威治的子午线作为划分地球东西两半球的经度零度。观测所门口墙上有一个标志24小时的时钟,显示当下的时间,对全球而言,这里所设定的时间是世界时间参考点,全球都以格林威治的时间作为标准来设定时间,这就是我们耳熟能详的「格林威治标准时间」(Greenwich Mean Time,简称G.M.T.)的由来,标示在手表上,则代表此表具有两地时间功能,也就是同时可以显示原居地和另一个国度的时间。世界协调时间UTC多数的两地时间表都以GMT来表示,但也有些两地时间表上看不到GMT字样,出现的反而是UTC这3个英文字母,究竟何谓UTC?事实上,UTC指的是Coordinated Universal Time- 世界协调时间(又称世界标准时间、世界统一时间),是经过平均太阳时(以格林威治时间GMT为准)、地轴运动修正后的新时标以及以「秒」为单位的国际原子时所综合精算而成的时间,计算过程相当严谨精密,因此若以「世界标准时间」的角度来说,UTC比GMT来得更加精准。其误差值必须保持在0.9秒以内,若大于0.9秒则由位于巴黎的国际地球自转事务中央局发布闰秒,使UTC与地球自转周期一致。所以基本上UTC的本质强调的是比GMT更为精确的世界时间标准,不过对于现行表款来说,GMT与UTC的功能与精确度是没有差别的。时区可以理解为UTC的偏移(offset),例如,在夏令时,纽约时间落后于UTC时间四个小时,而在一年的其他时间里,纽约时间落后于UTC时间五个小时。在python中,时区信息来自第三方的pytz库,这个库利用的是奥尔森数据库,这个数据库汇集了世界时区信息。这个信息对于历史数据很重要,因为夏令时(daylight saving time,DST)的交接日(transition date)取决于当地政府的心血来潮。在美国,自1900年后,夏令时的交接日已经被改了很多次。关于pytz库的更多信息,需要查看相关的文档。本书中pandas包含了一些pytz的功能,除了时区的名字,其他的API都不用去查。时区名字可以通过下面的方法获得:import pytzpytz.common_timezones[-5:]['US/Eastern', 'US/Hawaii', 'US/Mountain', 'US/Pacific', 'UTC']想要从pytz中得到一个时区对象(time zone object),使用pytz.timezone:tz = pytz.timezone('America/New_York') tz<DstTzInfo 'America/New_York' LMT-1 day, 19:04:00 STD>1 Time Zone Localization and Conversion(时区定位和转换)默认的,pandas中的时间序列是time zone naive(朴素时区)。例如,考虑下面的时间序列:import pandas as pd import numpy as nprng = pd.date_range('3/9/2012 9:30', periods=6, freq='D')ts = pd.Series(np.random.randn(len(rng)), index=rng) ts2012-03-09 09:30:00 1.001642 2012-03-10 09:30:00 -1.277818 2012-03-11 09:30:00 0.481214 2012-03-12 09:30:00 0.738525 2012-03-13 09:30:00 0.396482 2012-03-14 09:30:00 -0.269782 Freq: D, dtype: float64索引的tz部分是None:print(ts.index.tz)None日期范围也能通过时区集合(time zone set)来创建:pd.date_range('3/9/2012 9:30', periods=10, freq='D', tz='UTC')DatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00', '2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00', '2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00', '2012-03-15 09:30:00+00:00', '2012-03-16 09:30:00+00:00', '2012-03-17 09:30:00+00:00', '2012-03-18 09:30:00+00:00'], dtype='datetime64[ns, UTC]', freq='D')使用tz_localize方法,可以实现从朴素到本地化(naive to localized)的转变:ts2012-03-09 09:30:00 1.001642 2012-03-10 09:30:00 -1.277818 2012-03-11 09:30:00 0.481214 2012-03-12 09:30:00 0.738525 2012-03-13 09:30:00 0.396482 2012-03-14 09:30:00 -0.269782 Freq: D, dtype: float64ts_utc = ts.tz_localize('UTC') ts_utc2012-03-09 09:30:00+00:00 1.001642 2012-03-10 09:30:00+00:00 -1.277818 2012-03-11 09:30:00+00:00 0.481214 2012-03-12 09:30:00+00:00 0.738525 2012-03-13 09:30:00+00:00 0.396482 2012-03-14 09:30:00+00:00 -0.269782 Freq: D, dtype: float64ts_utc.indexDatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00', '2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00', '2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00'], dtype='datetime64[ns, UTC]', freq='D')一旦时间序列被定位到某个时区,那么它就可以被转换为任何其他时区,使用tz_convert:ts_utc.tz_convert('America/New_York')2012-03-09 04:30:00-05:00 1.001642 2012-03-10 04:30:00-05:00 -1.277818 2012-03-11 05:30:00-04:00 0.481214 2012-03-12 05:30:00-04:00 0.738525 2012-03-13 05:30:00-04:00 0.396482 2012-03-14 05:30:00-04:00 -0.269782 Freq: D, dtype: float64在处理时间序列的时候,我们可以先把时间定位到纽约时间,然后转换到柏林时间:ts_eastern = ts.tz_localize('America/New_York')ts_eastern.tz_convert('UTC')2012-03-09 14:30:00+00:00 1.001642 2012-03-10 14:30:00+00:00 -1.277818 2012-03-11 13:30:00+00:00 0.481214 2012-03-12 13:30:00+00:00 0.738525 2012-03-13 13:30:00+00:00 0.396482 2012-03-14 13:30:00+00:00 -0.269782 Freq: D, dtype: float64ts_eastern.tz_convert('Europe/Berlin')2012-03-09 15:30:00+01:00 1.001642 2012-03-10 15:30:00+01:00 -1.277818 2012-03-11 14:30:00+01:00 0.481214 2012-03-12 14:30:00+01:00 0.738525 2012-03-13 14:30:00+01:00 0.396482 2012-03-14 14:30:00+01:00 -0.269782 Freq: D, dtype: float64tz_localize和tz_convert也是DatetimeIndex上的实例方法(instance methods):ts.index.tz_localize('Asia/Shanghai')DatetimeIndex(['2012-03-09 09:30:00+08:00', '2012-03-10 09:30:00+08:00', '2012-03-11 09:30:00+08:00', '2012-03-12 09:30:00+08:00', '2012-03-13 09:30:00+08:00', '2012-03-14 09:30:00+08:00'], dtype='datetime64[ns, Asia/Shanghai]', freq='D')讲朴素的时间戳进行本地化,还会检查夏令时转换期附近是否有模糊的或不存在的时间。2 Operations with Time Zone−Aware Timestamp Objects(时区的操作-意识到时间戳对象)和时间序列或日期范围(date ranges)相似,单独的Timestamp object(时间戳对象)也能从朴素(即无时区)本地化为有时区的日期,然后就可以转换为其他时区了:stamp = pd.Timestamp('2011-03-12 04:00')stamp_utc = stamp.tz_localize('utc')stamp_utc.tz_convert('America/New_York')Timestamp('2011-03-11 23:00:00-0500', tz='America/New_York')在创建Timestamp的时候,我们可以传递一个时区:stamp_moscow = pd.Timestamp('2011-03-12 04:00', tz='Europe/Moscow') stamp_moscowTimestamp('2011-03-12 04:00:00+0300', tz='Europe/Moscow')有时区的Timestamp对象内部存储了一个UTC时间戳,这个值是从Unix纪元(即1907年1月1日)到现在的纳秒;这个UTC值在即使换了不同的时区,也是不变的:stamp_utc.value1299902400000000000stamp_utc.tz_convert('America/New_York').value 1299902400000000000在使用pandas的DateOffset对象进行算数运算的时候,如果夏令时存在,pandas也会考虑进去。这里我们构建一个时间戳,正好出现在夏令时转换前。首先,在变为夏令时的前30分钟:from pandas.tseries.offsets import Hourstamp = pd.Timestamp('2012-03-12 01:30', tz='US/Eastern') stampTimestamp('2012-03-12 01:30:00-0400', tz='US/Eastern')stamp + Hour()Timestamp('2012-03-12 02:30:00-0400', tz='US/Eastern')变为夏令时的90分钟前:stamp = pd.Timestamp('2012-11-04 00:30', tz='US/Eastern') stampTimestamp('2012-11-04 00:30:00-0400', tz='US/Eastern')stamp + 2 * Hour()Timestamp('2012-11-04 01:30:00-0500', tz='US/Eastern')3 Operations Between Diferent Time Zones(不同时区间的运算)如果两个不同时区的时间序列被合并,那么结果为UTC。因为时间戳是以UTC为背后机制的,这种变化是直接的,不需要手动转换:rng = pd.date_range('3/7/2012 9:30', periods=10, freq='B')ts = pd.Series(np.random.randn(len(rng)), index=rng) ts2012-03-07 09:30:00 0.857427 2012-03-08 09:30:00 -0.985773 2012-03-09 09:30:00 -0.037836 2012-03-12 09:30:00 -1.561366 2012-03-13 09:30:00 0.195092 2012-03-14 09:30:00 -0.182154 2012-03-15 09:30:00 0.629671 2012-03-16 09:30:00 -1.351815 2012-03-19 09:30:00 -1.054486 2012-03-20 09:30:00 0.072799 Freq: B, dtype: float64ts1 = ts[:7].tz_localize('Europe/London') ts2 = ts1[2:].tz_convert('Europe/Moscow') result = ts1 + ts2result.indexDatetimeIndex(['2012-03-07 09:30:00+00:00', '2012-03-08 09:30:00+00:00', '2012-03-09 09:30:00+00:00', '2012-03-12 09:30:00+00:00', '2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00', '2012-03-15 09:30:00+00:00'], dtype='datetime64[ns, UTC]', freq='B')
0
0
0
浏览量1528
清晨我上码

python将dataframe数据导入MongoDB非关系型数据库

pymongo连接import pymongo client = pymongo.MongoClient("mongodb://localhost:27017/") dblist = client.list_database_names() for db in dblist: print(db) #查看已有数据库admin bilibili config local student新建数据库和集合import pandas as pd db=client['government'] col=db['policy']pandas导入数据使用df.to_dict函数,返回结果为列表,列表中的每个元素为json型,是原来excel中的一条记录。df = pd.read_excel('汽车行业政策文本研究.xlsx') print(df.columns) # orient='records', 表示将DataFrame的数据转换成我想要的json格式 data_json = df.to_dict(orient='records') print(type(data_json))插入数据col.insert_many(data_json) #一次性插入多条 #for dt in data_json: #一次一条 #col.insert_one(dt)数据查看
0
0
0
浏览量424
清晨我上码

pandas-profiling / ydata-profiling介绍与使用教程

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
0
0
浏览量2018
清晨我上码

pandas教程:Categorical Data 类别数据

12.1 Categorical Data(类别数据)这一届会介绍pandas的Categorical类型。1 Background and Motivation(背景和动力)表格中的列可能会有重复的部分。我们可以用unique和value_counts,从一个数组从提取不同的值,并计算频度:import numpy as np import pandas as pdvalues = pd.Series(['apple', 'orange', 'apple', 'apple'] * 2) values0 apple 1 orange 2 apple 3 apple 4 apple 5 orange 6 apple 7 apple dtype: objectpd.unique(values)array(['apple', 'orange'], dtype=object)pd.value_counts(values)apple 6 orange 2 dtype: int64对于不同的类型数据值,一个更好的方法是用维度表(dimension table)来表示,然后用整数键(integer keys)来指代维度表:values = pd.Series([0, 1, 0, 0] * 2) values0 0 1 1 2 0 3 0 4 0 5 1 6 0 7 0 dtype: int64dim = pd.Series(['apple', 'orange']) dim0 apple 1 orange dtype: object用take方法来重新存储原始的,由字符串构成的Series:dim.take(values)0 apple 1 orange 0 apple 0 apple 0 apple 1 orange 0 apple 0 apple dtype: object这种用整数表示的方法叫做类别(categorical)或字典编码(dictionary-encoded)表示法。表示不同类别值的数组,被称作类别,字典,或层级。本书中我们将使用类别(categorical and categories)来称呼。表示类别的整数值被叫做,类别编码(category code),或编码(code)。2 Categorical Type in pandas(pandas中的Categorical类型)pandas中有一个Categorical类型,是用来保存那些基于整数的类别型数据。考虑下面的例子:fruits = ['apple', 'orange', 'apple', 'apple'] * 2N = len(fruits)df = pd.DataFrame({'fruit': fruits, 'basket_id': np.arange(N), 'count': np.random.randint(3, 15, size=N), 'weight': np.random.uniform(0, 4, size=N)}, columns=['basket_id', 'fruit', 'count', 'weight']) df这里,df['fruit']是一个python的字符串对象。我们将其转换为类型对象:fruits_cat = df['fruit'].astype('category') fruits_cat0 apple 1 orange 2 apple 3 apple 4 apple 5 orange 6 apple 7 apple Name: fruit, dtype: category Categories (2, object): [apple, orange]fruits_cat的值并不是一个numpy数组,而是一个pandas.Categorical实例:c = fruits_cat.values type(c)pandas.core.categorical.Categorical这个Categorical对象有categories和codes属性:c.categoriesIndex(['apple', 'orange'], dtype='object')c.codesarray([0, 1, 0, 0, 0, 1, 0, 0], dtype=int8)可以把转换的结果变为DataFrame列:df['fruit'] = df['fruit'].astype('category') df.fruit0 apple 1 orange 2 apple 3 apple 4 apple 5 orange 6 apple 7 apple Name: fruit, dtype: category Categories (2, object): [apple, orange]也可以直接把其他的python序列变为pandas.Categorical类型:my_categories = pd.Categorical(['foo', 'bar', 'baz', 'foo', 'bar']) my_categories[foo, bar, baz, foo, bar] Categories (3, object): [bar, baz, foo]如果已经得到了分类编码数据(categorical encoded data),我们可以使用from_codes构造器:categories = ['foo', 'bar', 'baz']codes = [0, 1, 2, 0, 0, 1]my_cats_2 = pd.Categorical.from_codes(codes, categories) my_cats_2[foo, bar, baz, foo, foo, bar] Categories (3, object): [foo, bar, baz]除非明确指定,非常默认类别没有特定的顺序。所以,取决于输入的数据,categories数组可能有不同的顺序。当使用from_codes或其他一些构造器的时候,我们可以指定类别的顺序:ordered_cat = pd.Categorical.from_codes(codes, categories, ordered=True) ordered_cat[foo, bar, baz, foo, foo, bar] Categories (3, object): [foo < bar < baz]输出的结果中,[foo < bar < baz]表示foo在bar之间,以此类推。一个没有顺序的类型实例(unordered categorical instance)可以通过as_ordered来排序:my_cats_2.as_ordered()[foo, bar, baz, foo, foo, bar] Categories (3, object): [foo < bar < baz]最后一点需要注意的,类型数据没必要一定是字符串,它可以是任何不可变的值类型(any immutable value types)。3 Computations with Categoricals(类型计算)Categorical类型和其他类型差不多,不过对于某些函数,比如groupby函数,在Categorical数据上会有更好的效果。很多函数可以利用ordered标记。假设有一些随机的数字,用pandas.quct进行分箱(binning)。得到的类型是pandas.Categorical;虽然之前用到过pandas.cut,但是没有具体介绍里面的细节:np.random.seed(12345)draws = np.random.randn(1000)draws[:5]array([-0.20470766, 0.47894334, -0.51943872, -0.5557303 , 1.96578057])计算分箱后的分位数:bins = pd.qcut(draws, 4) bins[(-0.684, -0.0101], (-0.0101, 0.63], (-0.684, -0.0101], (-0.684, -0.0101], (0.63, 3.928], ..., (-0.0101, 0.63], (-0.684, -0.0101], (-2.95, -0.684], (-0.0101, 0.63], (0.63, 3.928]] Length: 1000 Categories (4, interval[float64]): [(-2.95, -0.684] < (-0.684, -0.0101] < (-0.0101, 0.63] < (0.63, 3.928]]具体分位数并不如季度的名字直观,我们直接在qcut中设定labels:bins = pd.qcut(draws, 4, labels=['Q1', 'Q2', 'Q3', 'Q4']) bins[Q2, Q3, Q2, Q2, Q4, ..., Q3, Q2, Q1, Q3, Q4] Length: 1000 Categories (4, object): [Q1 < Q2 < Q3 < Q4]bins.codes[:10]array([1, 2, 1, 1, 3, 3, 2, 2, 3, 3], dtype=int8)bins caetegorical并没有包含边界,我们可以用groupby来提取:bins = pd.Series(bins, name='quartile')results = (pd.Series(draws) .groupby(bins) .agg(['count', 'min', 'max']) .reset_index()) resultsquartile列包含了原始的类别信息,包含bins中的顺序:results['quartile']0 Q1 1 Q2 2 Q3 3 Q4 Name: quartile, dtype: category Categories (4, object): [Q1 < Q2 < Q3 < Q4]Better performance with categoricals (使用categoricals得到更好的效果)使用categorical能让效果提高。如果一个DataFrame的列是categorical类型,使用的时候会减少很多内存的使用。假设我们有一个一千万的元素和一个类别:N = 10000000 draws = pd.Series(np.random.randn(N)) labels = pd.Series(['foo', 'bar', 'baz', 'qux'] * (N // 4))把labels变为categorical:categories = labels.astype('category')可以看到labels会比categories使用更多的内存:labels.memory_usage()80000080categories.memory_usage()10000272当然,转换成category也是要消耗计算的,不过这种消耗是一次性的:%time _ = labels.astype('category')CPU times: user 303 ms, sys: 70.1 ms, total: 373 ms Wall time: 385 ms在categories上使用groupby会非常快,因为用的是基于整数的编码,而不是由字符串组成的数组。4 Categorical Methods(类别方法)如果是包含categorical数据的Series数据,有和Series.str类似的一些比较特殊的方法。对于访问categories和code很方便:s = pd.Series(['a', 'b', 'c', 'd'] * 2)cat_s = s.astype('category') cat_s0 a 1 b 2 c 3 d 4 a 5 b 6 c 7 d dtype: category Categories (4, object): [a, b, c, d]属性cat可以访问categorical方法:cat_s.cat.codes0 0 1 1 2 2 3 3 4 0 5 1 6 2 7 3 dtype: int8cat_s.cat.categoriesIndex(['a', 'b', 'c', 'd'], dtype='object')假设我们知道实际的类别超过了当前观测到的四个类别,那么我们可以使用set_categories方法来扩展:actual_categories = ['a', 'b', 'c', 'd', 'e'] cat_s2 = cat_s.cat.set_categories(actual_categories) cat_s20 a 1 b 2 c 3 d 4 a 5 b 6 c 7 d dtype: category Categories (5, object): [a, b, c, d, e]数据本身似乎没有改变,不过在对其进行操作的时候会反应出来。例如,value_counts:cat_s.value_counts()d 2 c 2 b 2 a 2 dtype: int64cat_s2.value_counts()d 2 c 2 b 2 a 2 e 0 dtype: int64在大型数据集,categoricals经常用来作为省内存和提高效果的工具。在对一个很大的DataFrame或Series进行过滤后,很多类型可能不会出现在数据中。我们用remove_unused_categories方法来除去没有观测到的类别:cat_s3 = cat_s[cat_s.isin(['a', 'b'])] cat_s30 a 1 b 4 a 5 b dtype: category Categories (4, object): [a, b, c, d]cat_s3.cat.remove_unused_categories()0 a 1 b 4 a 5 b dtype: category Categories (2, object): [a, b]Creating dummy variables for modeling(为建模创建哑变量)在使用机器学习的一些工具时,经常要转变类型数据为哑变量(dummy variables ),也被称作是独热编码(one-hot encoding)。即在DataFrame中,给一列中不同的类别创建不同的列,用1表示出现,用0表示未出现。例子:cat_s = pd.Series(['a', 'b', 'c', 'd'] * 2, dtype='category')在第七章也介绍过,pandas.get_dummies函数会把一维的类型数据变为包含哑变量的DataFrame:pd.get_dummies(cat_s)
0
0
0
浏览量725
清晨我上码

pandas教程:Periods and Period Arithmetic 周期和周期运算

11.5 Periods and Period Arithmetic(周期和周期运算)Periods(周期)表示时间跨度(timespans),比如天,月,季,年。Period类表示的就是这种数据类型,构建的时候需要用字符串或整数,以及一个频度(关于频度的代码可以看11.4中的表格):import numpy as np import pandas as pdp = pd.Period(2007, freq='A-DEC') pPeriod('2007', 'A-DEC')在这个例子里,Period对象代表了整个2007年一年的时间跨度,从1月1日到12月31日。在Period对象上进行加减,会有和对频度进行位移(shifting)一样的效果:p + 5Period('2012', 'A-DEC')p - 2Period('2005', 'A-DEC')如果两个周期有相同的频度,二者的区别就是它们之间有多少个单元(units):pd.Period('2014', freq='A-DEC') - p7固定范围的周期(Regular ranges of periods)可以通过period_range函数创建:rng = pd.period_range('2000-01-01', '2000-06-03', freq='M') rngPeriodIndex(['2000-01', '2000-02', '2000-03', '2000-04', '2000-05', '2000-06'], dtype='int64', freq='M')PeriodIndex类能存储周期组成的序列,而且可以作为任何pandas数据结构中的轴索引(axis index):pd.Series(np.random.randn(6), index=rng)2000-01 0.439035 2000-02 -0.231125 2000-03 -1.085106 2000-04 -1.909902 2000-05 1.478810 2000-06 0.656713 Freq: M, dtype: float64如果我们有字符串组成的数组,可以使用PeriodIndex类:values = ['2001Q3', '2002Q2', '2003Q1']index = pd.PeriodIndex(values, freq='Q-DEC') indexPeriodIndex(['2001Q3', '2002Q2', '2003Q1'], dtype='int64', freq='Q-DEC')1 Period Frequency Conversion(周期频度转换)通过使用asfreq方法,Periods和PeriodIndex对象能被转换为其他频度。例如,假设我们有一个年度期间(annual period),并且想要转换为月度期间(monthly period),做法非常直观:p = pd.Period('2007', freq='A-DEC') pPeriod('2007', 'A-DEC')p.asfreq('M', how='start')Period('2007-01', 'M')p.asfreq('M', how='end')Period('2007-12', 'M')我们可以认为Period('2007', freq='A-DEC')是某种指向时间跨度的光标,而这个时间跨度被细分为月度期间。可以看下面的图示:如果一个财政年度(fiscal year)是在1月结束,而不是12月,那么对应的月度期间会不一样:p = pd.Period('2007', freq='A-JUN') pPeriod('2007', 'A-JUN')p.asfreq('M', 'start')Period('2006-07', 'M')p.asfreq('M', 'end')Period('2007-06', 'M')当我们转换高频度为低频度时,pandas会根据 subperiod(次周期;子周期)的归属来决定superperiod(超周期;母周期)。例如,在A-JUN频度中,月份Aug-2007其实是个2008周期的一部分:p = pd.Period('Aug-2007', 'M')p.asfreq('A-JUN')Period('2008', 'A-JUN')整个PeriodIndex对象或时间序列可以被转换为一样的语义(semantics):rng = pd.period_range('2006', '2009', freq='A-DEC')ts = pd.Series(np.random.randn(len(rng)), index=rng) ts2006 0.391629 2007 0.497413 2008 -1.685639 2009 0.939885 Freq: A-DEC, dtype: float64ts.asfreq('M', how='start')2006-01 0.391629 2007-01 0.497413 2008-01 -1.685639 2009-01 0.939885 Freq: M, dtype: float64这里,年度周期可以用月度周期替换,对应的第一个月也会包含在每个年度周期里。如果我们想要每年的最后一个工作日的话,可以使用'B'频度,并指明我们想要周期的结尾:ts.asfreq('B', how='end')2006-12-29 0.391629 2007-12-31 0.497413 2008-12-31 -1.685639 2009-12-31 0.939885 Freq: B, dtype: float642 Quarterly Period Frequencies(季度周期频度)季度数据经常出现在会计,经济等领域。大部分季度数据都与财政年度结束日(fiscal year end)相关,比如12月最后一个工作日。因此,根据财政年度结束的不同,周期2012Q4也有不同的意义。pandas支持所有12个周期频度,从Q-JAN到Q-DEC:p = pd.Period('2012Q4', freq='Q-JAN') pPeriod('2012Q4', 'Q-JAN')如果是财政年度结束日在一月份,那么2012Q4代表从11月到1月,可以用日频度查看。p.asfreq('D', 'start')Period('2011-11-01', 'D')p.asfreq('D', 'end')Period('2012-01-31', 'D')因此,做些简单的周期运算也是可能的,例如,获得每个季度的,第二个到最后一个工作日的,下午4点的时间戳:p4pm = (p.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60 p4pmPeriod('2012-01-30 16:00', 'T')p4pm.to_timestamp()Timestamp('2012-01-30 16:00:00')还可以用period_range产生季度范围数据。运算方法也一样:rng = pd.period_range('2011Q3', '2012Q4', freq='Q-JAN')ts = pd.Series(np.arange(len(rng)), index=rng) ts2011Q3 0 2011Q4 1 2012Q1 2 2012Q2 3 2012Q3 4 2012Q4 5 Freq: Q-JAN, dtype: int64new_rng = (rng.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60ts.index = new_rng.to_timestamp() ts2010-10-28 16:00:00 0 2011-01-28 16:00:00 1 2011-04-28 16:00:00 2 2011-07-28 16:00:00 3 2011-10-28 16:00:00 4 2012-01-30 16:00:00 5 dtype: int643 Converting Timestamps to Periods (and Back)(时间戳与周期相互转换)用时间戳作为索引的Series和DataFrame对象,可以用to_period方法转变为周期:rng = pd.date_range('2000-01-01', periods=3, freq='M')ts = pd.Series(np.random.randn(3), index=rng) ts2000-01-31 1.556049 2000-02-29 -0.708661 2000-03-31 -0.154767 Freq: M, dtype: float64pts = ts.to_period() pts2000-01 1.556049 2000-02 -0.708661 2000-03 -0.154767 Freq: M, dtype: float64因为周期是不重复的时间跨度(non-overlapping timespans),一个时间戳只能属于一个有指定频度的单独周期。尽管默认情况下新的PeriodIndex的频度会从时间戳中来推测,但我们也可以自己设定想要的频度。结果中有重复的周期也没有关系:rng = pd.date_range('1/29/2000', periods=6, freq='D') rngDatetimeIndex(['2000-01-29', '2000-01-30', '2000-01-31', '2000-02-01', '2000-02-02', '2000-02-03'], dtype='datetime64[ns]', freq='D')ts2 = pd.Series(np.random.randn(6), index=rng) ts22000-01-29 1.115254 2000-01-30 -1.813124 2000-01-31 0.970670 2000-02-01 1.306337 2000-02-02 0.673274 2000-02-03 -0.105436 Freq: D, dtype: float64ts2.to_period('M')2000-01 1.115254 2000-01 -1.813124 2000-01 0.970670 2000-02 1.306337 2000-02 0.673274 2000-02 -0.105436 Freq: M, dtype: float64想转换回时间戳的话,使用to_timestamp:pts = ts2.to_period() pts2000-01-29 1.115254 2000-01-30 -1.813124 2000-01-31 0.970670 2000-02-01 1.306337 2000-02-02 0.673274 2000-02-03 -0.105436 Freq: D, dtype: float64pts.to_timestamp(how='end')2000-01-29 1.115254 2000-01-30 -1.813124 2000-01-31 0.970670 2000-02-01 1.306337 2000-02-02 0.673274 2000-02-03 -0.105436 Freq: D, dtype: float644 Creating a PeriodIndex from Arrays(从数组中创建一个周期索引)有固定频度的数据集,有时会在很多列上存储时间跨度信息。例如,在下面的宏观经济数据及上,年度和季度在不同的列:data = pd.read_csv('../examples/macrodata.csv') data.head()data.year[:5]1959Q1    1959.01959Q2 1959.0 1959Q3 1959.0 1959Q4 1959.0 1960Q1 1960.0 Freq: Q-DEC, Name: year, dtype: float64data.quarter[:5]1959Q1 1.0 1959Q2 2.0 1959Q3 3.0 1959Q4 4.0 1960Q1 1.0 Freq: Q-DEC, Name: quarter, dtype: float64通过把这些数组传递给PeriodIndex,并指定频度,我们可以把这些合并得到一个新的DataFrame:index = pd.PeriodIndex(year=data.year, quarter=data.quarter, freq='Q-DEC') indexPeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2', '1960Q3', '1960Q4', '1961Q1', '1961Q2', ... '2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3', '2008Q4', '2009Q1', '2009Q2', '2009Q3'], dtype='int64', length=203, freq='Q-DEC')data.index = indexdata.head()data.infl[:5]1959Q1 0.00 1959Q2 2.34 1959Q3 2.74 1959Q4 0.27 1960Q1 2.31 Freq: Q-DEC, Name: infl, dtype: float64
0
0
0
浏览量112
清晨我上码

pandas教程:Data Aggregation 数据聚合

10.2 Data Aggregation(数据聚合)聚合(Aggregation)指的是一些数据转化(data transformation),这些数据转化能从数组中产生标量(scalar values)。下面的例子就是一些聚合方法,包括mean, count, min and sum。我们可能会好奇,在一个GroupBy对象上调用mean()的时候,究竟发生了什么。一些常见的聚合,比如下表,实现方法上都已经被优化过了。当然,我们可以使用的聚合方法不止这些:我们可以使用自己设计的聚合方法,而且可以调用分组后对象上的任意方法。例如,我们可以调用quantile来计算Series或DataFrame中列的样本的百分数。尽管quantile并不是专门为GroupBy对象设计的方法,这是一个Series方法,但仍可以被GroupBy对象使用。GroupBy会对Series进行切片(slice up),并对于切片后的每一部分调用piece.quantile(0.9),然后把每部分的结果整合到一起:import numpy as np import pandas as pddf = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'], 'key2' : ['one', 'two', 'one', 'two', 'one'], 'data1' : np.random.randn(5), 'data2' : np.random.randn(5)}) dfgrouped = df.groupby('key1') for key, group in grouped: print(key) print(group)a data1 data2 key1 key2 0 1.707738 0.186729 a one 1 1.069831 1.305796 a two 4 0.341176 0.429461 a one b data1 data2 key1 key2 2 -2.291339 -1.609071 b one 3 1.348090 -0.294999 b twogrouped['data1'].quantile(0.9)key1 a 1.580157 b 0.984147 Name: data1, dtype: float64如果想用自己设计的聚合函数,把用于聚合数组的函数传入到aggregate或agg方法即可:def peak_to_peak(arr): return arr.max() - arr.min()grouped.agg(peak_to_peak)我们发现很多方法,比如describe,也能正常使用,尽管严格的来说,这并不是聚合:grouped.describe()细节的部分在10.3会进行更多解释。注意:自定义的函数会比上面表中的函数慢一些,上面的函数时优化过的,而自定义的函数会有一些额外的计算,所以慢一些。1 Column-Wise and Multiple Function Application(列对列和多函数应用)让我们回到tipping数据集。加载数据及后,我们添加一列用于描述小费的百分比:tips = pd.read_csv('../examples/tips.csv')# Add tip percentage of total bill tips['tip_pct'] = tips['tip'] / tips['total_bill']tips[:6]我们可以看到,对series或DataFrame进行聚合,其实就是通过aggregate使用合适的函数,或者调用一些像mean或std这样的方法。然而,我们可能想要在列上使用不同的函数进行聚合,又或者想要一次执行多个函数。幸运的是,这是可能的,下面将通过一些例子来说明。首先,对于tips数据集,先用day和smoker进行分组:grouped = tips.groupby(['day', 'smoker'])对于像是上面表格10-1中的一些描述性统计,我们可以直接传入函数的名字,即字符串:grouped_pct = grouped['tip_pct']for name, group in grouped_pct: print(name) print(group[:2], '\n')('Fri', 'No') 91 0.155625 94 0.142857 Name: tip_pct, dtype: float64 ('Fri', 'Yes') 90 0.103555 92 0.173913 Name: tip_pct, dtype: float64 ('Sat', 'No') 19 0.162228 20 0.227679 Name: tip_pct, dtype: float64 ('Sat', 'Yes') 56 0.078927 58 0.156584 Name: tip_pct, dtype: float64 ('Sun', 'No') 0 0.059447 1 0.160542 Name: tip_pct, dtype: float64 ('Sun', 'Yes') 164 0.171331 172 0.710345 Name: tip_pct, dtype: float64 ('Thur', 'No') 77 0.147059 78 0.131810 Name: tip_pct, dtype: float64 ('Thur', 'Yes') 80 0.154321 83 0.152999 Name: tip_pct, dtype: float64 grouped_pct.agg('mean')day smoker Fri No 0.151650 Yes 0.174783 Sat No 0.158048 Yes 0.147906 Sun No 0.160113 Yes 0.187250 Thur No 0.160298 Yes 0.163863 Name: tip_pct, dtype: float64如果我们把函数或函数的名字作为一个list传入,我们会得到一个DataFrame,每列的名字就是函数的名字:# def peak_to_peak(arr): # return arr.max() - arr.min() grouped_pct.agg(['mean', 'std', peak_to_peak])上面我们把多个聚合函数作为一个list传入给agg,这些函数会独立对每一个组进行计算。上面结果的列名是自动给出的,当然,我们也可以更改这些列名。这种情况下,传入一个由tuple组成的list,每个tuple的格式是(name, function),每个元组的第一个元素会被用于作为DataFrame的列名(我们可以认为这个二元元组list是一个有序的映射):grouped_pct.agg([('foo', 'mean'), ('bar', np.std)])如果是处理一个DataFrame,我们有更多的选择,我们可以用一个含有多个函数的list应用到所有的列上,也可以在不同的列上应用不同的函数。演示一下,假设我们想要在tip_pct和total_bill这两列上,计算三个相同的统计指标:functions = ['count', 'mean', 'max']result = grouped['tip_pct', 'total_bill'].agg(functions) result我们可以看到,结果中的DataFrame有多层级的列(hierarchical columns)。另外一种做法有相同的效果,即我们对于每一列单独进行聚合(aggregating each column separately),然后使用concat把结果都结合在一起,然后用列名作为keys参数:result['tip_pct']我们之前提到过,可以用元组组成的list来自己定义列名:ftuples = [('Durchschnitt', 'mean'), ('Abweichung', np.var)]grouped['tip_pct', 'total_bill'].agg(ftuples)现在,假设我们想要把不同的函数用到一列或多列上。要做到这一点,给agg传递一个dict,这个dict需要包含映射关系,用来表示列名和函数之间的对应关系:grouped.agg({'tip': np.max, 'size': 'sum'})grouped.agg({'tip_pct': ['min', 'max', 'mean', 'std'], 'size': 'sum'})只有当多个函数用于至少一列的时候,DataFrame才会有多层级列(hierarchical columns)2 Returning Aggregated Data Without Row Indexes(不使用行索引返回聚合数据)目前为止提到的所有例子,最后返回的聚合数据都是有索引的,而且这个索引默认是多层级索引,这个索引是由不同的组键的组合构成的(unique group key combinations)。因为我们并不是总需要返回这种索引,所以我们可以取消这种模式,在调用groupby的时候设定as_index=False即可:tips.groupby(['day', 'smoker'], as_index=False).mean()当然,我们也可以在上面的结果上直接调用reset_index,这样的话就能得到之前那种多层级索引的结果。不过使用as_index=False方法可以避免一些不必要的计算。
0
0
0
浏览量71
清晨我上码

pandas教程:Summarizing and Computing Descriptive Sta

5.3 Summarizing and Computing Descriptive Statistics(汇总和描述性统计)pandas有很多数学和统计方法。大部分可以归类为降维或汇总统计,这些方法是用来从series中提取单个值(比如sum或mean)。还有一些方法来处理缺失值:import pandas as pd import numpy as npdf = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'], columns=['one', 'two']) df使用sum的话,会返回一个series:df.sum()one 9.25 two -5.80 dtype: float64使用axis='columns' or axis=1,计算列之间的和:df.sum(axis='columns')a 1.40 b 2.60 c 0.00 d -0.55 dtype: float64计算的时候,NA(即缺失值)会被除外,除非整个切片全是NA。我们可以用skipna来跳过计算NA:df.mean(axis='columns', skipna=False)a NaN b 1.300 c NaN d -0.275 dtype: float64一些方法,比如idxmin和idxmax,能返回间接的统计值,比如index value:dfdf.idxmax()one b two d dtype: object还能计算累加值:df.cumsum()另一种类型既不是降维,也不是累加。describe能一下子产生多维汇总数据:df.describe()/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/numpy/lib/function_base.py:4116: RuntimeWarning: Invalid value encountered in percentile interpolation=interpolation)对于非数值性的数据,describe能产生另一种汇总统计:obj = pd.Series(['a', 'a', 'b', 'c'] * 4) obj0 a 1 a 2 b 3 c 4 a 5 a 6 b 7 c 8 a 9 a 10 b 11 c 12 a 13 a 14 b 15 c dtype: objectobj.describe()count 16 unique 3 top a freq 8 dtype: object1 Correlation and Covariance (相关性和协方差)假设DataFrame时股价和股票数量。这些数据取自yahoo finance,用pandas-datareader包能加载。如果没有的话,用conda或pip来下载这个包:conda install pandas-datareaderimport pandas_datareader.data as weball_data = {ticker: web.get_data_yahoo(ticker) for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']} price = pd.DataFrame({ticker: data['Adj Close'] for ticker, data in all_data.items()}) volumn = pd.DataFrame({ticker: data['Volumn'] for ticker, data in all_data.items()})上面的代码无法直接从yahoo上爬取数据,因为yahoo被verizon收购后,好像是不能用了。于是这里我们直接从下好的数据包里加载。ls ../examples/5.1 Introduction to pandas Data Structures(pandas的数据结构).ipynb 5.2 Essential Functionality(主要功能).ipynb 5.3 Summarizing and Computing Descriptive Statistics(总结和描述性统计).ipynbnbprice = pd.read_pickle('../examples/yahoo_price.pkl') volume = pd.read_pickle('../examples/yahoo_volume.pkl')price.head()volume.head()pct_change(): 这个函数用来计算同colnums两个相邻的数字之间的变化率现在我们计算一下价格百分比的变化:returns = price.pct_change() returns.tail()series的corr方法计算两个,重合的,非NA的,通过index排列好的series。cov计算方差:returns['MSFT'].corr(returns['IBM'])0.4997636114415116returns['MSFT'].cov(returns['IBM'])8.8706554797035489e-05因为MSFT是一个有效的python属性,我们可以通过更简洁的方式来选中columns:returns.MSFT.corr(returns.IBM)0.4997636114415116dataframe的corr和cov方法,能返回一个完整的相似性或方差矩阵:returns.corr()returns.cov()用Dataframe的corrwith方法,我们可以计算dataframe中不同columns之间,或row之间的相似性。传递一个series:returns.corrwith(returns.IBM)AAPL 0.386817 GOOG 0.405099 IBM 1.000000 MSFT 0.499764 dtype: float64传入一个dataframe能计算匹配的column names质监局的相似性。这里我计算vooumn中百分比变化的相似性:returns.corrwith(volume)AAPL -0.075565 GOOG -0.007067 IBM -0.204849 MSFT -0.092950 dtype: float64传入axis='columns'能做到row-by-row计算。在correlation被计算之前,所有的数据会根据label先对齐。2 Unique Values, Value Counts, and Membership(唯一值,值计数,会员)这里介绍另一种从一维series中提取信息的方法:obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])第一个函数时unique,能告诉我们series里unique values有哪些:uniques = obj.unique() uniquesarray(['c', 'a', 'd', 'b'], dtype=object)返回的unique values不是有序的,但我们可以排序,uniques.sort()。相对的,value_counts能计算series中值出现的频率:obj.value_counts()a 3 c 3 b 2 d 1 dtype: int64返回的结果是按降序处理的。vaule_counts也是pandas中的方法,能用在任何array或sequence上:pd.value_counts(obj.values, sort=False)d 1 c 3 b 2 a 3 dtype: int64isin 能实现一个向量化的集合成员关系检查,能用于过滤数据集,检查一个子集,是否在series的values中,或在dataframe的column中:obj0 c 1 a 2 d 3 a 4 a 5 b 6 b 7 c 8 c dtype: objectmask = obj.isin(['b', 'c']) mask0 True 1 False 2 False 3 False 4 False 5 True 6 True 7 True 8 True dtype: boolobj[mask]0 c 5 b 6 b 7 c 8 c dtype: object与isin相对的另一个方法是Index.get_indexer,能返回一个index array,告诉我们有重复值的values(to_match),在非重复的values(unique_vals)中对应的索引值:to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a']) unique_vals = pd.Series(['c', 'b', 'a'])pd.Index(unique_vals).get_indexer(to_match)array([0, 2, 1, 1, 0, 2])在某些情况下,你可能想要计算一下dataframe中多个column的柱状图:data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4], 'Qu2': [2, 3, 1, 2, 3], 'Qu3': [1, 5, 2, 4, 4]}) data把pandas.value_counts传递给dataframe的apply函数:result = data.apply(pd.value_counts) result每一行的laebls(即1,2,3,4,5)其实就是整个data里出现过的值,从1到5。而对应的每个方框里的值,则是表示该值在当前列中出现的次数。比如,(2, Qu1)的值是Nan,说明2这个数字没有在Qu1这一列出现过。(2, Qu2)的值是2,说明2这个数字在Qu2这一列出现过2次。(2, Qu3)的值是1,说明2这个数字在Qu3这一列出现过1次。
0
0
0
浏览量1653
清晨我上码

pandas使用教程:迭代、填充和合并

pandas迭代item迭代items() 通过键值对进行迭代:Series:(Index,标量值)对DataFrame:(列,Series)对team name Liver E Arry C Ack A Eorge C Oah D .. Gabriel C Austin7 C Lincoln4 C Eli E Ben E Name: team, Length: 100, dtype: objectitemrows迭代iterrows() 迭代 DataFrame 或 Series 里的每一行数据。这个操作返回一个迭代器,生成索引值及包含每行数据的 Series。for index,row in df.iterrows(): print(index) print(row) Liver team E Q1 89 Q2 21 Q3 24 Q4 64 Name: Liver, dtype: objectitertuples迭代itertuples() 方法返回为 DataFrame 里每行数据生成命名元组的迭代器。该元组的第一个元素是行的索引值,其余的值则是行的值。for row in df.itertuples(): print(row) Output exceeds the size limit. Open the full output data in a text editorPandas(Index='Liver', team='E', Q1=89, Q2=21, Q3=24, Q4=64) Pandas(Index='Arry', team='C', Q1=36, Q2=37, Q3=37, Q4=57) Pandas(Index='Ack', team='A', Q1=57, Q2=60, Q3=18, Q4=84) Pandas(Index='Eorge', team='C', Q1=93, Q2=96, Q3=71, Q4=78) Pandas(Index='Oah', team='D', Q1=65, Q2=49, Q3=61, Q4=86) Pandas(Index='Harlie', team='C', Q1=24, Q2=13, Q3=87, Q4=43)rng = pd.date_range('1/3/2000', periods=8) ts = pd.Series(np.random.randint(0,100,8), index=rng) ts2 = ts[[0, 3, 6]] ts2.reindex(ts.index) 2000-01-03 20.0 2000-01-04 NaN 2000-01-05 NaN 2000-01-06 94.0 2000-01-07 NaN 2000-01-08 NaN 2000-01-09 24.0 2000-01-10 NaN Freq: D, dtype: float64先前填充#先前填充 ts2.reindex(ts.index, method='ffill') 2000-01-03 20 2000-01-04 20 2000-01-05 20 2000-01-06 94 2000-01-07 94 2000-01-08 94 2000-01-09 24 2000-01-10 24 Freq: D, dtype: int32向后填充ts2.reindex(ts.index, method='bfill') 2000-01-03 20.0 2000-01-04 94.0 2000-01-05 94.0 2000-01-06 94.0 2000-01-07 24.0 2000-01-08 24.0 2000-01-09 24.0 2000-01-10 NaN Freq: D, dtype: float64就近填充ts2.reindex(ts.index, method='nearest') 2000-01-03 20 2000-01-04 20 2000-01-05 94 2000-01-06 94 2000-01-07 94 2000-01-08 24 2000-01-09 24 2000-01-10 24 Freq: D, dtype: int32合并merge结合 concatPandas 提供了多种将 Series、DataFrame 对象组合在一起的功能,用索引与关联代数功能的多种设置逻辑可执行连接(join)与合并(merge)操作。piece = [df[:2],df[5:7],df[95:]] pd.concat(piece) team Q1 Q2 Q3 Q4 name Liver E 89 21 24 64 Arry C 36 37 37 57 Harlie C 24 13 87 43 Acob B 61 95 94 8 Gabriel C 48 59 87 74 Austin7 C 21 31 30 43 Lincoln4 C 98 93 1 20 Eli E 11 74 58 91 Ben E 21 43 41 74数据透视表(Pivot Tables)#name为行,team为列 df.pivot(index='name',columns='team',values='Q1') team A B C D E name Aaron 96.0 NaN NaN NaN NaN Ack 57.0 NaN NaN NaN NaN Acob NaN 61.0 NaN NaN NaN Adam NaN NaN 90.0 NaN NaN Aiden NaN NaN NaN 20.0 NaN ... ... ... ... ... ... Toby 52.0 NaN NaN NaN NaN Tommy NaN NaN 29.0 NaN NaN Tyler 75.0 NaN NaN NaN NaN William NaN NaN 80.0 NaN NaN Zachary NaN NaN NaN NaN 12.0
0
0
0
浏览量668
清晨我上码

numpy教程:The NumPy ndarray

NumPy Basics: Arrays and Vectorized Computation 数组和向量计算在数值计算领域,说Numpy是python最重要的包也不为过。在numpy中有下面这些东西:ndarray, 一个有效的多维数组,能提供以数组为导向的快速数值计算和灵活的广播功能(broadcasting)便利的数学函数用于读取/写入(reading/writing)数据到磁盘的便利工具线性代数,随机数生成,傅里叶变换能力可以用C API来写C,C++,或FORTRAN通过学习理解numpy中数组和数组导向计算,能帮我们理解pandas之类的工具。4.1 The NumPy ndarray: A Multidimensional Array Object(ndarray: 多维数组对象)N-dimensional array object(n维数组对象), or ndarray,这是numpy的关键特征。先来尝试一下,生成一个随机数组:import numpy as npe:\python3.7\lib\site-packages\numpy\_distributor_init.py:32: UserWarning: loaded more than 1 DLL from .libs: e:\python3.7\lib\site-packages\numpy\.libs\libopenblas.TXA6YQSD3GCQQC22GEQ54J2UDCXDXHWN.gfortran-win_amd64.dll e:\python3.7\lib\site-packages\numpy\.libs\libopenblas.XWYDX2IKJW2NMTWSFYNGFUWKQU3LYTCZ.gfortran-win_amd64.dll stacklevel=1)# Generate some random data data = np.random.randn(2, 3)dataarray([[ 0.66415323, -1.4920515 , 0.35804571], [ 0.59032967, -0.28798428, 0.52392625]])进行一些数学运算:data * 10array([[-3.55123655, -6.37795453, 1.41379333], [ 3.66420556, 3.0898139 , -8.70402916]])data + dataarray([[-0.71024731, -1.27559091, 0.28275867], [ 0.73284111, 0.61796278, -1.74080583]])每一个数组都有一个shape,来表示维度大小。而dtype,用来表示data type:data.shape(2, 3)data.dtypedtype('float64')1 Greating ndarrays (创建n维数组)最简单的方法使用array函数,输入一个序列即可,比如list:data1 = [6, 7.5, 8, 0, 1] arr1 = np.array(data1) arr1array([ 6. , 7.5, 8. , 0. , 1. ])嵌套序列能被转换为多维数组:data2 = [[1, 2, 3, 4], [5, 6, 7, 8]] arr2 = np.array(data2) arr2array([[1, 2, 3, 4], [5, 6, 7, 8]])因为data2是一个list of lists, 所以arr2维度为2。我们能用ndim和shape属性来确认一下:arr2.ndim2arr2.shape(2, 4)除非主动声明,否则np.array会自动给data搭配适合的类型,并保存在dtype里:arr1.dtypedtype('float64')arr2.dtypedtype('int64')除了np.array,还有一些其他函数能创建数组。比如zeros,ones,另外还可以在一个tuple里指定shape:np.zeros(10)array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])np.zeros((3, 6))array([[ 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0.]])np.empty((2, 3, 2))array([[[ 0.00000000e+000, 0.00000000e+000], [ 2.16538378e-314, 2.16514681e-314], [ 2.16511832e-314, 2.16072529e-314]], [[ 0.00000000e+000, 0.00000000e+000], [ 2.14037397e-314, 6.36598737e-311], [ 0.00000000e+000, 0.00000000e+000]]])np.empty并不能保证返回所有是0的数组,某些情况下,会返回为初始化的垃圾数值,比如上面。arange是一个数组版的python range函数:np.arange(15)array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])2 Data Types for ndarraysdtype保存数据的类型:arr1 = np.array([1, 2, 3], dtype=np.float64)arr2 = np.array([1, 2, 3], dtype=np.int32)arr1.dtypedtype('float64')arr2.dtypedtype('int32')dtype才是numpy能灵活处理其他外界数据的原因。可以用astype来转换类型:arr = np.array([1, 2, 3, 4, 5]) arr.dtypedtype('int64')float_arr = arr.astype(np.float64) float_arr.dtypedtype('float64')上面是把int变为float。如果是把float变为int,小数点后的部分会被丢弃:arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1]) arrarray([ 3.7, -1.2, -2.6, 0.5, 12.9, 10.1])arr.astype(np.int32)array([ 3, -1, -2, 0, 12, 10], dtype=int32)还可以用astype把string里的数字变为实际的数字:numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string_) numeric_stringsarray([b'1.25', b'-9.6', b'42'], dtype='|S4')numeric_strings.astype(float)array([ 1.25, -9.6 , 42. ])要十分注意numpy.string_类型,这种类型的长度是固定的,所以可能会直接截取部分输入而不给警告。如果转换(casting)失败的话,会给出一个ValueError提示。可以用其他数组的dtype直接来制定类型:int_array = np.arange(10) calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)int_array.astype(calibers.dtype)array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])还可以利用类型的缩写,比如u4就代表unit32:empty_unit32 = np.empty(8, dtype='u4') empty_unit32array([0, 0, 0, 0, 0, 0, 0, 0], dtype=uint32)记住,astype总是会返回一个新的数组3 Arithmetic with NumPy Arrays(数组计算)数组之所以重要,是因为不用写for循环就能表达很多操作,这种特性叫做vectorization(向量化)。任何两个大小相等的数组之间的运算,都是element-wise(点对点):arr = np.array([[1., 2., 3.], [4., 5., 6.]])arrarray([[ 1., 2., 3.], [ 4., 5., 6.]])arr * arrarray([[ 1., 4., 9.], [ 16., 25., 36.]])arr - arrarray([[ 0., 0., 0.], [ 0., 0., 0.]])element-wise 我翻译为点对点,就是指两个数组的运算,在同一位置的元素间才会进行运算。这种算数操作如果涉及标量(scalar)的话,会涉及到数组的每一个元素:1 / arrarray([[ 1. , 0.5 , 0.33333333], [ 0.25 , 0.2 , 0.16666667]])arr ** 0.5array([[ 1. , 1.41421356, 1.73205081], [ 2. , 2.23606798, 2.44948974]])两个数组的比较会产生布尔数组:arr2 = np.array([[0., 4., 1.], [7., 2., 12.]]) arr2array([[ 0., 4., 1.], [ 7., 2., 12.]])arr2 > arrarray([[False, True, False], [ True, False, True]], dtype=bool)4 Basic Indexing and Slicing(基本的索引和切片)一维的我们之前已经在list部分用过了,没什么不同:arr = np.arange(10) arrarray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])arr[5]5arr[5:8]array([5, 6, 7])arr[5:8] = 12arrarray([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])这里把12赋给arr[5:8],其实用到了broadcasted(我觉得应该翻译为广式转变)。这里有一个比较重要的概念需要区分,python内建的list与numpy的array有个明显的区别,这里array的切片后的结果只是一个views(视图),用来代表原有array对应的元素,而不是创建了一个新的array。但list里的切片是产生了一个新的list:arr_slice = arr[5:8] arr_slicearray([12, 12, 12])如果我们改变arr_slice的值,会反映在原始的数组arr上:arr_slice[1] = 12345arrarray([ 0, 1, 2, 3, 4, 12, 12345, 12, 8, 9])[:]这个赋值给所有元素:arr_slice[:] = 64arrarray([ 0,  1,  2,  3,  4, 64, 64, 64,  8,  9])之所以这样设计是出于性能和内存的考虑,毕竟如果总是复制数据的话,会很影响运算时间。当然如果想要复制,可以使用copy()方法,比如arr[5:8].copy()在一个二维数组里,单一的索引指代的是一维的数组:arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) arr2d[2]array([7, 8, 9])有两种方式可以访问单一元素:arr2d[0][2]3arr2d[0, 2]3对于多维数组,如果省略后面的索引,返回的将是一个低纬度的多维数组。比如下面一个2 x 2 x 3数组:arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) arr3darray([[[ 1, 2, 3], [ 4, 5, 6]], [[ 7, 8, 9], [10, 11, 12]]])arr3d[0]是一个2x3数组:arr3d[0]array([[1, 2, 3], [4, 5, 6]])标量和数组都能赋给arr3d[0]:old_values = arr3d[0].copy() arr3d[0] = 42 arr3darray([[[42, 42, 42], [42, 42, 42]], [[ 7, 8, 9], [10, 11, 12]]])arr3d[0] = old_values arr3darray([[[ 1, 2, 3], [ 4, 5, 6]], [[ 7, 8, 9], [10, 11, 12]]])arr3d[1, 0]会给你一个(1, 0)的一维数组:arr3d[1, 0]array([7, 8, 9])上面的一步等于下面的两步:x = arr3d[1] xarray([[ 7, 8, 9], [10, 11, 12]])x[0]array([7, 8, 9])一定要牢记这些切片后返回的数组都是viewsIndexing with slices(用切片索引)一维的话和python里的list没什么差别:arrarray([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])arr[1:6]array([ 1, 2, 3, 4, 64])二维的话,数组的切片有点不同:arr2d[:2]array([[1, 2, 3], [4, 5, 6]])可以看到,切片是沿着axis 0(行)来处理的。所以,数组中的切片,是要沿着设置的axis来处理的。我们可以把arr2d[:2]理解为“选中arr2d的前两行”。当然,给定多个索引后,也可以使用复数切片:arr2darray([[1, 2, 3], [4, 5, 6], [7, 8, 9]])arr2d[:2, 1:] # 前两行,第二列之后array([[2, 3], [5, 6]])记住,选中的是array view。通过混合整数和切片,能做低维切片。比如,我们选中第二行的前两列:arr2d[1, :2]array([4, 5])选中第三列的前两行:arr2d[:2, 2]array([3, 6])冒号表示提取整个axis(轴):arr2d[:, :1]array([[1], [4], [7]])赋值也很方便:arr2d[:2, 1:] = 0 arr2darray([[1, 0, 0], [4, 0, 0], [7, 8, 9]])5 Boolean Indexing (布尔索引)假设我们的数组数据里有一些重复。这里我们用numpy.random里的randn函数来随机生成一些离散数据:names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe']) namesarray(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')data = np.random.randn(7, 4) dataarray([[ 0.02584271, -1.53529621, 0.73143988, -0.34086189], [ 0.40864782, 0.53476799, 1.09620596, 0.4846564 ], [ 1.95024076, -0.37291038, -0.40424703, 0.30297059], [-0.48632936, 0.63817756, -0.40792716, -1.48037389], [-0.81976335, -1.10162466, -0.59823212, -0.10926744], [-0.5212113 , 0.29449179, 2.0568032 , 2.00515735], [-2.36066876, -0.3294302 , -0.24464646, -0.81432884]])假设每一个name对应data数组中的一行,我们想要选中name为’Bob’的所有行。就像四则运算,用比较运算符(==):names == 'Bob'array([ True, False, False, True, False, False, False], dtype=bool)然后用这个布尔数组当做索引:data[names == 'Bob']array([[ 0.02584271, -1.53529621, 0.73143988, -0.34086189], [-0.48632936, 0.63817756, -0.40792716, -1.48037389]])注意:布尔数组和data数组的长度要一样。我们可以选中names=='Bob'的行,然后索引列:data[names == 'Bob', 2:]array([[ 0.73143988, -0.34086189], [-0.40792716, -1.48037389]])data[names == 'Bob', 3]array([-0.34086189, -1.48037389])选中除了’Bob’外的所有行,可以用!=或者~:names != 'Bob'array([False, True, True, False, True, True, True], dtype=bool)data[~(names == 'Bob')]array([[ 0.40864782, 0.53476799, 1.09620596, 0.4846564 ], [ 1.95024076, -0.37291038, -0.40424703, 0.30297059], [-0.81976335, -1.10162466, -0.59823212, -0.10926744], [-0.5212113 , 0.29449179, 2.0568032 , 2.00515735], [-2.36066876, -0.3294302 , -0.24464646, -0.81432884]])当想要反转一个条件时,用~操作符很方便:cond = names == 'Bob'data[~cond]array([[ 0.40864782, 0.53476799, 1.09620596, 0.4846564 ], [ 1.95024076, -0.37291038, -0.40424703, 0.30297059], [-0.81976335, -1.10162466, -0.59823212, -0.10926744], [-0.5212113 , 0.29449179, 2.0568032 , 2.00515735], [-2.36066876, -0.3294302 , -0.24464646, -0.81432884]])选中2个或3个名字,组合多个布尔条件,用布尔运算符&,|,另外python中的关键词and和or不管用:namesarray(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')mask = (names == 'Bob') | (names == 'Will') maskarray([ True, False, True, True, True, False, False], dtype=bool)data[mask]array([[ 0.02584271, -1.53529621, 0.73143988, -0.34086189], [ 1.95024076, -0.37291038, -0.40424703, 0.30297059], [-0.48632936, 0.63817756, -0.40792716, -1.48037389], [-0.81976335, -1.10162466, -0.59823212, -0.10926744]])用布尔索引总是会返回一份新创建的数据,原本的数据不会被改变。更改值的方式也很直觉。比如我们想让所有负数变为0:data[data < 0] = 0dataarray([[ 0.02584271, 0. , 0.73143988, 0. ], [ 0.40864782, 0.53476799, 1.09620596, 0.4846564 ], [ 1.95024076, 0. , 0. , 0.30297059], [ 0. , 0.63817756, 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0. , 0.29449179, 2.0568032 , 2.00515735], [ 0. , 0. , 0. , 0. ]])用一维的布尔数组也能更改所有行或列:namesarray(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')data[names != 'Joe'] = 7dataarray([[ 7. , 7. , 7. , 7. ], [ 0.40864782, 0.53476799, 1.09620596, 0.4846564 ], [ 7. , 7. , 7. , 7. ], [ 7. , 7. , 7. , 7. ], [ 7. , 7. , 7. , 7. ], [ 0. , 0.29449179, 2.0568032 , 2.00515735], [ 0. , 0. , 0. , 0. ]])6 Fancy Indexing(花式索引)通过整数数组来索引。假设我们有一个8 x 4的数组:arr = np.empty((8, 4))for i in range(8): arr[i] = iarrarray([[ 0., 0., 0., 0.], [ 1., 1., 1., 1.], [ 2., 2., 2., 2.], [ 3., 3., 3., 3.], [ 4., 4., 4., 4.], [ 5., 5., 5., 5.], [ 6., 6., 6., 6.], [ 7., 7., 7., 7.]])想要按一定顺序选出几行,可以用一个整数list或整数ndarray来指定顺序:arr[[4, 3, 0, 6]]array([[ 4., 4., 4., 4.], [ 3., 3., 3., 3.], [ 0., 0., 0., 0.], [ 6., 6., 6., 6.]])用符号来从后选择row:arr[[-3, -5, -7]]array([[ 5., 5., 5., 5.], [ 3., 3., 3., 3.], [ 1., 1., 1., 1.]])用多维索引数组,能选出由一维数组中的元素,通过在每个tuple中指定索引:arr = np.arange(32).reshape((8, 4)) arrarray([[ 0, 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]])arr[[1, 5, 7, 2], [0, 3, 1, 2]]array([ 4, 23, 29, 10])可以看到[ 4, 23, 29, 10]分别对应(1, 0), (5, 3), (7, 1), (2, 2)。不论数组有多少维,fancy indexing的结果总是一维。对于长方形区域,有下面的方法来截取:arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]array([[ 4, 7, 5, 6], [20, 23, 21, 22], [28, 31, 29, 30], [ 8, 11, 9, 10]])上面的意思是,先从arr中选出[1, 5, 7, 2]这四行:array([[ 4, 5, 6, 7], [20, 21, 22, 23], [28, 29, 30, 31], [ 8, 9, 10, 11]])然后[:, [0, 3, 1, 2]]表示选中所有行,但是列的顺序要按0,3,1,2来排。于是得到:array([[ 4, 7, 5, 6], [20, 23, 21, 22], [28, 31, 29, 30], [ 8, 11, 9, 10]])要记住,fancy indexing和切片不同,得到的是一个新的array。7 Transposing Arrays and Swapping Axes(数组转置和轴交换)转置也是返回一个view,而不是新建一个数组。有两种方式,一个是transpose方法,一个是T属性:arr = np.arange(15).reshape((3, 5)) arrarray([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]])arr.Tarray([[ 0, 5, 10], [ 1, 6, 11], [ 2, 7, 12], [ 3, 8, 13], [ 4, 9, 14]])做矩阵计算的时候,这个功能很常用,计算矩阵乘法的时候,用np.dot:arr = np.arange(8).reshape((4, 2)) print(arr.T) print(arr)[[0 2 4 6] [1 3 5 7]] [[0 1] [2 3] [4 5] [6 7]]np.dot(arr.T, arr)array([[56, 68], [68, 84]])上面的例子是 (2x4) x (4x2) = (2x2)。得到的结果是2x2维,就是普通的矩阵乘法。对于多维数组,transpose会接受由轴数字组成的tuple,来交换轴:arr = np.arange(16).reshape((2, 2, 4)) arrarray([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]], [[ 8, 9, 10, 11], [12, 13, 14, 15]]])arr.transpose((1, 0, 2))array([[[ 0, 1, 2, 3], [ 8, 9, 10, 11]], [[ 4, 5, 6, 7], [12, 13, 14, 15]]])这里,secode axis(1)被设为第一个,first axis(0)第二个,最后的axis没边。使用.T来转置swapping axes(交换轴)的一个特殊情况。ndarray有方法叫做swapaxes, 这个方法取两个axis值,并交换这两个轴:arrarray([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]], [[ 8, 9, 10, 11], [12, 13, 14, 15]]])arr.swapaxes(1, 2) # 直交换second axis和last axisarray([[[ 0, 4], [ 1, 5], [ 2, 6], [ 3, 7]], [[ 8, 12], [ 9, 13], [10, 14], [11, 15]]])swapaxes也是返回view,不生成新的data。
0
0
0
浏览量931
清晨我上码

Apply:General split-apply-combine 通常的分割-应用-合并

10.3 Apply:General split-apply-combine(应用:通用的分割-应用-合并)general-purpose: 可以理解为通用,泛用。例子:在计算机软件中,通用编程语言(General-purpose programming language )指被设计为各种应用领域服务的编程语言。通常通用编程语言不含有为特定应用领域设计的结构。相对而言,特定域编程语言就是为某一个特定的领域或应用软件设计的编程语言。比如说,LaTeX就是专门为排版文献而设计的语言。最通用的GroupBy(分组)方法是apply,这也是本节的主题。如下图所示,apply会把对象分为多个部分,然后将函数应用到每一个部分上,然后把所有的部分都合并起来:返回之前提到的tipping数据集,假设我们想要根据不同组(group),选择前5个tip_pct值最大的。首先,写一个函数,函数的功能为在特定的列,选出有最大值的行: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()def top(df, n=5, column='tip_pct'): return df.sort_values(by=column)[-n:]top(tips, n=6)现在,如果我们按smoker分组,然后用apply来使用这个函数,我们能得到下面的结果:tips.groupby('smoker').apply(top)我们来解释下上面这一行代码发生了什么。这里的top函数,在每一个DataFrame中的行组(row group)都被调用了一次,然后各自的结果通过pandas.concat合并了,最后用组名(group names)来标记每一部分。(译者:可以理解为,我们先按smoker这一列对整个DataFrame进行了分组,一共有No和Yes两组,然后对每一组上调用了top函数,所以每一组会返还5行作为结果,最后把两组的结果整合起来,一共是10行)。最后的结果是有多层级索引(hierarchical index)的,而且这个多层级索引的内部层级(inner level)含有来自于原来DataFrame中的索引值(index values)如果传递一个函数给apply,可以在函数之后,设定其他一些参数:tips.groupby(['smoker', 'day']).apply(top, n=1, column='total_bill')除了上面这些基本用法,要想用好apply可能需要一点创新能力。毕竟传给这个函数的内容取决于我们自己,而最终的结果只需要返回一个pandas对象或一个标量。这一章的剩余部分主要介绍如何解决在使用groupby时遇到的一些问题。可以试一试在GroupBy对象上调用describe:result = tips.groupby('smoker')['tip_pct'].describe()resultsmoker No count 151.000000 mean 0.159328 std 0.039910 min 0.056797 25% 0.136906 50% 0.155625 75% 0.185014 max 0.291990 Yes count 93.000000 mean 0.163196 std 0.085119 min 0.035638 25% 0.106771 50% 0.153846 75% 0.195059 max 0.710345 Name: tip_pct, dtype: float64result.unstack('smoker')在GroupBy内部,当我们想要调用一个像describe这样的函数的时候,其实相当于下面的写法:f = lambda x: x.describe() grouped.apply(f)1 Suppressing the Group Keys(抑制组键)在接下来的例子,我们会看到作为结果的对象有一个多层级索引(hierarchical index),这个多层级索引是由原来的对象中,组键(group key)在每一部分的索引上得到的。我们可以在groupby函数中设置group_keys=False来关闭这个功能:tips.groupby('smoker', group_keys=False).apply(top)2 Quantile and Bucket Analysis(分位数与桶分析)在第八章中,我们介绍了pandas的一些工具,比如cut和qcut,通过设置中位数,切割数据为buckets with bins(有很多箱子的桶)。把函数通过groupby整合起来,可以在做桶分析或分位数分析的时候更方便。假设一个简单的随机数据集和一个等长的桶类型(bucket categorization),使用cut:frame = pd.DataFrame({'data1': np.random.randn(1000), 'data2': np.random.randn(1000)}) frame.head()quartiles = pd.cut(frame.data1, 4) quartiles[:10]0 (0.194, 1.795] 1 (1.795, 3.395] 2 (-1.407, 0.194] 3 (-1.407, 0.194] 4 (0.194, 1.795] 5 (0.194, 1.795] 6 (0.194, 1.795] 7 (-1.407, 0.194] 8 (-1.407, 0.194] 9 (-1.407, 0.194] Name: data1, dtype: category Categories (4, object): [(-3.0139, -1.407] < (-1.407, 0.194] < (0.194, 1.795] < (1.795, 3.395]]cut返回的Categorical object(类别对象)能直接传入groupby。所以我们可以在data2列上计算很多统计值:def get_stats(group): return {'min': group.min(), 'max': group.max(), 'count': group.count(), 'mean': group.mean()}grouped = frame.data2.groupby(quartiles)grouped.apply(get_stats).unstack()也有相同长度的桶(equal-length buckets);想要按照样本的分位数得到相同长度的桶,用qcut。这里设定labels=False来得到分位数的数量:# Return quantile numbers grouping = pd.qcut(frame.data1, 10, labels=False)译者:上面的代码是把frame的data1列分为10个bin,每个bin都有相同的数量。因为一共有1000个样本,所以每个bin里有100个样本。grouping保存的是每个样本的index以及其对应的bin的编号。grouped = frame.data2.groupby(grouping)grouped.apply(get_stats).unstack()对于pandas的Categorical类型,会在第十二章做详细介绍。3 Example: Filling Missing Values with Group-Specific Values(例子:用组特异性值来填充缺失值)在处理缺失值的时候,一些情况下我们会直接用dropna来把缺失值删除,但另一些情况下,我们希望用一些固定的值来代替缺失值,而fillna就是用来做这个的,例如,这里我们用平均值mean来代替缺失值NA:s = pd.Series(np.random.randn(6))s[::2] = np.nans0 NaN 1 0.878562 2 NaN 3 -0.264051 4 NaN 5 0.760488 dtype: float64s.fillna(s.mean())0 0.458333 1 0.878562 2 0.458333 3 -0.264051 4 0.458333 5 0.760488 dtype: float64假设我们想要给每一组填充不同的值。一个方法就是对数据分组后,用apply来调用fillna,在每一个组上执行一次。这里有一些样本是把美国各州分为西部和东部:states = ['Ohio', 'New York', 'Vermont', 'Florida', 'Oregon', 'Nevada', 'California', 'Idaho']group_key = ['East'] * 4 + ['West'] * 4 group_key['East', 'East', 'East', 'East', 'West', 'West', 'West', 'West']data = pd.Series(np.random.randn(8), index=states) dataOhio 0.683283 New York -1.059896 Vermont 0.105837 Florida -0.328586 Oregon 1.973413 Nevada 0.656673 California 0.001700 Idaho -0.713295 dtype: float64我们令data中某些值为缺失值:data[['Vermont', 'Nevada', 'Idaho']] = np.nan dataOhio 0.683283 New York -1.059896 Vermont NaN Florida -0.328586 Oregon 1.973413 Nevada NaN California 0.001700 Idaho NaN dtype: float64data.groupby(group_key).mean()East -0.235066 West 0.987556 dtype: float64然后我们可以用每个组的平均值来填充NA:fill_mean = lambda g: g.fillna(g.mean())data.groupby(group_key).apply(fill_mean)Ohio 0.683283 New York -1.059896 Vermont -0.235066 Florida -0.328586 Oregon 1.973413 Nevada 0.987556 California 0.001700 Idaho 0.987556 dtype: float64在另外一些情况下,我们可能希望提前设定好用于不同组的填充值。因为group有一个name属性,我们可以利用这个:fill_values = {'East': 0.5, 'West': -1}fill_func = lambda g: g.fillna(fill_values[g.name])data.groupby(group_key).apply(fill_func)Ohio 0.683283 New York -1.059896 Vermont 0.500000 Florida -0.328586 Oregon 1.973413 Nevada -1.000000 California 0.001700 Idaho -1.000000 dtype: float644 Example: Random Sampling and Permutation(例子:随机抽样和排列)假设我们想要从一个很大的数据集里随机抽出一些样本,这里我们可以在Series上用sample方法。为了演示,这里县创建一副模拟的扑克牌:# Hearts红桃,Spades黑桃,Clubs梅花,Diamonds方片 suits = ['H', 'S', 'C', 'D'] card_val = (list(range(1, 11)) + [10] * 3) * 4 base_names = ['A'] + list(range(2, 11)) + ['J', 'K', 'Q'] cards = [] for suit in ['H', 'S', 'C', 'D']: cards.extend(str(num) + suit for num in base_names) deck = pd.Series(card_val, index=cards)这样我们就得到了一个长度为52的Series,索引(index)部分是牌的名字,对应的值为牌的点数,这里的点数是按Blackjack(二十一点)的游戏规则来设定的。Blackjack(二十一点): 2点至10点的牌以牌面的点数计算,J、Q、K 每张为10点,A可记为1点或为11点。这里为了方便,我们只把A记为1点。deck[:13]AH 1 2H 2 3H 3 4H 4 5H 5 6H 6 7H 7 8H 8 9H 9 10H 10 JH 10 KH 10 QH 10 dtype: int64现在,就像我们上面说的,随机从牌组中抽出5张牌:def draw(deck, n=5): return deck.sample(n)draw(deck)7H 7 6D 6 AC 1 JH 10 JS 10 dtype: int64假设我们想要从每副花色中随机抽取两张,花色是每张牌名字的最后一个字符(即H, S, C, D),我们可以根据花色分组,然后使用apply:get_suit = lambda card: card[-1] # last letter is suitdeck.groupby(get_suit).apply(draw, n=2)C QC 10 9C 9 D 3D 3 JD 10 H KH 10 6H 6 S 3S 3 7S 7 dtype: int64另外一种写法:deck.groupby(get_suit, group_keys=False).apply(draw, n=2)7C 7 KC 10 AD 1 4D 4 AH 1 8H 8 7S 7 9S 9 dtype: int645 Example: Group Weighted Average and Correlation(例子:组加权平均和相关性)在groupby的split-apply-combine机制下,DataFrame的两列或两个Series,计算组加权平均(Group Weighted Average)是可能的。这里举个例子,下面的数据集包含组键,值,以及权重:df = pd.DataFrame({'category': ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'], 'data': np.random.randn(8), 'weights': np.random.rand(8)}) df按category分组来计算组加权平均:grouped = df.groupby('category')get_wavg = lambda g: np.average(g['data'], weights=g['weights'])grouped.apply(get_wavg)category a 0.695189 b -0.399497 dtype: float64另一个例子,考虑一个从Yahoo!财经上得到的经济数据集,包含一些股票交易日结束时的股价,以及S&P 500指数(即SPX符号):标准普尔500指数英文简写为S&P 500 Index,是记录美国500家上市公司的一个股票指数。这个股票指数由标准普尔公司创建并维护。标准普尔500指数覆盖的所有公司,都是在美国主要交易所,如纽约证券交易所、Nasdaq交易的上市公司。与道琼斯指数相比,标准普尔500指数包含的公司更多,因此风险更为分散,能够反映更广泛的市场变化。close_px = pd.read_csv('../examples/stock_px_2.csv', parse_dates=True, index_col=0)close_px.info()<class 'pandas.core.frame.DataFrame'> DatetimeIndex: 2214 entries, 2003-01-02 to 2011-10-14 Data columns (total 4 columns): AAPL 2214 non-null float64 MSFT 2214 non-null float64 XOM 2214 non-null float64 SPX 2214 non-null float64 dtypes: float64(4) memory usage: 86.5 KBclose_px[-4:]一个比较有意思的尝试是计算一个DataFrame,包括与SPX这一列逐年日收益的相关性(计算百分比变化)。一个可能的方法是,我们先创建一个能计算不同列相关性的函数,然后拿每一列与SPX这一列求相关性:spx_corr = lambda x: x.corrwith(x['SPX'])然后我们通过pct_change在close_px上计算百分比的变化:rets = close_px.pct_change().dropna()最后,我们按年来给这些百分比变化分组,年份可以从每行的标签中通过一个一行函数提取,然后返回的结果中,用datetime标签来表示年份:get_year = lambda x: x.yearby_year = rets.groupby(get_year)by_year.apply(spx_corr)我们也可以计算列内的相关性。这里我们计算苹果和微软每年的相关性:by_year.apply(lambda g: g['AAPL'].corr(g['MSFT']))2003 0.480868 2004 0.259024 2005 0.300093 2006 0.161735 2007 0.417738 2008 0.611901 2009 0.432738 2010 0.571946 2011 0.581987 dtype: float646 Example: Group-Wise Linear Regression(例子:组对组的线性回归)就像上面介绍的例子,使用groupby可以用于更复杂的组对组统计分析,只要函数能返回一个pandas对象或标量。例如,我们可以定义regress函数(利用statsmodels库),在每一个数据块(each chunk of data)上进行普通最小平方回归(ordinary least squares (OLS) regression)计算:import statsmodels.api as smdef regress(data, yvar, xvars): Y = data[yvar] X = data[xvars] X['intercept'] = 1 result = sm.OLS(Y, X).fit() return result.params现在,按年用苹果AAPL在标普SPX上做线性回归:by_year.apply(regress, 'AAPL', ['SPX'])
0
0
0
浏览量291
清晨我上码

pandas使用教程:pandas resample函数处理时间序列数据

时间序列(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
0
0
0
浏览量2014
清晨我上码

pandas使用教程:数据透视表函数 pivot_table

导入数据使用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
0
0
0
浏览量2020
清晨我上码

pandas教程:Handling Missing Data 处理缺失数据

Chapter 7 Data Cleaning and Preparation 数据清洗和准备其实数据分析中80%的时间都是在数据清理部分,loading, clearning, transforming, rearranging。而pandas非常适合用来执行这些任务。7.1 Handling Missing Data 处理缺失数据在pandas中,missing data呈现的方式有些缺点的,但对大部分用户能起到足够的效果。对于数值型数据,pandas用浮点值Nan(Not a Number)来表示缺失值。我们称之为识别符(sentinel value),这种值能被轻易检测到:import pandas as pd import numpy as npstring_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado']) string_data0 aardvark 1 artichoke 2 NaN 3 avocado dtype: objectstring_data.isnull() 1 0 False 1 False 2 True 3 False dtype: bool在pandas中,我们使用了R语言中的一些传统,把缺失值表示为NA(not available)。在统计应用里,NA数据别是要么是数据不存在,要么是存在但不能被检测到。做数据清理的时候,对缺失值做分析是很重要的,我们要确定是否是数据收集的问题,或者缺失值是否会带来潜在的偏见。内建的Python None值也被当做NA:string_data[0] = Nonestring_data.isnull()0 True 1 False 2 True 3 False dtype: bool1 Filtering Out Missing Data(过滤缺失值)有一些方法来过滤缺失值。可以使用pandas.isnull和boolean indexing, 配合使用dropna。对于series,只会返回non-null数据和index values:from numpy import nan as NAdata = pd.Series([1, NA, 3.5, NA, 7])data.dropna()0 1.0 2 3.5 4 7.0 dtype: float64上面的等同于:data[data.notnull()]0 1.0 2 3.5 4 7.0 dtype: float64对于DataFrame,会复杂一些。你可能想要删除包含有NA的row和column。dropna默认会删除包含有缺失值的row:data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA], [NA, NA, NA], [NA, 6.5, 3.]]) datacleaned = data.dropna()cleaned设定how=all只会删除那些全是NA的行:data.dropna(how='all')删除列也一样,设置axis=1:data[4] = NA datadata.dropna(axis=1, how='all')一种删除DataFrame row的相关应用是是time series data。假设你想要保留有特定数字的观测结果,可以使用thresh参数:df = pd.DataFrame(np.random.randn(7, 3))dfdf.iloc[:4, 1] = NA dfdf.iloc[:2, 2] = NA dfdf.dropna()df.dropna(thresh=2) 2 Filling In Missing Data(填补缺失值)不是删除缺失值,而是用一些数字填补。对于大部分目的,fillna是可以用的。调用fillna的时候设置好一个常用用来替换缺失值:df.fillna(0)给fillna传入一个dict,可以给不同列替换不同的值:df.fillna({1: 0.5, 2: 0})fillna返回一个新对象,但你可以使用in-place来直接更改原有的数据:_ = df.fillna(0, inplace=True) df在使用fillna的时候,这种插入法同样能用于reindexing:df = pd.DataFrame(np.random.randn(6, 3)) dfdf.iloc[2:, 1] = NA dfdf.iloc[4:, 2] = NA dfdf.fillna(method='ffill')df.fillna(method='ffill', limit=2)使用fillna可以我们做一些颇有创造力的事情。比如,可以传入一个series的平均值或中位数:data = pd.Series([1., NA, 3.5, NA, 7]) data.fillna(data.mean())0 1.000000 1 3.833333 2 3.500000 3 3.833333 4 7.000000 dtype: float64
0
0
0
浏览量830
清晨我上码

Interfacing Between pandas and Model Code pandas

13.1 Interfacing Between pandas and Model Code(pandas与建模代码间的交互)一个通常的工作流程中,在建模之前,会用pandas来加载数据并清理。模型开发过程中,一个很重要的部分就是特征工程(feature engineering),指的是通过数据变换或分析,从原始数据中提取出对建模有用的信息。之前介绍的聚合(aggregation)和GroupBy就经常用于特征工程。至于什么样才是好的特征工程,这就超出了本书的范围。这里会简单介绍如何在数据处理与建模之间切换。连接pandas和其他一些分析库的点,通常是Numpy数组。要想把一个DataFrame变为Numpy数组,使用.values属性:import numpy as np import pandas as pddata = pd.DataFrame({'x0': [1, 2, 3, 4, 5], 'x1': [0.01, -0.01, 0.25, -4.1, 0.], 'y': [-1.5, 0., 3.6, 1.3, -2.]}) datadata.columnsIndex(['x0', 'x1', 'y'], dtype='object')data.valuesarray([[ 1. , 0.01, -1.5 ], [ 2. , -0.01, 0. ], [ 3. , 0.25, 3.6 ], [ 4. , -4.1 , 1.3 ], [ 5. , 0. , -2. ]])变回DataFrame的方法是,传入一个二维ndarray,并指定列名:df2 = pd.DataFrame(data.values, columns=['one', 'two', 'three']) df2.values属性最好用于同质的数据,即数据类型都是数值型。如果有异质的数据,结果会变为python对象:df3 = data.copy()df3['strings'] = ['a', 'b', 'c', 'd', 'e'] df3df3.valuesarray([[1, 0.01, -1.5, 'a'], [2, -0.01, 0.0, 'b'], [3, 0.25, 3.6, 'c'], [4, -4.1, 1.3, 'd'], [5, 0.0, -2.0, 'e']], dtype=object)对于一些模型,我们可能希望使用列中的一部分数据。建议使用loc,然后用values进行索引:model_cols = ['x0', 'x1']data.loc[:, model_cols].valuesarray([[ 1. , 0.01], [ 2. , -0.01], [ 3. , 0.25], [ 4. , -4.1 ], [ 5. , 0. ]])一些库对于pandas的支持非常好:能自动把DataFrame转换为numpy,并把模型的参数名字作为输出的列名。对于其他的一些库,就必须要自己手动操作了。在第十二章里,我们学习了pandas的Categorical数据类型和pandas.get_dummies函数。假设我们的数据集中有一个非数值列:data['category'] = pd.Categorical(['a', 'b', 'a', 'a', 'b'], categories=['a', 'b']) data如果想要哑变量来代替category这一列,我们可以创建哑变量,去除category列,然后把结果合并起来:dummies = pd.get_dummies(data.category, prefix='category') dummiesdata_with_dummies = data.drop('category', axis=1).join(dummies) data_with_dummies
0
0
0
浏览量1139
清晨我上码

2012 Federal Election Commission Database 2012联邦选举

14.5 2012 Federal Election Commission Database(2012联邦选举委员会数据库)这个选举数据库对于政治宣传很有用。里面包含了支持者的名字,职业和雇主,地址,赞助金额。一个有意思的数据集是关于2012年美国总统选举的。这个数据集有150MB,名称为P00000001-ALL.csv。(译者:因为github对于单个文件的上传限额是100MB,所以这个数据集无法推送到github上,这里我是把数据集压缩成了ZIP格式,解压后能正常导入)。我们用pandas来加载这个数据集:import numpy as np import pandas as pdpd.options.display.max_rows = 10fec = pd.read_csv('../datasets/fec/P00000001-ALL.csv', low_memory=False) # 不设定low_memory=False的话会报错fec.info()<class 'pandas.core.frame.DataFrame'> RangeIndex: 1001731 entries, 0 to 1001730 Data columns (total 16 columns): cmte_id 1001731 non-null object cand_id 1001731 non-null object cand_nm 1001731 non-null object contbr_nm 1001731 non-null object contbr_city 1001712 non-null object contbr_st 1001727 non-null object contbr_zip 1001620 non-null object contbr_employer 988002 non-null object contbr_occupation 993301 non-null object contb_receipt_amt 1001731 non-null float64 contb_receipt_dt 1001731 non-null object receipt_desc 14166 non-null object memo_cd 92482 non-null object memo_text 97770 non-null object form_tp 1001731 non-null object file_num 1001731 non-null int64 dtypes: float64(1), int64(1), object(14) memory usage: 122.3+ MB拿出一份记录来看是什么样子:fec.iloc[123456]cmte_id C00431445 cand_id P80003338 cand_nm Obama, Barack contbr_nm ELLMAN, IRA contbr_city TEMPE ... receipt_desc NaN memo_cd NaN memo_text NaN form_tp SA17A file_num 772372 Name: 123456, Length: 16, dtype: object我们可以已经能想到如何切割、整理数据,来提取赞助者的统计数据,或提取宣传活动的模式,这里我们展示一些不同的分析方法。我们可以看到这里面没有政治党派(political party affiliations),所以我们最好添加这样的信息。我们可以使用unique得到所有候选人名单:unique_cands = fec.cand_nm.unique() unique_candsarray(['Bachmann, Michelle', 'Romney, Mitt', 'Obama, Barack', "Roemer, Charles E. 'Buddy' III", 'Pawlenty, Timothy', 'Johnson, Gary Earl', 'Paul, Ron', 'Santorum, Rick', 'Cain, Herman', 'Gingrich, Newt', 'McCotter, Thaddeus G', 'Huntsman, Jon', 'Perry, Rick'], dtype=object)unique_cands[2]'Obama, Barack'一种标记政党的方法是使用dict:parties = {'Bachmann, Michelle': 'Republican', 'Cain, Herman': 'Republican', 'Gingrich, Newt': 'Republican', 'Huntsman, Jon': 'Republican', 'Johnson, Gary Earl': 'Republican', 'McCotter, Thaddeus G': 'Republican', 'Obama, Barack': 'Democrat', 'Paul, Ron': 'Republican', 'Pawlenty, Timothy': 'Republican', 'Perry, Rick': 'Republican', "Roemer, Charles E. 'Buddy' III": 'Republican', 'Romney, Mitt': 'Republican', 'Santorum, Rick': 'Republican'}用map方法,把这个映射应用到Series对象上,我们可以用候选者名字得到一个政党的数组:fec.cand_nm[123456:123461]123456 Obama, Barack 123457 Obama, Barack 123458 Obama, Barack 123459 Obama, Barack 123460 Obama, Barack Name: cand_nm, dtype: objectfec.cand_nm[123456:123461].map(parties)123456 Democrat 123457 Democrat 123458 Democrat 123459 Democrat 123460 Democrat Name: cand_nm, dtype: object# Add it as a column fec['party'] = fec.cand_nm.map(parties)fec['party'].value_counts()Democrat 593746 Republican 407985 Name: party, dtype: int64一些需要注意的地方。首先,这个数据集中包含捐款和退款(退款表示把捐赠的钱退还给捐助者,用负数表示):(fec.contb_receipt_amt > 0).value_counts()True 991475 False 10256 Name: contb_receipt_amt, dtype: int64为了简化之后的分析过程,这里我们只取捐款的数据,不考虑退款的数据:fec = fec[fec.contb_receipt_amt > 0]因为Barack Obama和Mitt Romney是两个最主要的候选者,所以这里我们单独准备一个子集来包含二人的数据:fec_mrbo = fec[fec.cand_nm.isin(['Obama, Barack', 'Romney, Mitt'])]1 Donation Statistics by Occupation and Employer(按职业与雇主划分的捐赠数据)职业与捐赠也是有关系的。例如,律师倾向于给民主党(Democrats)捐更多的钱,而企业主管(business executives)倾向给共和党(Republicans)捐更多的钱。首先,按职业计算捐赠总额:fec.contbr_occupation.value_counts()[:10]RETIRED 233990 INFORMATION REQUESTED 35107 ATTORNEY 34286 HOMEMAKER 29931 PHYSICIAN 23432 INFORMATION REQUESTED PER BEST EFFORTS 21138 ENGINEER 14334 TEACHER 13990 CONSULTANT 13273 PROFESSOR 12555 Name: contbr_occupation, dtype: int64可以看到有些职业是同一种工作类型,指代的是同一个东西。下面用一小段代码来清理一下,把一些职业映射为另一种;注意dict.get方法,它会无视没有映射关系的职业:occ_mapping = { 'INFORMATION REQUESTED PER BEST EFFORTS' : 'NOT PROVIDED', 'INFORMATION REQUESTED' : 'NOT PROVIDED', 'INFORMATION REQUESTED (BEST EFFORTS)' : 'NOT PROVIDED', 'C.E.O.': 'CEO' } # If no mapping provided, return x f = lambda x: occ_mapping.get(x, x) fec.contbr_occupation = fec.contbr_occupation.map(f)/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/pandas/core/generic.py:3110: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy self[name] = value译者:上面这个warning我觉得也有必要好好了解一下,这个网站做了很好的讲解,Understanding SettingwithCopyWarning in pandas。简单的说,pandas会在遇到Chained assignment的情况时,跳出这个SettingWithCopyWarning。Chained assignment指的是data[data.bidder == 'parakeet2004']['bidderrate'] = 100这样的情况,即把两个索引操作连在一起。data[data.bidder == ‘parakeet2004’][‘bidderrate’] = 100这两个连续操作是分别独立执行的,一个结束后,另一个才开始。第一个操作是一个访问(access)方法,这个方法会返回一个新的DataFrame。而第二个方法是一个赋值(assignment)方法,这个方法是作用在新产生的DataFrame上,所以这里的100是赋给了新的DataFrame,原始的DataFrame并没有被更改。所以为了防止这样的情况出现,pandas会发出SettingWithCopyWarning,让我们检查一下有没有出错。那就检查一下呗,下面输出fec.contbr_occupation,发现映射关系被正常执行了,所以这里的warning我们可以忽略:fec.contbr_occupation0 RETIRED 1 RETIRED 2 NOT PROVIDED 3 RETIRED 4 RETIRED ... 1001726 NOT PROVIDED 1001727 BUSINESS OWNER 1001728 NOT PROVIDED 1001729 LONGWALL MAINTENANCE FOREMAN 1001730 NOT PROVIDED Name: contbr_occupation, Length: 991475, dtype: object对雇主也做同样的映射处理:emp_mapping = { 'INFORMATION REQUESTED PER BEST EFFORTS' : 'NOT PROVIDED', 'INFORMATION REQUESTED' : 'NOT PROVIDED', 'SELF' : 'SELF-EMPLOYED', 'SELF EMPLOYED' : 'SELF-EMPLOYED', } # If no mapping provided, return x f = lambda x: emp_mapping.get(x, x) fec.contbr_employer = fec.contbr_employer.map(f)/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/pandas/core/generic.py:3110: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy self[name] = valuefec.contbr_employer0 RETIRED 1 RETIRED 2 NOT PROVIDED 3 NONE 4 NONE ... 1001726 NOT PROVIDED 1001727 DUFFY EQUIPMENT COMPANY INC. 1001728 NOT PROVIDED 1001729 T.A.C.C. 1001730 NOT PROVIDED Name: contbr_employer, Length: 991475, dtype: object现在,我们可以用pivot_table,按政党和职业对数据进行聚合,然后过滤下得到捐赠额大于两百万美元以上的职业:by_occupation = fec.pivot_table('contb_receipt_amt', index='contbr_occupation', columns='party', aggfunc='sum')over_2mm = by_occupation[by_occupation.sum(1) > 2000000] over_2mm17 rows × 2 columns画出柱状图,barh表示水平柱状图:import seaborn as sns %matplotlib inlineover_2mm.plot(kind='barh', figsize=(10, 8))我们可以能对那些捐款给Obama和Romney的顶级捐助者职业,或顶级捐助公司感兴趣。想要得到这些信息的话,可以按候选者名字进行分组,然后使用top方法的一个变形:def get_top_amounts(group, key, n=5): totals = group.groupby(key)['contb_receipt_amt'].sum() return totals.nlargest(n)然后按职业和雇主聚合:grouped = fec_mrbo.groupby('cand_nm') grouped<pandas.core.groupby.DataFrameGroupBy object at 0x12bed7dd8>grouped.apply(get_top_amounts, 'contbr_occupation', n=7)cand_nm contbr_occupation Obama, Barack RETIRED 25305116.38 ATTORNEY 11141982.97 INFORMATION REQUESTED 4866973.96 HOMEMAKER 4248875.80 PHYSICIAN 3735124.94 ... Romney, Mitt HOMEMAKER 8147446.22 ATTORNEY 5364718.82 PRESIDENT 2491244.89 EXECUTIVE 2300947.03 C.E.O. 1968386.11 Name: contb_receipt_amt, Length: 14, dtype: float64grouped.apply(get_top_amounts, 'contbr_employer', n=10)cand_nm contbr_employer Obama, Barack RETIRED 22694358.85 SELF-EMPLOYED 17080985.96 NOT EMPLOYED 8586308.70 INFORMATION REQUESTED 5053480.37 HOMEMAKER 2605408.54 ... Romney, Mitt CREDIT SUISSE 281150.00 MORGAN STANLEY 267266.00 GOLDMAN SACH & CO. 238250.00 BARCLAYS CAPITAL 162750.00 H.I.G. CAPITAL 139500.00 Name: contb_receipt_amt, Length: 20, dtype: float642 Bucketing Donation Amounts(桶捐赠额)一个分析这种数据的有用方法是使用cut函数,把捐赠额去中心化,按捐赠额大小分为多个桶:bins = np.array([0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000])labels = pd.cut(fec_mrbo.contb_receipt_amt, bins) labels411 (10, 100] 412 (100, 1000] 413 (100, 1000] 414 (10, 100] 415 (10, 100] ... 701381 (10, 100] 701382 (100, 1000] 701383 (1, 10] 701384 (10, 100] 701385 (100, 1000] Name: contb_receipt_amt, Length: 694282, dtype: category Categories (8, interval[int64]): [(0, 1] < (1, 10] < (10, 100] < (100, 1000] < (1000, 10000] < (10000, 100000] < (100000, 1000000] < (1000000, 10000000]]我们可以按二人的名字和箱标签(bin label)分组,得到按不同捐赠额的直方图:grouped = fec_mrbo.groupby(['cand_nm', labels]) grouped.size().unstack(0)这份数据说明Obama收到的小额捐助是远超Romney的。我们也可以对每一个箱进行归一化,然后得到百分比数据:bucket_sums = grouped.contb_receipt_amt.sum().unstack(0) bucket_sumsnormed_sums = bucket_sums.div(bucket_sums.sum(axis=1), axis=0) normed_sumsd_nm Obama, Barack Romney, Mittnormed_sums[:-2].plot(kind='barh', figsize=(10, 8))这里没有包含捐款数额最大的两个bins,因为没有Romney这边没有捐款。3 Donation Statistics by State(按州划分的捐赠数据)按州划分也是一个比较常见的方法:grouped = fec_mrbo.groupby(['cand_nm', 'contbr_st'])totals = grouped.contb_receipt_amt.sum().unstack(0).fillna(0) totals67 rows × 2 columnstotals = totals[totals.sum(1) > 100000] totals52 rows × 2 columns还可以得到每个州,两个候选人获得金额的百分比:percent = totals.div(totals.sum(1), axis=0) percent[:10]
0
0
0
浏览量1351
清晨我上码

pandas教程:GroupBy Mechanics 分组机制

Chapter 10 Data Aggregation and Group Operations(数据汇总和组操作)这一章的内容:把一个pandas对象(series或DataFrame)按key分解为多个计算组的汇总统计值(group summary statistics),比如计数,平均值,标准差,或用户自己定义的函数应用组内的转换或其他一些操作,比如标准化,线性回归,排序,子集选择计算透视表和交叉列表进行分位数分析和其他一些统计组分析10.1 GroupBy Mechanics(分组机制)Hadley Wickham,是很多R语言有名库的作者,他描述group operation(组操作)为split-apply-combine(分割-应用-结合)。第一个阶段,存储于series或DataFrame中的数据,根据不同的keys会被split(分割)为多个组。而且分割的操作是在一个特定的axis(轴)上。例如,DataFrame能按行(axis=0)或列(axis=1)来分组。之后,我们可以把函数apply(应用)在每一个组上,产生一个新的值。最后,所以函数产生的结果被combine(结合)为一个结果对象(result object)。下面是一个图示:每一个用于分组的key能有很多形式,而且keys也不必都是一种类型:含有值的list或array的长度,与按axis分组后的长度是一样的值的名字指明的是DataFrame中的列名一个dict或Series,给出一个对应关系,用于对应按轴分组后的值与组的名字能在axis index(轴索引)上被调用的函数,或index上的labels(标签)注意后面三种方法都是用于产生一个数组的快捷方式,而这个数组责备用来分割对象(split up the object)。不用担心这些很抽象,这一章会有很多例子来帮助我们理解这些方法。先从一个例子来开始吧,这里有一个用DataFrame表示的表格型数据集:import numpy as np import pandas as pddf = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'], 'key2' : ['one', 'two', 'one', 'two', 'one'], 'data1' : np.random.randn(5), 'data2' : np.random.randn(5)}) df假设我们想要,通过使用key1作为labels,来计算data1列的平均值。有很多方法可以做到这点,一种是访问data1,并且使用列(a series)在key1上,调用groupby。(译者:其实就是按key1来进行分组,但只保留data1这一列):grouped = df['data1'].groupby(df['key1'])grouped<pandas.core.groupby.SeriesGroupBy object at 0x111db3710>这个grouped变量是一个GroupBy object(分组对象)。实际上现在还没有进行任何计算,除了调用group key(分组键)df['key1']时产生的一些中间数据。整个方法是这样的,这个GroupBy object(分组对象)已经有了我们想要的信息,现在需要的是对于每一个group(组)进行一些操作。例如,通过调用GroupBy的mean方法,我们可以计算每个组的平均值:grouped.mean()key1 a 0.599194 b -0.630067 Name: data1, dtype: float64之后我们会对于调用.mean()后究竟发生了什么进行更详细的解释。重要的是,我们通过group key(分组键)对数据(a series)进行了聚合,这产生了一个新的Series,而且这个series的索引是key1列中不同的值。得到的结果中,index(索引)也有’key1’,因为我们使用了df['key1']。如果我们传入多个数组作为一个list,那么我们会得到不同的东西:means = df['data1'].groupby([df['key1'], df['key2']]).mean() meanskey1 key2 a one 0.222108 two 1.353368 b one 0.253311 two -1.513444 Name: data1, dtype: float64这里我们用了两个key来分组,得到的结果series现在有一个多层级索引,这个多层索引是根据key1和key2不同的值来构建的:means.unstack()在上面的例子里,group key全都是series,即DataFrame中的一列,当然,group key只要长度正确,可以是任意的数组:states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio'])years = np.array([2005, 2005, 2006, 2005, 2006])df['data1'].groupby([states, years]).mean()California 2005 1.353368 2006 0.253311 Ohio 2005 -0.074456 2006 -0.920317 Name: data1, dtype: float64df['data1'].groupby([states, years])<pandas.core.groupby.SeriesGroupBy object at 0x112530e48>df['data1']0 1.364533 1 1.353368 2 0.253311 3 -1.513444 4 -0.920317 Name: data1, dtype: float64df其中分组信息经常就在我们处理的DataFrame中,在这种情况下,我们可以传入列名(可以是字符串,数字,或其他python对象)作为group keys:df.groupby('key1').mean()df.groupby(['key1', 'key2']).mean()我们注意到第一个例子里,df.groupby('key1').mean()的结果里并没有key2这一列。因为df['key2']这一列不是数值型数据,我们称这种列为nuisance column(有碍列),这种列不会出现在结果中。默认,所有的数值型列都会被汇总计算,但是出现有碍列的情况的话,就会过滤掉这种列。一个很有用的GroupBy方法是size,会返回一个包含group size(组大小)的series:df.groupby(['key1', 'key2']).size()key1 key2 a one 2 two 1 b one 1 two 1 dtype: int64另外一点需要注意的是,如果作为group key的列中有缺失值的话,也不会出现在结果中。1 Iterating Over Groups(对组进行迭代)GroupBy对象支持迭代,能产生一个2-tuple(二元元组),包含组名和对应的数据块。考虑下面的情况:for name, group in df.groupby('key1'): print(name) print(group)a data1 data2 key1 key2 0 1.364533 0.633262 a one 1 1.353368 0.361008 a two 4 -0.920317 2.037712 a one b data1 data2 key1 key2 2 0.253311 -1.107940 b one 3 -1.513444 -1.038035 b two对于有多个key的情况,元组中的第一个元素会被作为另一个元组的key值for (k1, k2), group in df.groupby(['key1', 'key2']): print((k1, k2)) print(group)('a', 'one') data1 data2 key1 key2 0 1.364533 0.633262 a one 4 -0.920317 2.037712 a one ('a', 'two') data1 data2 key1 key2 1 1.353368 0.361008 a two ('b', 'one') data1 data2 key1 key2 2 0.253311 -1.10794 b one ('b', 'two') data1 data2 key1 key2 3 -1.513444 -1.038035 b two当然,也可以对数据的一部分进行各种操作。一个便利的用法是,用一个含有数据片段(data pieces)的dict来作为单行指令(one-liner):pieces = dict(list(df.groupby('key1')))pieces{'a': data1 data2 key1 key2 0 1.364533 0.633262 a one 1 1.353368 0.361008 a two 4 -0.920317 2.037712 a one, 'b': data1 data2 key1 key2 2 0.253311 -1.107940 b one 3 -1.513444 -1.038035 b two}pieces['b']groupby默认作用于axis=0,但是我们可以指定任意的轴。例如,我们可以按dtype来对列进行分组:df.dtypesdata1 float64 data2 float64 key1 object key2 object dtype: objectgrouped = df.groupby(df.dtypes, axis=1)for dtype, group in grouped: print(dtype) print(group)float64 data1 data2 0 1.364533 0.633262 1 1.353368 0.361008 2 0.253311 -1.107940 3 -1.513444 -1.038035 4 -0.920317 2.037712 object key1 key2 0 a one 1 a two 2 b one 3 b two 4 a one2 Selecting a Column or Subset of Columns (选中一列,或列的子集)如果一个GroupBy对象是由DataFrame创建来的,那么通过列名或一个包含列名的数组来对GroupBy对象进行索引的话,就相当于对列取子集做聚合(column subsetting for aggregation)。这句话的意思是:df.groupby('key1')['data1'] df.groupby('key1')[['data2']]上面的代码其实就是下面的语法糖(Syntactic sugar):df['data1'].groupby(df['key1']) df[['data2']].groupby(df['key1'])法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。尤其是对于一些很大的数据集,这种用法可以聚集一部分列。例如,在处理一个数据集的时候,想要只计算data2列的平均值,并将结果返还为一个DataFrame,我们可以这样写:dfdf.groupby(['key1', 'key2'])[['data2']].mean()data2如果一个list或一个数组被传入,返回的对象是一个分组后的DataFrame,如果传入的只是单独一个列名,那么返回的是一个分组后的grouped:s_grouped = df.groupby(['key1', 'key2'])['data2'] s_grouped<pandas.core.groupby.SeriesGroupBy object at 0x1125309e8>s_grouped.mean()key1 key2 a one 1.335487 two 0.361008 b one -1.107940 two -1.038035 Name: data2, dtype: float643 Grouping with Dicts and Series(用Dicts与Series进行分组)分组信息可以不是数组的形式。考虑下面的例子:people = pd.DataFrame(np.random.randn(5, 5), columns=['a', 'b', 'c', 'd', 'e'], index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis'])people.iloc[2:3, [1, 2]] = np.nan # Add a few NA valuespeople假设我们有一个组,对应多个列,而且我们想要按组把这些列的和计算出来:mapping = {'a': 'red', 'b': 'red', 'c': 'blue', 'd': 'blue', 'e': 'red', 'f': 'orange'}现在,我们可以通过这个dict构建一个数组,然后传递给groupby,但其实我们可以直接传入dict(可以注意到key里有一个'f',这说明即使有,没有被用到的group key,也是ok的):by_column = people.groupby(mapping, axis=1)by_column.sum()这种用法同样适用于series,这种情况可以看作是固定大小的映射(fixed-size mapping):map_series = pd.Series(mapping) map_seriesa red b red c blue d blue e red f orange dtype: objectpeople.groupby(map_series, axis=1).count()4 Grouping with Functions(用函数进行分组)比起用dict或series定义映射关系,使用python的函数是更通用的方法。任何一个作为group key的函数,在每一个index value(索引值)上都会被调用一次,函数计算的结果在返回的结果中会被用做group name。更具体一点,考虑前一个部分的DataFrame,用人的名字作为索引值。假设我们想要按照名字的长度来分组;同时我们要计算字符串的长度,使用len函数会变得非常简单:people.groupby(len).sum() # len函数在每一个index(即名字)上被调用了混合不同的函数、数组,字典或series都不成问题,因为所有对象都会被转换为数组:key_list = ['one', 'one', 'one', 'two', 'two']people.groupby([len, key_list]).min()5 Grouping by Index Levels (按索引层级来分组)最后关于多层级索引数据集(hierarchically indexed dataset),一个很方便的用时是在聚集(aggregate)的时候,使用轴索引的层级(One of the levels of an axis index)。看下面的例子:columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JP', 'JP'], [1, 3, 5, 1, 3]], names=['cty', 'tenor']) columnsMultiIndex(levels=[['JP', 'US'], [1, 3, 5]], labels=[[1, 1, 1, 0, 0], [0, 1, 2, 0, 1]], names=['cty', 'tenor'])hier_df = pd.DataFrame(np.random.randn(4, 5), columns=columns) hier_df要想按层级分组,传入层级的数字或者名字,通过使用level关键字:hier_df.groupby(level='cty', axis=1).count()
0
0
0
浏览量1763
清晨我上码

pandas教程:Combining and Merging Datasets 合并数据集

8.2 Combining and Merging Datasets(合并数据集)pandas里有几种方法可以合并数据:pandas.merge 按一个或多个key把DataFrame中的行连接起来。这个和SQL或其他一些关系型数据库中的join操作相似。pandas.concat 在一个axis(轴)上,串联或堆叠(stack)多个对象。combine_first 实例方法(instance method)能拼接相互之间有重复的数据,并用一个对象里的值填满缺失值这里每一个都会给出一些例子。这些用法贯穿这本书。1 Database-Style DataFrame Joins(数据库风格的DataFrame Joins)Merge或join操作,能通过一个或多个key,把不同的数据集的行连接在一起。这种操作主要集中于关系型数据库。pandas中的merge函数是这种操作的主要切入点:import pandas as pd import numpy as npdf1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)}) df1df2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2': range(3)}) df2这个例子是many-to-one join(多个变为一个的结合);在df1中有标签为a和b的行,而df2中的key列,每一行只有对应的一个值。调用merge我们可以得到:pd.merge(df1, df2)这里我们并没有指定按哪一列来合并。如果我们没有指定,merge会用两个对象中都存在的列名作为key(键)。当然,最好还是清楚指定比较好:pd.merge(df1, df2, on='key')如果每一个对象中的列名不一会,我们可以分别指定:df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)}) df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'], 'data2': range(3)})pd.merge(df3, df4, left_on='lkey', right_on='rkey')我们可能注意到,在结果中并没有c和d。因为merge默认是inner join(内联结),结果中的key是交集的结果,或者在两个表格中都有的集合。其他一些可选项,比如left, right, outer。outer join(外联结)取key的合集,其实就是left join和right join同时应用的效果:pd.merge(df1, df2, how='outer')many-to-many(多对多)结合也被定义好了,不过可能不是那么直观。这里有一个例子:df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6)}) df1df2 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'], 'data2': range(5)}) df2pd.merge(df1, df2, on='key', how='left')many-to-many join是对行进行笛卡尔集运算。(两个集合X和Y的笛卡儿积(Cartesian product),又称直积,在集合论中表示为X × Y,是所有可能的有序对组成的集合。比如1到13是一个集合,四种花色是一个集合,二者的笛卡尔积就有52个元素)。这里在左侧的DataFrame中有三行含b,右边的DataFrame则有两行含b,于是结果是有六行含b。这个join方法只会让不相同的key值出现在最后的结果里:pd.merge(df1, df2, how='inner')用多个key来联结的话,用一个含有多个列名的list来指定:left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'], 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]}) right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'], 'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]})pd.merge(left, right, on=['key1', 'key2'], how='outer')哪一种key组合会出现在结果里取决于merge方法的选择,可以把多个key当做一个tuple组成的单一key(尽管实际上并不是这样)。注意:当我们讲列和列进行联结时,DataFrame中的index对象会被丢弃。最后一个问题是在做merge操作的时候,如何处理重叠的列名。当我们想要手动去解决重叠问题时(参考重命名axis labels的部分),merge有一个suffixes选项,能让我们指定字符串,添加重叠的列名到左、右DataFrame:pd.merge(left, right, on='key1')pd.merge(left, right, on='key1', suffixes=('_left', '_right'))2 Merging on Index(在index上做合并)在一些情况下,用于合并的key(键),可能是DataFrame中的index。这种情况下,可以使用left_index=True 或 right_index=True来指明,哪一个index被用来作为合并键:left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'], 'value': range(6)}) left1right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b']) right1pd.merge(left1, right1, left_on='key', right_index=True)merge的默认方法是用key的交集,我们也可以设定用合集,即outer join:pd.merge(left1, right1, left_on='key', right_index=True, how='outer')对于那些有多层级索引的数据,就更复杂了。index上的merge默认会是multiple-key merge(复数键合并):lefth = pd.DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'], 'key2': [2000, 2001, 2002, 2001, 2002], 'data': np.arange(5.)}) lefthrighth = pd.DataFrame(np.arange(12).reshape((6, 2)), index=[['Nevada', 'Nevada', 'Ohio', 'Ohio', 'Ohio', 'Ohio'], [2001, 2000, 2000, 2000, 2001, 2002]], columns=['event1', 'event2']) righth在这个例子里,我们必须指明将多列合并作为一个list(注意处理重复index的方法是令how='outer'):pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True, how='outer')同时使用两个对象里的index来合并也是可能的:left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]], index=['a', 'c', 'e'], columns=['Ohio', 'Nevada']) left2right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]], index=['b', 'c', 'd', 'e'], columns=['Missouri', 'Alabama']) right2pd.merge(left2, right2, how='outer', left_index=True, right_index=True)DataFrame有一个很便利的join实例,可以直接用index来合并。这个也可以用于与其他DataFrame进行合并,要有一样的index但不能由重叠的列:left2.join(right2, how='outer')由于一些历史原因,在早期的pandas版本中,DataFrame的join方法是在结合键上做left join(左联结),这样会保留左侧Dataframe的行索引。这也支持把传入的dataframe的index与被调用的DataFrame的column联结在一起:left1.join(right1, on='key')最后,对于简单的index-on-index合并,可以直接给join传入一个DataFrame。(作为备选,也可以使用最普遍的concat函数,这个在下一节会做介绍):another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]], index=['a', 'c', 'e', 'f'], columns=['New York', 'Oregon']) anotherleft2.join([right2, another])left2.join([right2, another], how='outer')2 Concatenating Along an Axis(沿着轴连接)另一种结合方式被称为可互换的,比如concatenation, binding, or stacking(连接,绑定,堆叠)。Numpy中的concatenate函数可以作用于numpy 数组:arr = np.arange(12.).reshape((3, 4)) arrarray([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.]])np.concatenate([arr, arr], axis=1)array([[ 0., 1., 2., 3., 0., 1., 2., 3.], [ 4., 5., 6., 7., 4., 5., 6., 7.], [ 8., 9., 10., 11., 8., 9., 10., 11.]])而在pandas的对象中,比如Series和DataFrame,labeled axes(便签化的轴)能让我们做更泛化的数组连接操作。不过我们可能会有下面一些疑问:如果一个对象在其他轴上的index不同,我们应不应该在这些轴上把不同的元素连接起来,或者只用交集?经过连接操作后,连接的部分在输出对象里应不应该是可被识别的?concatenation axis(连接轴)含有的数据需要被保留吗?在很多情况下,DataFrame中一些用整数做的label(标签)其实最好在连接后被删除。pandas中的concat函数能解决上面这些问题。这里会给出几个例子来说明。假设我们有三个Series,他们指明没有index overlap(索引重叠):s1 = pd.Series([0, 1], index=['a', 'b']) s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e']) s3 = pd.Series([5, 6], index=['f', 'g'])调用concat,把上面的series放在一个list里,结果会把值和索引都整合在一起:pd.concat([s1, s2, s3])a 0 b 1 c 2 d 3 e 4 f 5 g 6 dtype: int64默认情况下,concat中axis=0,结果会得到一个新的而series。如果令axis=1, 结果会变成一个DataFrame(axis=1 是列):pd.concat([s1, s2, s3], axis=1)这种情况下,不会与其他轴产生重叠,效果与join中的outer join一样。你也可以通过设定join='inner'来使用交集:s4 = pd.concat([s1, s3]) s4a 0 b 1 f 5 g 6 dtype: int64pd.concat([s1, s4], axis=1)pd.concat([s1, s4], axis=1, join='inner')因为join='inner',所以f和g标签消失了。你也可以在join_axes中指定使用哪些轴:pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'b', 'e']])一个潜在的问题是连接的部分在结果里是不可辨识的。假设我们想在连接轴上创建一个多层级索引,我们需要用到keys参数:result = pd.concat([s1, s1, s3], keys=['one', 'two', 'three']) resultone a 0 b 1 two a 0 b 1 three f 5 g 6 dtype: int64result.unstack()如果是设定axis=1,那么keys会变为DataFrame的column header(列头):print(s1) print(s2) print(s3)a 0 b 1 dtype: int64 c 2 d 3 e 4 dtype: int64 f 5 g 6 dtype: int64pd.concat([s1, s2, s3], axis=1, keys=['one', 'two', 'three'])这种逻辑也可以扩展到DataFrame对象上:df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'], columns=['one', 'two']) df1df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'], columns=['three', 'four']) df2pd.concat([df1, df2], axis=1, keys=['level1', 'level2'])如果导入一个dict而不是list,那么dict的key会被用于上面concat中的keys选项:pd.concat({'level1': df1, 'level2': df2}, axis=1)还有其他一些选项负责多层级索引的设定(表8-3)。比如,可以给创建的axis level(轴层级)用names参数来命名:pd.concat([df1, df2], axis=1, keys=['level1', 'level2'], names=['upper', 'lower'])最后我们关心的是,在DataFrame中,行索引(row index)没有包含相关的数据:df1 = pd.DataFrame(np.random.randn(3, 4), columns=['a', 'b', 'c', 'd']) df1df2 = pd.DataFrame(np.random.randn(2, 3), columns=['b', 'd', 'a']) df2这种情况下,可以设置ignore_index=True:pd.concat([df1, df2], ignore_index=True)3 Combining Data with Overlap另一种数据结合方法既不属于merge,也不属于concatenation。比如两个数据集,index可能完全覆盖,或覆盖一部分。这里举个例子,考虑下numpy的where函数,可以在数组上进行类似于if-else表达式般的判断:a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan], index=['f', 'e', 'd', 'c', 'b', 'a']) af NaN e 2.5 d NaN c 3.5 b 4.5 a NaN dtype: float64b = pd.Series(np.arange(len(a), dtype=np.float64), index=['f', 'e', 'd', 'c', 'b', 'a']) bf 0.0 e 1.0 d 2.0 c 3.0 b 4.0 a 5.0 dtype: float64np.where(pd.isnull(a), b, a)array([ 0. , 2.5, 2. , 3.5, 4.5, 5. ])Series有一个combine_first方法,效果和上面是一样,而且还会自动对齐(比如把index按字母进行排列):b[:-2].combine_first(a[2:])a NaN b 4.5 c 3.0 d 2.0 e 1.0 f 0.0 dtype: float64对于DataFrame, combine_first可以在列与列之间做到同样的事情,可以认为是用传递的对象,给调用对象中的缺失值打补丁:df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan], 'b': [np.nan, 2., np.nan, 6.], 'c': range(2, 18, 4)}) df1df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.], 'b': [np.nan, 3., 4., 6., 8.]}) df2df1.combine_first(df2)
0
0
0
浏览量1159
清晨我上码

pandas教程:Introduction to statsmodels statsmodels简介

13.3 Introduction to statsmodels(statsmodels简介)statsmodels是一个有很多统计模型的python库,能完成很多统计测试,数据探索以及可视化。它也包含一些经典的统计方法,比如贝叶斯方法和一个机器学习的模型。statsmodels中的模型包括:线性模型(linear models),广义线性模型(generalized linear models),鲁棒线性模型(robust linear models)线性混合效应模型(Linear mixed effects models)方差分析(ANOVA)方法(Analysis of variance (ANOVA) methods)时间序列处理(Time series processes)和状态空间模型(state space models)广义矩估计方法(Generalized method of moments)接下来我们用一些statsmodels中的工具,并了解如何使用Patsy公式和pandas DataFrame进行建模。1 Estimating Linear Models(估计线性模型)statsmodels中的线性模型大致分为两种:基于数组的(array-based),和基于公式的(formula-based)。调用的模块为:import statsmodels.api as sm import statsmodels.formula.api as smf为了演示如何使用,我们对一些随机数据生成一个线性模型:import numpy as np import pandas as pddef dnorm(mean, variance, size=1): if isinstance(size, int): size = size return mean + np.sqrt(variance) * np.random.randn(size) # For reproducibility np.random.seed(12345) N = 100 X = np.c_[dnorm(0, 0.4, size=N), dnorm(0, 0.6, size=N), dnorm(0, 0.2, size=N)] eps = dnorm(0, 0.1, size=N) beta = [0.1, 0.3, 0.5] y = np.dot(X, beta) + epsprint(X.shape) print(eps.shape)(100, 3) (100,)真正的模型用的参数是beta,dnorm的功能是产生指定平均值和方差的随机离散数据,得到:X[:5]array([[-0.12946849, -1.21275292, 0.50422488], [ 0.30291036, -0.43574176, -0.25417986], [-0.32852189, -0.02530153, 0.13835097], [-0.35147471, -0.71960511, -0.25821463], [ 1.2432688 , -0.37379916, -0.52262905]])y[:5]array([ 0.42786349, -0.67348041, -0.09087764, -0.48949442, -0.12894109])一个线性模型通常会有一个截距,这里我们用sm.add_constant函数添加一个截距列给X:X_model = sm.add_constant(X) X_model[:5]array([[ 1. , -0.12946849, -1.21275292, 0.50422488], [ 1. , 0.30291036, -0.43574176, -0.25417986], [ 1. , -0.32852189, -0.02530153, 0.13835097], [ 1. , -0.35147471, -0.71960511, -0.25821463], [ 1. , 1.2432688 , -0.37379916, -0.52262905]])sm.OLS可以拟合(fit)普通最小二乘线性回归:model = sm.OLS(y, X)fit方法返回的是一个回顾结果对象,包含预测模型的参数和其他一些诊断数据:results = model.fit() results.paramsarray([ 0.17826108, 0.22303962, 0.50095093])在results上调用summary方法,可能得到一些详细的诊断数据:results.summary()参数的名字通常为x1, x2,以此类推。假设所有的模型参数都在一个DataFrame里:data = pd.DataFrame(X, columns=['col0', 'col1', 'col2']) data['y'] = ydata.head()现在我们可以使用statsmodels formula API(公式API)和Patsy的公式字符串:results = smf.ols('y ~ col0 + col1 + col2', data=data).fit() results.paramsIntercept 0.033559 col0 0.176149 col1 0.224826 col2 0.514808 dtype: float64results.tvaluesIntercept 0.952188 col0 3.319754 col1 4.850730 col2 6.303971 dtype: float64可以看到statsmodel返回的结果是Series,而Series的索引部分是DataFrame的列名。当我们使用公式和pandas对象的时候,不需要使用add_constant。如果得到新的数据,我们可以用预测模型的参数来进行预测:results.predict(data[:5])0 -0.002327 1 -0.141904 2 0.041226 3 -0.323070 4 -0.100535 dtype: float64其他一些分析、诊断、可视化工具可以自己多尝试。2 Estimating Time Series Processes(预测时序过程)statsmodels中的另一个类是用于时间序列分析的,其中有自动回归处理(autoregressive processes), 卡尔曼滤波(Kalman filtering),状态空间模型(state space models),多元回归模型(multivariate autoregressive models)。让我们用自动回归结果和噪音模拟一个时间序列数据:init_x = 4 import random values = [init_x, init_x] N = 1000 b0 = 0.8 b1 = -0.4 noise = dnorm(0, 0.1, N) for i in range(N): new_x = values[-1] * b0 + values[-2] * b1 + noise[i] values.append(new_x)values[:6][4, 4, 1.8977509636904242, 0.086865262206104243, -0.57694691325353353, -0.49950238023089472]这种数据有AR(2)结构(two lags,延迟两期),延迟参数是0.8和-0.4。当我们拟合一个AR模型,我们可能不知道延迟的期间是多少,所以可以在拟合时设一个比较大的延迟数字:MAXLAGS = 5 model = sm.tsa.AR(values) results = model.fit(MAXLAGS)结果里的预测参数,第一个是解决,之后是两个延迟(lags):results.paramsarray([-0.00616093, 0.78446347, -0.40847891, -0.01364148, 0.01496872, 0.01429462])
0
0
0
浏览量1721
清晨我上码

Introduction to pandas Data Structures pandas的数据结构

Chapter 5 Getting Started with pandas这样导入pandas:import pandas as pde:\python3.7\lib\site-packages\numpy\_distributor_init.py:32: UserWarning: loaded more than 1 DLL from .libs: e:\python3.7\lib\site-packages\numpy\.libs\libopenblas.TXA6YQSD3GCQQC22GEQ54J2UDCXDXHWN.gfortran-win_amd64.dll e:\python3.7\lib\site-packages\numpy\.libs\libopenblas.XWYDX2IKJW2NMTWSFYNGFUWKQU3LYTCZ.gfortran-win_amd64.dll stacklevel=1)另外可以导入Series和DataFrame,因为这两个经常被用到:from pandas import Series, DataFrame5.1 Introduction to pandas Data Structures数据结构其实就是Series和DataFrame。1 Series这里series我就不翻译成序列了,因为之前的所有笔记里,我都是把sequence翻译成序列的。series是一个像数组一样的一维序列,并伴有一个数组表示label,叫做index。创建一个series的方法也很简单:obj = pd.Series([4, 7, -5, 3]) obj0 4 1 7 2 -5 3 3 dtype: int64可以看到,左边表示index,右边表示对应的value。可以通过value和index属性查看:obj.valuesarray([ 4, 7, -5, 3], dtype=int64)obj.index # like range(4)RangeIndex(start=0, stop=4, step=1)当然我们也可以自己指定index的label:obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])obj2d 4 b 7 a -5 c 3 dtype: int64obj2.indexIndex(['d', 'b', 'a', 'c'], dtype='object')可以用index的label来选择:obj2['a']-5obj2['d'] = 6obj2[['c', 'a', 'd']]c 3 a -5 d 6 dtype: int64这里[‘c’, ‘a’, ‘d’]其实被当做了索引,尽管这个索引是用string构成的。使用numpy函数或类似的操作,会保留index-value的关系:obj2[obj2 > 0]d 6 b 7 c 3 dtype: int64obj2 * 2d 12 b 14 a -10 c 6 dtype: int64import numpy as np np.exp(obj2)d 403.428793 b 1096.633158 a 0.006738 c 20.085537 dtype: float64另一种看待series的方法,它是一个长度固定,有顺序的dict,从index映射到value。在很多场景下,可以当做dict来用:'b' in obj2True'e' in obj2False还可以直接用现有的dict来创建series:sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon':16000, 'Utah': 5000}obj3 = pd.Series(sdata) obj3Ohio 35000 Texas 71000 Oregon 16000 Utah 5000 dtype: int64series中的index其实就是dict中排好序的keys。我们也可以传入一个自己想要的顺序:states = ['California', 'Ohio', 'Oregon', 'Texas']obj4 = pd.Series(sdata, index=states) obj4California NaN Ohio 35000.0 Oregon 16000.0 Texas 71000.0 dtype: float64顺序是按states里来的,但因为没有找到california,所以是NaN。NaN表示缺失数据,用之后我们提到的话就用missing或NA来指代。pandas中的isnull和notnull函数可以用来检测缺失数据:pd.isnull(obj4)California True Ohio False Oregon False Texas False dtype: boolpd.notnull(obj4)California False Ohio True Oregon True Texas True dtype: boolseries也有对应的方法:obj4.isnull()California True Ohio False Oregon False Texas False dtype: bool关于缺失数据,在第七章还会讲得更详细一些。series中一个有用的特色自动按index label来排序(Data alignment features):obj3Ohio 35000 Texas 71000 Oregon 16000 Utah 5000 dtype: int64obj4California NaN Ohio 35000.0 Oregon 16000.0 Texas 71000.0 dtype: float64obj3 + obj4California NaN Ohio 70000.0 Oregon 32000.0 Texas 142000.0 Utah NaN dtype: float64这个Data alignment features(数据对齐特色)和数据库中的join相似。series自身和它的index都有一个叫name的属性,这个能和其他pandas的函数进行整合:obj4.name = 'population'obj4.index.name = 'state'obj4state California NaN Ohio 35000.0 Oregon 16000.0 Texas 71000.0 Name: population, dtype: float64series的index能被直接更改:obj0 4 1 7 2 -5 3 3 dtype: int64obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan'] objBob 4 Steve 7 Jeff -5 Ryan 3 dtype: int642 DataFrameDataFrame表示一个长方形表格,并包含排好序的列,每一列都可以是不同的数值类型(数字,字符串,布尔值)。DataFrame有行索引和列索引(row index, column index);可以看做是分享所有索引的由series组成的字典。数据是保存在一维以上的区块里的。(其实我是把dataframe当做excel里的那种表格来用的,这样感觉更直观一些)构建一个dataframe的方法,用一个dcit,dict里的值是list:data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002, 2003], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]} frame = pd.DataFrame(data) framedataframe也会像series一样,自动给数据赋index, 而列则会按顺序排好。对于一个较大的DataFrame,用head方法会返回前5行(注:这个函数在数据分析中经常使用,用来查看表格里有什么东西):frame.head()如果指定一列的话,会自动按列排序:pd.DataFrame(data, columns=['year', 'state', 'pop'])如果你导入一个不存在的列名,那么会显示为缺失数据:frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'], index=['one', 'two', 'three', 'four', 'five', 'six'])frame2frame2.columnsIndex(['year', 'state', 'pop', 'debt'], dtype='object')从DataFrame里提取一列的话会返回series格式,可以以属性或是dict一样的形式来提取:frame2['state']one Ohio two Ohio three Ohio four Nevada five Nevada six Nevada Name: state, dtype: objectframe2.yearone 2000 two 2001 three 2002 four 2001 five 2002 six 2003 Name: year, dtype: int64注意:frame2[column]能应对任何列名,但frame2.column的情况下,列名必须是有效的python变量名才行。返回的series有DataFrame种同样的index,而且name属性也是对应的。对于行,要用在loc属性里用 位置或名字:frame2.loc['three']year 2002 state Ohio pop 3.6 debt NaN Name: three, dtype: object列值也能通过赋值改变。比如给debt赋值:frame2['debt'] = 16.5 frame2frame2['debt'] = np.arange(6.) frame2如果把list或array赋给column的话,长度必须符合DataFrame的长度。如果把一二series赋给DataFrame,会按DataFrame的index来赋值,不够的地方用缺失数据来表示:val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five']) frame2['debt'] = val frame2如果列不存在,赋值会创建一个新列。而del也能像删除字典关键字一样,删除列:frame2['eastern'] = frame2.state == 'Ohio' frame2然后用del删除这一列:del frame2['eastern']frame2.columnsIndex(['year', 'state', 'pop', 'debt'], dtype='object')注意:columns返回的是一个view,而不是新建了一个copy。因此,任何对series的改变,会反映在DataFrame上。除非我们用copy方法来新建一个。另一种常见的格式是dict中的dict:pop = {'Nevada': {2001: 2.4, 2002: 2.9}, 'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}把上面这种嵌套dict传给DataFrame,pandas会把外层dict的key当做列,内层key当做行索引:frame3 = pd.DataFrame(pop) frame3另外DataFrame也可以向numpy数组一样做转置:frame3.T指定index:pd.DataFrame(pop, index=[2001, 2002, 2003])series组成的dict:pdata = {'Ohio': frame3['Ohio'][:-1], 'Nevada': frame3['Nevada'][:2]}pd.DataFrame(pdata)如果DataFrame的index和column有自己的name属性,也会被显示:frame3.index.name = 'year'; frame3.columns.name = 'state'frame3values属性会返回二维数组:frame3.valuesarray([[ nan, 1.5], [ 2.4, 1.7], [ 2.9, 3.6]])如果column有不同的类型,dtype会适应所有的列:frame2.valuesarray([[2000, 'Ohio', 1.5, nan], [2001, 'Ohio', 1.7, -1.2], [2002, 'Ohio', 3.6, nan], [2001, 'Nevada', 2.4, -1.5], [2002, 'Nevada', 2.9, -1.7], [2003, 'Nevada', 3.2, nan]], dtype=object)3 Index Objects (索引对象)pandas的Index Objects (索引对象)负责保存axis labels和其他一些数据(比如axis name或names)。一个数组或其他一个序列标签,只要被用来做构建series或DataFrame,就会被自动转变为index:obj = pd.Series(range(3), index=['a', 'b', 'c'])index = obj.index indexIndex(['a', 'b', 'c'], dtype='object')index[1:]Index(['b', 'c'], dtype='object')index object是不可更改的:index[1] = 'd'--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-67-676fdeb26a68> in <module>() ----> 1 index[1] = 'd' /Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/pandas/indexes/base.py in __setitem__(self, key, value) 1243 1244 def __setitem__(self, key, value): -> 1245 raise TypeError("Index does not support mutable operations") 1246 1247 def __getitem__(self, key): TypeError: Index does not support mutable operations正因为不可修改,所以data structure中分享index object是很安全的:labels = pd.Index(np.arange(3)) labelsInt64Index([0, 1, 2], dtype='int64')obj2 = pd.Series([1.5, -2.5, 0], index=labels) obj20 1.5 1 -2.5 2 0.0 dtype: float64obj2.index is labelsTrueindex除了想数组,还能像大小一定的set:frame3frame3.columnsIndex(['Nevada', 'Ohio'], dtype='object', name='state')'Ohio' in frame3.columnsTrue2003 in frame3.columnsFalse与python里的set不同,pandas的index可以有重复的labels:dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar']) dup_labelsIndex(['foo', 'foo', 'bar', 'bar'], dtype='object')
0
0
0
浏览量212
清晨我上码

pandas教程:Advanced GroupBy Use 高级GroupBy用法

12.2 Advanced GroupBy Use(高级GroupBy用法)我们已经在第十章讨论了groupby的一些用法,这里还有一些技巧可能会用得到。1 Group Transforms and “Unwrapped” GroupBys(组变换和无包装的GroupBy)在第十章里,使用apply方法在组上进行转换操作的。还有一个内建的方法叫transform,和apply相同,但是在一些函数的用法上有一些限制:可以产生一个标量,将数据广播(broadcast)到与组一样的形状(这里的broadcast可以理解为改变数据形状的方法,感兴趣的可以直接搜索 numpy broadcast)可以产生一个和输入的组一样形状的对象不能对输入进行改变举个例子:import numpy as np import pandas as pddf = pd.DataFrame({'key': ['a', 'b', 'c'] * 4, 'value': np.arange(12.)}) df通过key来计算组的平均值:g = df.groupby('key').value g.mean()key a 4.5 b 5.5 c 6.5 Name: value, dtype: float64假设我们想要产生一个和df['value']一样大小的Series,不过要用key分组后的平均值来替换。我们可以把函数lambda x: x.mean()给transform:g.transform(lambda x: x.mean())0 4.5 1 5.5 2 6.5 3 4.5 4 5.5 5 6.5 6 4.5 7 5.5 8 6.5 9 4.5 10 5.5 11 6.5 Name: value, dtype: float64对于内建的聚合函数,我们可以传入一个字符串别名,就像使用groupby agg方法的时候一样:g.transform('mean')0 4.5 1 5.5 2 6.5 3 4.5 4 5.5 5 6.5 6 4.5 7 5.5 8 6.5 9 4.5 10 5.5 11 6.5 Name: value, dtype: float64就像apply,transform能用那些返回Series的函数,但是结果的大小和输入的必须一样。例如,我们通过一个lambda函数令每个小组都乘2:g.transform(lambda x: x * 2)0 0.0 1 2.0 2 4.0 3 6.0 4 8.0 5 10.0 6 12.0 7 14.0 8 16.0 9 18.0 10 20.0 11 22.0 Name: value, dtype: float64一个更复杂的例子,我们可以按降序来计算每一个组:g.transform(lambda x: x.rank(ascending=False))0 4.0 1 4.0 2 4.0 3 3.0 4 3.0 5 3.0 6 2.0 7 2.0 8 2.0 9 1.0 10 1.0 11 1.0 Name: value, dtype: float64考虑一个包含简单聚合的分组转换函数:def normalize(x): return (x - x.mean()) / x.std()使用transform或apply,都能得到一样的结果:g.transform(normalize)0 -1.161895 1 -1.161895 2 -1.161895 3 -0.387298 4 -0.387298 5 -0.387298 6 0.387298 7 0.387298 8 0.387298 9 1.161895 10 1.161895 11 1.161895 Name: value, dtype: float64g.apply(normalize)0 -1.161895 1 -1.161895 2 -1.161895 3 -0.387298 4 -0.387298 5 -0.387298 6 0.387298 7 0.387298 8 0.387298 9 1.161895 10 1.161895 11 1.161895 Name: value, dtype: float64内建的聚合函数,比如mean, sum经常比一般的apply函数要快。而是用transform的话,会更快一些。这就需要我们使用无包装的组操作(upwrapped group operation):g.transform('mean')0 4.5 1 5.5 2 6.5 3 4.5 4 5.5 5 6.5 6 4.5 7 5.5 8 6.5 9 4.5 10 5.5 11 6.5 Name: value, dtype: float64normalized = (df['value'] - g.transform('mean')) / g.transform('std') normalized0 -1.161895 1 -1.161895 2 -1.161895 3 -0.387298 4 -0.387298 5 -0.387298 6 0.387298 7 0.387298 8 0.387298 9 1.161895 10 1.161895 11 1.161895 Name: value, dtype: float64一个无包装的组操作可能会涉及多个组聚合操作,不过向量化操作会胜过这种操作。2 Grouped Time Resampling(分组时间重采样)对于时间序列数据,resample方法是一个基于时间的组操作。这里有一个样本表格:N = 15 times = pd.date_range('2017-05-20 00:00', freq='1min', periods=N) df = pd.DataFrame({'time': times, 'value': np.arange(N)}) df我们用time索引,然后重采样:df.set_index('time').resample('5min').count()假设一个DataFrame包含多个时间序列,用多一个key列来表示:df2 = pd.DataFrame({'time': times.repeat(3), 'key': np.tile(['a', 'b', 'c'], N), 'value': np.arange(N * 3.)}) df2[:7]想要对key列的值做重采样,我们引入pandas.TimeGrouper对象:time_key = pd.TimeGrouper('5min')然后设置time为索引,对key和time_key做分组,然后聚合:resampled = (df2.set_index('time') .groupby(['key', time_key]) .sum()) resampledresampled.reset_index()
0
0
0
浏览量1517
清晨我上码

pandas教程:MovieLens 1M Dataset MovieLens 1M数据集

14.2 MovieLens 1M Dataset(MovieLens 1M数据集)这个数据集是电影评分数据:包括电影评分,电影元数据(风格类型,年代)以及关于用户的人口统计学数据(年龄,邮编,性别,职业等)。MovieLens 1M数据集含有来自6000名用户对4000部电影的100万条评分数据。分为三个表:评分,用户信息,电影信息。这些数据都是dat文件格式,可以通过pandas.read_table将各个表分别读到一个pandas DataFrame对象中:import pandas as pd# Make display smaller pd.options.display.max_rows = 10unames = ['user_id', 'gender', 'age', 'occupation', 'zip'] users = pd.read_table('../datasets/movielens/users.dat', sep='::', header=None, names=unames)/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/ipykernel/__main__.py:3: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'. app.launch_new_instance()因为sep='::'有点像是正则表达式,于是有了上面的错误。在这个帖子找到了解决方法,设置engine为python即可。Looks like on Python 2.7 Pandas just doesn’t handle separators thatlook regexish. The initial “error” can be worked around by addingengine=‘python’ as a named parameter in the call, as suggested in thewarning.users = pd.read_table('../datasets/movielens/users.dat', sep='::', header=None, names=unames, engine='python')rnames = ['user_id', 'movie_id', 'rating', 'timestamp'] ratings = pd.read_table('../datasets/movielens/ratings.dat', sep='::', header=None, names=rnames, engine='python')mnames = ['movie_id', 'title', 'genres'] movies = pd.read_table('../datasets/movielens/movies.dat', sep='::', header=None, names=mnames, engine='python')加载前几行验证一下数据加载工作是否顺利users[:5]ratings[:5]movies[:5]注意,年龄和职业是以编码形式给出的,它们的具体含义请参考改数据集的README文件。分析散布在三个表中的数据不是一件轻松的事情。假设我们想要根据性别和年龄来计算某部电影的平均得分,如果将所有的数据都合并到一个表中的话,问题就简单多了。我们先用pandas的merge函数将ratings和users合并到一起,然后再将movies也合并进去。pandas会根据列名的重叠情况推断出哪些列是合并(或连接)键:data = pd.merge(pd.merge(ratings, users), movies)data.head()data.iloc[0]user_id 1 movie_id 1193 rating 5 timestamp 978300760 gender F age 1 occupation 10 zip 48067 title One Flew Over the Cuckoo's Nest (1975) genres Drama Name: 0, dtype: object现在,只要稍微熟悉一下pandas,就能轻松地根据任意个用户或电影属性对评分数据进行聚合操作了。为了按性别计算每部电影的平均得分,我们可以使用pivot_table方法:mean_ratings = data.pivot_table('rating', index='title', columns='gender', aggfunc='mean')mean_ratings[:5]该操作产生了另一个DataFrame,其内容为电影平均得分,行标为电影名称,列表为性别。现在,我们打算过滤掉评分数据不够250条的电影(这个数字可以自己设定)。为了达到这个目的,我们先对title进行分组,然后利用size()得到一个含有各电影分组大小的Series对象:ratings_by_title = data.groupby('title').size()ratings_by_title[:10]title $1,000,000 Duck (1971) 37 'Night Mother (1986) 70 'Til There Was You (1997) 52 'burbs, The (1989) 303 ...And Justice for All (1979) 199 1-900 (1994) 2 10 Things I Hate About You (1999) 700 101 Dalmatians (1961) 565 101 Dalmatians (1996) 364 12 Angry Men (1957) 616 dtype: int64active_titles = ratings_by_title.index[ratings_by_title >= 250]print(active_titles)Index([''burbs, The (1989)', '10 Things I Hate About You (1999)', '101 Dalmatians (1961)', '101 Dalmatians (1996)', '12 Angry Men (1957)', '13th Warrior, The (1999)', '2 Days in the Valley (1996)', '20,000 Leagues Under the Sea (1954)', '2001: A Space Odyssey (1968)', '2010 (1984)', ... 'X-Men (2000)', 'Year of Living Dangerously (1982)', 'Yellow Submarine (1968)', 'You've Got Mail (1998)', 'Young Frankenstein (1974)', 'Young Guns (1988)', 'Young Guns II (1990)', 'Young Sherlock Holmes (1985)', 'Zero Effect (1998)', 'eXistenZ (1999)'], dtype='object', name='title', length=1216)上面的active_titles中的电影,都是评论是大于250条以上的。我们可以用这些标题作为索引,从mean_ratings中选出这些评论大于250条的电影:mean_ratings = mean_ratings.loc[active_titles] mean_ratings1216 rows × 2 columns想要查看女性观众喜欢的电影,可以按F列进行降序操作:top_female_ratings = mean_ratings.sort_values(by='F', ascending=False) top_female_ratings[:10]1 Measuring Rating Disagreement(计算评分分歧)假设我们想要找出男性和女性观众分歧最大的电影。一个办法是给mean_ratings加上一个用于存放平均得分之差的列,并对其进行排序:mean_ratings['diff'] = mean_ratings['M'] - mean_ratings['F']按‘diff’排序即可得到分歧最大且女性观众更喜欢的电影:sorted_by_diff = mean_ratings.sort_values(by='diff') sorted_by_diff[:15]15 rows × 3 columns对行进行反序操作,并取出前15行,得到的则是男性更喜欢,而女性评价较低的电影:# Reverse order of rows, take first 10 rows sorted_by_diff[::-1][:10]如果只是想要找出分歧最大的电影(不考虑性别因素),则可以计算得分数据的方差或标准差:# 根据电影名称分组的得分数据的标准差 rating_std_by_title = data.groupby('title')['rating'].std()# 根据active_titles进行过滤 rating_std_by_title = rating_std_by_title.loc[active_titles]# Order Series by value in descending order rating_std_by_title.sort_values(ascending=False)[:10]title Dumb & Dumber (1994) 1.321333 Blair Witch Project, The (1999) 1.316368 Natural Born Killers (1994) 1.307198 Tank Girl (1995) 1.277695 Rocky Horror Picture Show, The (1975) 1.260177 Eyes Wide Shut (1999) 1.259624 Evita (1996) 1.253631 Billy Madison (1995) 1.249970 Fear and Loathing in Las Vegas (1998) 1.246408 Bicentennial Man (1999) 1.245533 Name: rating, dtype: float64这里我们注意到,电影分类是以竖线|分割的字符串形式给出的。如果想对不同的电影分类进行分析的话,就需要先将其转换成更有用的形式才行。
0
0
0
浏览量460
清晨我上码

pandas教程:Techniques for Method Chaining 方法链接的技巧

12.3 Techniques for Method Chaining(方法链接的技巧)对序列进行转换的时候,我们会发现会创建很多再也不会用到的临时变量(temporary variable)。比如下面的例子:df = load_data() df2 = df[df['col2'] < 0] df2['col1_demeaned'] = df2['col1'] - df2['col1'].mean() result = df2.groupby('key').col1_demeaned.std()这里我们不使用任何真实数据,这个例子说明了一些新方法。首先,DataFrame.assign方法是一个函数,它可以作为列赋值方法df[k] = v的替代品。它不会修改原有的对象,而是会返回一个带有修改标识的新DataFrame对象。所以下面两个方法是相等的:# Usual non-functional way df2 = df.copy() df2['k'] = v # Functional assign way df2 = df.assign(k=v)在原始对象上直接进行赋值比用assign会更快一些,但是assign可以使用更方便的方法链接(method chaining):result = (df2.assign(col1_demeaned=df2.col1 - df2.col2.mean()) .groupby('key') .col1_demeaned.std())需要记住的是,当使用方法链接的时候,你可能会需要引用临时对象。在之后的例子,我们不能引用load_data的结果,除非它被赋值给临时变量df. 。为了做到这一点,assign和其他一些pandas函数接受像函数一样函数参数(function-like arguments),也被称作为可调用(callables)。为了演示callables,考虑上面例子里的一个片段:df = load_data() df2 = df[df['col2'] < 0]这句可以被写为:df = (load_data()[lambda x: x['col2'] < 0])在这里,load_data的结果没有赋值给参数,所以传入[]中的函数被绑定到了绑定到了在某个链接状态下的对象上(so the function passed into [] is then bound to the object at that stage of the method chain)。我们可以把整个序列携程一行链接表达式:result = (load_data() [lambda x: x.col2 < 0] .assign(col1_demeaned=lambda x: x.col1 - x.col1.mean()) .groupby('key') .col1_demeaned.std())我们可以把代码写成这种风格,但也可以分解成为步来写,这样可读性会更高。1 The pipe Method(pipe方法)我们可以利用pandas内建的函数和一些用callables实现的方法链接,做很多事情。不过,有时候我们想要用自己的函数或一些第三方库里的函数。这就是pipe方法出现的原因。假设有一系列函数调用:a = f(df, arg1=v1) b = g(a, v2, arg3=v3) c = h(b, arg4=v4)当使用函数来接受或返回Series或DataFrame对象的时候,我们可以把上面的利用pipe重写为:result = (df.pipe(f, arg1=v1) .pipe(g, v2, arg3=v3) .pipe(h, arg4=v4))f(df)和df.pipe(f)是一样的,但是pipe能让链接调用更简单。pipe一个有用的模式是生成一系列可重复的函数操作。例如,考虑计算两个组的平均值的差:g = df.groupby(['key1', 'key2']) df['col1'] = df['col1'] - g.transform('mean')假设我们想要能对不止一个组进行减值,只要改变分组键(group key)即可。除此之外,我们可能想要把这种转换用方法链接的形式实现。这里有一个例子:def group_demean(df, by, cols): result = df.copy() g = df.groupby(by) for c in cols: result[c] = df[c] - g[c].transform('mean') return result上面的也可以写为:result = (df[df.col1 < 0] .pipe(group_demean, ['key1', 'key2'], ['col1']))
0
0
0
浏览量217
清晨我上码

pandas教程:Reshaping and Pivoting 整形和旋转

8.3 Reshaping and Pivoting(整形和旋转)有很多用于整理表格型数据的基本操作,指的就是reshape和pivot。1 Reshaping with Hierarchical Indexing(对多层级索引进行整形)多层级索引提供一套统一的方法来整理DataFrame中数据。主要有两个操作:stack这个操作会把列旋转为行unstack这个会把行变为列下面我们会给出一些例子。这里有一个DataFrame,我们用字符串数组来作为行和列的索引:import numpy as np import pandas as pddata = pd.DataFrame(np.arange(6).reshape((2, 3)), index=pd.Index(['Ohio', 'Colorado'], name='state'), columns=pd.Index(['one', 'two', 'three'], name='number')) data使用stack方法会把列数据变为行数据,产生一个Series:result = data.stack() resultstate number Ohio one 0 two 1 three 2 Colorado one 3 two 4 three 5 dtype: int64对于一个有多层级索引的Series,可以用unstack把它变回DataFrame:result.unstack()默认会把最内层的层级unstack(取消堆叠),stack默认也是这样。我们可以传入一个表示层级的数字或名字,来指定取消堆叠某个层级:result.unstack(0)result.unstack('state')如果某个层级里的值不能在subgroup(子组)里找到的话,unstack可能会引入缺失值:s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd']) s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e']) data2 = pd.concat([s1, s2], keys=['one', 'two'])data2one a 0 b 1 c 2 d 3 two c 4 d 5 e 6 dtype: int64data2.unstack()stack默认会把缺失值过滤掉,所以这两种操作是可逆的:data2.unstack()data2.unstack().stack()one a 0.0 b 1.0 c 2.0 d 3.0 two c 4.0 d 5.0 e 6.0 dtype: float64data2.unstack().stack(dropna=False)one a 0.0 b 1.0 c 2.0 d 3.0 e NaN two a NaN b NaN c 4.0 d 5.0 e 6.0 dtype: float64如果对一个DataFrame使用unstack,被取消堆叠(unstack)的层级会变为结果中最低的层级:df = pd.DataFrame({'left': result, 'right': result + 5}, columns=pd.Index(['left', 'right'], name='side')) df # 行的话,有state和number两个层级,number是内层级。而列的话有side这一个层级df.unstack('state') # state被unstack后,变为比side更低的层级调用stack的时候,可以指明想要stack(堆叠)哪一个轴:df.unstack('state').stack('side')2 Pivoting “Long” to “Wide” Format(把“长”格式旋转为“宽”格式)一种用来把多重时间序列数据存储在数据库和CSV中的格式叫long or stacked format(长格式或堆叠格式)。下面我们加载一些数据,处理一下时间序列文件并做一些数据清理工作:data = pd.read_csv('../examples/macrodata.csv') data.head()periods = pd.PeriodIndex(year=data.year, quarter=data.quarter, name='date')columns = pd.Index(['realgdp', 'infl', 'unemp'], name='item')data = data.reindex(columns=columns)data.index = periods.to_timestamp('D', 'end')ldata = data.stack().reset_index().rename(columns={0: 'value'})对于PeriodIndex,我们会在第十一章讲得更详细些。在这里,这个函数把year和quarter这两列整合起来作为一种时间间隔类型。ldata看起来是这样的:ldata[:10]这种格式叫做long format for multiple time series(用于多重时间序列的长格式),或者有两个以上键(keys)的观测数据(在这个例子里,keys指的是date和item)。表格中的每一行表示一个观测数据。这种数据经常被存储于关系型数据库中,比如MySQL,这种固定的模式(列名和数据类型)能让作为item列中不同的数据,添加到表格中。在前一个例子里,date和item通常被用来当做primary keys(主键,这是关系型数据库里的术语),能实现relational integrity(关系完整性)和更方便的join(联结)。但是在一些例子里,这种格式的数据并不好处理;我们可能更喜欢有一个DataFrame,其中一列能有不同的item值,并用date列作为索引。DataFrame中的pivot方法,就能做到这种转换:pivoted = ldata.pivot('date', 'item', 'value') pivoted203 rows × 3 columns前两个传入的值是列,分别被用于作为行索引和列索引(date是行索引,item是列索引),最后是一个是可选的value column(值列),用于填充DataFrame。假设我们有两列值,我们想要同时整形:ldata['value2'] = np.random.randn(len(ldata)) ldata[:10]舍弃最后一个参数,我们能得到一个有多层级列的DataFrame:pivoted = ldata.pivot('date', 'item') pivoted[:5]pivoted['value'][:5]这里pivot相当于用set_index创建了一个多层级用里,并调用了unstack:unstacked = ldata.set_index(['date', 'item']).unstack('item') unstacked[:7]3 Pivoting “Wide” to “Long” Format(把“宽”格式旋转为“长”格式)用于DataFrame,与pivot相反的操作是pandas.melt。相对于把一列变为多列的pivot,melt会把多列变为一列,产生一个比输入的DataFrame还要长的结果。看一下例子:df = pd.DataFrame({'key': ['foo', 'bar', 'baz'], 'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}) df'key’列可以作为group indicator(群指示器),其他列可以作为数据值。当使用pandas.melt,我们必须指明哪些列是群指示器。这里我们令key作为群指示器:melted = pd.melt(df, ['key']) melted使用pivot,我们可以得到原来的布局:reshaped = melted.pivot('key', 'variable', 'value') reshaped因为pivot会给行标签创建一个索引(key列),所以这里我们要用reset_index来让数据变回去:reshaped.reset_index()当然,我们也可以在使用melt的时候指定哪些列用于值:pd.melt(df, id_vars=['key'], value_vars=['A', 'B'])pandas.melt也能在没有群指示器的情况下使用:pd.melt(df, value_vars=['A', 'B', 'C'])pd.melt(df, value_vars=['key','A', 'B'])
0
0
0
浏览量565
清晨我上码

numpy教程:Universal Functions 通用函数 伪随机数

4.2 Universal Functions: Fast Element-Wise Array Functions(通用函数:快速点对点数组函数)universal function, 或 ufunc, 是用来在ndarray中实现element-wise操作的。可以认为这个ufunc可以把一些简单的函数做快速的向量化封装,输入时一个以上的标量,输出也是一个以上的标量。很多ufuncs都是点对点的变换,像sqrt或exp:import numpy as np arr = np.arange(10) arrarray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])np.sqrt(arr)array([ 0. , 1. , 1.41421356, 1.73205081, 2. , 2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])np.exp(arr)array([ 1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01, 5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03, 2.98095799e+03, 8.10308393e+03])这些函数叫做一元通用函数(unary ufuncs)。其他一些函数,比如add或maximum,需要两个数组(binary ufuncs),并返回一个数组作为结果:x = np.random.randn(8) y = np.random.randn(8) xarray([ 0.18373557, -1.82728347, -0.11149882, -1.34286776, -1.09016986, 1.63308 , 1.05205535, -0.32746706])yarray([-0.42410809, 1.89603273, -1.13649816, -0.98559379, -0.16827718, 0.52828569, 1.57543351, 1.50045399])np.maximum(x, y)array([ 0.18373557, 1.89603273, -0.11149882, -0.98559379, -0.16827718, 1.63308 , 1.57543351, 1.50045399])这里mamimum点对点的比较x和y中的元素。尽管不常见,但ufunc也能返回多个数组。例如modf,这是一个向量版的divmod(python内建函数),modf会返回小数部分和整数部分:本函数是实现a除以b,然后返回商与余数的元组。如果两个参数a,b都是整数,那么会采用整数除法,结果相当于(a//b, a % b)。如果a或b是浮点数,相当于(math.floor(a/b), a%b)。arr = np.random.randn(7) * 5 arrarray([ 1.51538382, -0.75054846, 0.02863286, 8.74026861, -3.44529124, -9.18401768, -0.68469611])remainder, whole_part = np.modf(arr) remainderarray([ 0.51538382, -0.75054846, 0.02863286, 0.74026861, -0.44529124, -0.18401768, -0.68469611])whole_partarray([ 1., -0., 0., 8., -3., -9., -0.])ufunc能接受一个可选参数作为输出,这样可以直接更改原有的数组:arrarray([ 1.51538382, -0.75054846, 0.02863286, 8.74026861, -3.44529124, -9.18401768, -0.68469611])np.sqrt(arr) # 没有改变原有的arr/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/ipykernel/__main__.py:1: RuntimeWarning: invalid value encountered in sqrt if __name__ == '__main__': array([ 1.23100927, nan, 0.16921248, 2.95639453, nan, nan, nan])np.sqrt(arr, arr) # 改变了原有的arr/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/ipykernel/__main__.py:1: RuntimeWarning: invalid value encountered in sqrt if __name__ == '__main__': array([ 1.23100927, nan, 0.16921248, 2.95639453, nan, nan, nan])arrarray([ 1.23100927, nan, 0.16921248, 2.95639453, nan, nan, nan])4.4 File Input and Output with Arrays(通过数组来进行文件的输入和输出)Numpy能从磁盘直接存储和加载数据,不论是文本格式还是二进制模式。这里我们只考虑Numpy的二进制模式,因为大多数用户更喜欢用pandas或其他工具来加载text或tabular数据。np.save和np.load。数组会以未压缩的原始二进制模式被保存,后缀为.npy:import numpy as np arr = np.arange(10) np.save('../examples/some_array', arr)即使保存的时候没有加后缀,也会被自动加上。可以用np.load来加载数组:np.load('../examples/some_array.npy')array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])用np.savez能保存多个数组,还可以指定数组对应的关键字,不过是未压缩的npz格式:np.savez('../examples/array_archive.npz', a=arr, b=arr)加载.npz文件的时候,得到一个dict object:arch = np.load('../examples/array_archive.npz') arch['b']array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])可以用np.savez_compressed来压缩文件:np.savez_compressed('../examples/array_compressed.npz', a=arr, b=arr)4.5 Linear Algebra (线性代数)在MATLAB里,* 代表矩阵乘法。但是在numpy里,*表示element-wise prodct。要想做到矩阵乘法,要用多函数dot:import numpy as npx = np.array([[1., 2., 3.], [4., 5., 6.]]) y = np.array([[6., 23.], [-1, 7], [8, 9]])xarray([[ 1., 2., 3.], [ 4., 5., 6.]])yarray([[ 6., 23.], [ -1., 7.], [ 8., 9.]])x.dot(y)array([[ 28., 64.], [ 67., 181.]])x.dot(y)等同于np.dot(x, y):np.dot(x, y)array([[ 28., 64.], [ 67., 181.]])一个二维数据和一个一维数组的矩阵乘法,得到一个一维数组:np.dot(x, np.ones(3)) # 这里应该是用狂了boradcasting,x中的每一行与[1, 1, 1]点对点乘积后求和array([ 6., 15.])@作为一个中缀计算符(鬼知道这是什么东西),也能实现矩阵乘法:x @ np.ones(3)array([ 6., 15.])np.linalg能用来做矩阵分解,以及比如转置和求秩之类的事情:from numpy.linalg import inv, qr# X = np.round(np.random.randn(5, 5), 3) # 这里我们用np.round控制小数点后的位数,看起来更舒服一些 X = np.random.randn(5, 5) Xarray([[ 0.0761557 , -0.34138565, -0.56325926, 1.7854 , 1.23440008], [ 1.46787829, 1.73130465, 1.03519282, 1.11137573, -0.05928319], [-0.95508009, -1.35350494, -1.43415583, -0.28499706, 0.32739284], [ 0.83307271, 1.89349058, 0.94116452, 0.32347353, 0.22236912], [-1.20661273, 0.4531822 , 0.47635565, -1.69312137, -0.34497803]])mat = X.T.dot(X) np.round(mat, 2)array([[ 5.22, 4.84, 3.06, 4.35, 0.3 ], [ 4.84, 8.74, 5.92, 1.55, -0.7 ], [ 3.06, 5.92, 4.56, 0.05, -1.18], [ 4.35, 1.55, 0.05, 7.48, 2.7 ], [ 0.3 , -0.7 , -1.18, 2.7 , 1.8 ]])np.round(inv(mat), 2)array([[ 12.14, -6.15, 3.85, -11.22, 14.95], [ -6.15, 4.85, -4.47, 5.7 , -8.57], [ 3.85, -4.47, 5.23, -3.78, 6.71], [-11.22, 5.7 , -3.78, 10.77, -14.55], [ 14.95, -8.57, 6.71, -14.55, 20.95]])np.round(mat.dot(inv(mat)), 2)array([[ 1., 0., 0., 0., -0.], [-0., 1., 0., -0., -0.], [ 0., -0., 1., -0., 0.], [-0., 0., 0., 1., 0.], [ 0., 0., -0., 0., 1.]])q, r = qr(mat)np.round(r, 2)array([[ -8.89, -10.37, -6.57, -7.16, -0.77], [ 0. , -5.5 , -4.79, 5.41, 3.04], [ 0. , 0. , -0.71, 1.85, 1.54], [ 0. , 0. , 0. , -0.75, -0.53], [ 0. , 0. , 0. , 0. , 0.03]])X.T.dot(X)计算的是X和X的转置的矩阵乘法。4.6 Pseudorandom Number Generation(伪随机数生成)numpy.random提供了很多生成随机数的函数,可以选择生成符合某种概率分布的随机数。比如我们可以用normal得到一个4 x 4的,符合标准正态分布的数组:import numpy as np samples = np.random.normal(size=(4, 4)) samplesarray([[ 0.5382462 , -0.79452471, -0.07993797, 0.72243531], [ 0.87180676, 1.61663011, -0.62169955, 1.73880636], [ 1.88294218, 0.07432438, 1.63474848, 0.23519213], [ 0.92847885, -0.45791646, 0.63965317, -0.23654448]])相对的,python内建的random模块一次只能生成一个样本。在生成大量样本方法,numpy.random是非常快的:from random import normalvariate N = 1000000%timeit sample = [normalvariate(0, 1) for _ in range(N)]1 loop, best of 3: 1.25 s per loop%timeit np.random.normal(size=N)10 loops, best of 3: 49.1 ms per loop之所以称之为伪随机数,是因为随机数生成算法是根据seed来生成的。也就是说,只要seed设置一样,每次生成的随机数是相同的:np.random.seed(1234)当然,这个seed是全局的,如果想要避免全局状态,可以用numpy.random.RandomState来创建一个独立的生成器:rng = np.random.RandomState(1234)rng.randn(10)array([ 0.47143516, -1.19097569, 1.43270697, -0.3126519 , -0.72058873, 0.88716294, 0.85958841, -0.6365235 , 0.01569637, -2.24268495])
0
0
0
浏览量1207
清晨我上码

Introduction to scikit-learn scikit-learn简介

13.4 Introduction to scikit-learn(scikit-learn简介)scikit-learn是一个被广泛使用的python机器学习工具包。里面包含了很多监督式学习和非监督式学习的模型,可以实现分类,聚类,预测等任务。虽然scikit-learn并没有和pandas深度整合,但在训练模型之前,pandas在数据清洗阶段能起很大作用。译者:构建的机器学习模型的一个常见流程是,用pandas对数据进行查看和清洗,然后把处理过的数据喂给scikit-learn中的模型进行训练。这里用一个经典的kaggle比赛数据集来做例子,泰坦尼克生还者数据集。加载训练集和测试集:import numpy as np import pandas as pdtrain = pd.read_csv('../datasets/titanic/train.csv') test = pd.read_csv('../datasets/titanic/test.csv')train.head()statsmodels和scikit-learn通常不能应付缺失值,所以我们先检查一下哪些列有缺失值:train.isnull().sum()PassengerId 0 Survived 0 Pclass 0 Name 0 Sex 0 Age 177 SibSp 0 Parch 0 Ticket 0 Fare 0 Cabin 687 Embarked 2 dtype: int64test.isnull().sum()PassengerId 0 Pclass 0 Name 0 Sex 0 Age 86 SibSp 0 Parch 0 Ticket 0 Fare 1 Cabin 327 Embarked 0 dtype: int64对于这样的数据集,通常的任务是预测一个乘客最后是否生还。在训练集上训练模型,在测试集上验证效果。上面的Age这一列有缺失值,这里我们简单的用中位数来代替缺失值:impute_value = train['Age'].median() train['Age'] = train['Age'].fillna(impute_value) test['Age'] = test['Age'].fillna(impute_value)对于Sex列,我们将其变为IsFemale,用整数来表示性别:train['IsFemale'] = (train['Sex'] == 'female').astype(int) test['IsFemale'] = (test['Sex'] == 'female').astype(int)train.head()接下来决定一些模型参数并创建numpy数组:predictors = ['Pclass', 'IsFemale', 'Age']X_train = train[predictors].values X_test = test[predictors].values y_train = train['Survived'].valuesX_train[:5]array([[ 3., 0., 22.], [ 1., 1., 38.], [ 3., 1., 26.], [ 1., 1., 35.], [ 3., 0., 35.]])y_train[:5]array([0, 1, 1, 1, 0])这里我们用逻辑回归模型(LogisticRegression):from sklearn.linear_model import LogisticRegressionmodel = LogisticRegression()然后是fit方法来拟合模型:model.fit(X_train, y_train)LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, solver='liblinear', tol=0.0001, verbose=0, warm_start=False)在测试集上进行预测,使用model.predict:y_predict = model.predict(X_test) y_predict[:10]array([0, 0, 0, 0, 1, 0, 1, 0, 1, 0])如果我们有测试集的真是结果的话,可以用来计算准确率或其他一些指标:(y_true == y_predcit).mean()实际过程中,训练模型的时候,经常用到交叉验证(cross-validation),用于调参,防止过拟合。这样得到的预测效果会更好,健壮性更强。交叉验证是把训练集分为几份,每一份上又取出一部分作为测试样本,这些被取出来的测试样本不被用于训练,但我们可以在这些测试样本上验证当前模型的准确率或均方误差(mean squared error),而且还可以在模型参数上进行网格搜索(grid search)。一些模型,比如逻辑回归,自带一个有交叉验证的类。LogisticRegressionCV类可以用于模型调参,使用的时候需要指定正则化项C,来控制网格搜索的程度:from sklearn.linear_model import LogisticRegressionCVmodel_cv = LogisticRegressionCV(10)model_cv.fit(X_train, y_train)LogisticRegressionCV(Cs=10, class_weight=None, cv=None, dual=False, fit_intercept=True, intercept_scaling=1.0, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, refit=True, scoring=None, solver='lbfgs', tol=0.0001, verbose=0)如果想要自己来做交叉验证的话,可以使用cross_val_score函数,可以用于数据切分。比如,把整个训练集分为4个不重叠的部分:from sklearn.model_selection import cross_val_scoremodel = LogisticRegression(C=10) modelLogisticRegression(C=10, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, solver='liblinear', tol=0.0001, verbose=0, warm_start=False)scores = cross_val_score(model, X_train, y_train, cv=4) scoresarray([ 0.77232143, 0.80269058, 0.77027027, 0.78828829])默认的评价指标每个模型是不一样的,但是可以自己指定评价函数。交叉验证的训练时间较长,但通常能得到更好的模型效果。
0
0
0
浏览量728
清晨我上码

Reading and Writing Data in Text Format

Chapter 6 Data Loading, Storage, and File Formats(数据加载,存储,文件格式)input和output大多可以分为几类:读取文本文件或其他一些存储在磁盘上的格式,从数据库加载数据,利用web API来获取网络资源。6.1 Reading and Writing Data in Text Format (以文本格式读取和写入数据)pandas有很多用来读取表格式数据作为dataframe的函数,下面列出来一些。其中read_csv和read_tabel是最经常用到的:这里我们给出这些函数的大致功能,就是把test data变为dataframe。这些函数的一些可选参数有以下几类:Indexing(索引)能把返回的一列或多列作为一个dataframe。另外也可以选择从文件中获取列名或完全不获取列名Type inference and data conversion(类型推测和数据转换)这个包括用户自己定义的转换类型和缺失值转换Datetime parsing(日期解析)包含整合能力,可以把多列中的时间信息整合为一列Iterating(迭代)支持对比较大的文件进行迭代Unclean data issues(未清洗的数据问题)跳过行或柱脚,评论,或其他一些小东西,比如csv中的逗号因为现实中的数据非常messy(杂乱),所以有一些数据加载函数(特别是read_csv)的轩轩也变得越来越多。对于众多参数感觉不知所措是正常的(read_csv有超过50个参数)。具体的可以去看pandas官网给出的例子。一些函数,比如pandas.read_csv实现type inference,因为column data type不是数据类型的一种。这意味着我们没有必要指定哪些columns是数值,哪些是整数,哪些是字符串。其他一些数据格式,比如HDF5,数据类型是在格式里的。先来一个CSV文件热热身(CSV文件指的是用逗号隔开数据的文件):!cat ../examples/ex1.csva,b,c,d,message 1,2,3,4,hello 5,6,7,8,world 9,10,11,12,foocat是unix下的一个shell command。如果是用windows,用type代替catimport pandas as pddf = pd.read_csv('../examples/ex1.csv') df我们也可以用read_table来指定分隔符:pd.read_table('../examples/ex1.csv', sep=',')一个文件不会总是有header row(页首行),考虑下面的文件:!cat ../examples/ex2.csv1,2,3,4,hello 5,6,7,8,world 9,10,11,12,foo读取这样的文件,设定column name:pd.read_csv('../examples/ex2.csv', header=None)pd.read_csv('../examples/ex2.csv', names=['a', 'b', 'c', 'd', 'message'])如果想要从多列从构建一个hierarchical index(阶层型索引),传入一个包含列名的list:!cat ../examples/csv_mindex.csvkey1,key2,value1,value2 one,a,1,2 one,b,3,4 one,c,5,6 one,d,7,8 two,a,9,10 two,b,11,12 two,c,13,14 two,d,15,16parsed = pd.read_csv('../examples/csv_mindex.csv', index_col=['key1', 'key2']) parsed在一些情况下,一个table可能没有固定的分隔符,用空格或其他方式来分隔。比如下面这个文件:list(open('../examples/ex3.txt'))[' A B C\n', 'aaa -0.264438 -1.026059 -0.619500\n', 'bbb 0.927272 0.302904 -0.032399\n', 'ccc -0.264273 -0.386314 -0.217601\n', 'ddd -0.871858 -0.348382 1.100491\n']可以看到区域是通过不同数量的空格来分隔的。这种情况下,可以传入一个正则表达式给read_table来代替分隔符。用正则表达式为\s+,我们得到:result = pd.read_table('../examples/ex3.txt', sep='\s+') result因为列名比行的数量少,所以read_table推测第一列应该是dataframe的index。这个解析器功能有很多其他参数能帮你解决遇到文件格式异常的问题(可以见之后的表格)。比如,我们要跳过第一、三、四行,使用skiprows:!cat ../examples/ex4.csv# hey! a,b,c,d,message # just wanted to make things more difficult for you # who reads CSV files with computers, anyway? 1,2,3,4,hello 5,6,7,8,world 9,10,11,12,foopd.read_csv('../examples/ex4.csv', skiprows=[0, 2, 3])对于缺失值,pandas使用一些sentinel value(标记值)来代表,比如NA和NULL:!cat ../examples/ex5.csvsomething,a,b,c,d,message one,1,2,3,4,NA two,5,6,,8,world three,9,10,11,12,fooresult = pd.read_csv('../examples/ex5.csv') resultpd.isnull(result)na_values选项能把我们传入的字符识别为NA,导入必须是list:result = pd.read_csv('../examples/ex5.csv', na_values=['NULL']) result我们还可以给不同的column设定不同的缺失值标记符,这样的话需要用到dict:sentinels = {'message': ['foo', 'NA'], 'something': ['two']} # 把message列中的foo和NA识别为NA,把something列中的two识别为NA pd.read_csv('../examples/ex5.csv', na_values=sentinels)1 Reading Text Files in Pieces(读取一部分文本)对于一些比较大的文件,我们想要一次读取一小部分,或者每次迭代一小部分。在我们看一个比较大的文件前,先设置一下pandas中显示的数量:pd.options.display.max_rows = 10result = pd.read_csv('../examples/ex6.csv') result10000 rows × 5 columns如果只是想要读取前几行(不读取整个文件),指定一下nrows:pd.read_csv('../examples/ex6.csv', nrows=5)读取文件的一部分,可以指定chunksize:chunker = pd.read_csv('../examples/ex6.csv', chunksize=1000)chunker<pandas.io.parsers.TextFileReader at 0x1121558d0>pandas返回的TextParser object能让我们根据chunksize每次迭代文件的一部分。比如,我们想要迭代ex6.csv, 计算key列的值的综合:chunker = pd.read_csv('../examples/ex6.csv', chunksize=1000) tot = pd.Series([]) for piece in chunker: tot = tot.add(piece['key'].value_counts(), fill_value=0) tot = tot.sort_values(ascending=False)tot[:10]E 368.0 X 364.0 L 346.0 O 343.0 Q 340.0 M 338.0 J 337.0 F 335.0 K 334.0 H 330.0 dtype: float64TextParser有一个get_chunk方法,能返回任意大小的数据片段:chunker = pd.read_csv('../examples/ex6.csv', chunksize=1000) chunker.get_chunk(10)2 Writing Data to Text Format (写入数据到文本格式)可以输出位csv格式:data = pd.read_csv('../examples/ex5.csv') datadata.to_csv('../examples/out.csv')!cat ../examples/out.csv,something,a,b,c,d,message 0,one,1,2,3.0,4, 1,two,5,6,,8,world 2,three,9,10,11.0,12,foo其他一些分隔符也可以使用(使用sys.stdout可以直接打印文本,方便查看效果):import sysdata.to_csv(sys.stdout, sep='|')|something|a|b|c|d|message 0|one|1|2|3.0|4| 1|two|5|6||8|world 2|three|9|10|11.0|12|foo缺失值会以空字符串打印出来,我们可以自己设定缺失值的指定符:data.to_csv(sys.stdout, na_rep='NULL'),something,a,b,c,d,message 0,one,1,2,3.0,4,NULL 1,two,5,6,NULL,8,world 2,three,9,10,11.0,12,foo如果不指定,行和列会被自动写入。当然也可以设定为不写入:data.to_csv(sys.stdout, index=False, header=False)one,1,2,3.0,4, two,5,6,,8,world three,9,10,11.0,12,foo你可以指定只读取一部分列,并按你选择的顺序读取:data.to_csv(sys.stdout, index=False, columns=['a', 'b', 'c'])a,b,c 1,2,3.0 5,6, 9,10,11.0series也有一个to_csv方法:import numpy as npdates = pd.date_range('1/1/2000', periods=7) ts = pd.Series(np.arange(7), index=dates) ts.to_csv('../examples/tseries.csv')!cat ../examples/tseries.csv2000-01-01,0 2000-01-02,1 2000-01-03,2 2000-01-04,3 2000-01-05,4 2000-01-06,5 2000-01-07,63 Working with Delimited Formats对于大部分磁盘中的表格型数据,用pandas.read_table就能解决。不过,有时候一些人工的处理也是需要的。当然,有时候,一些格式不正确的行能会把read_table绊倒。为了展示一些基本用法,这里先考虑一个小的CSV文件:!cat ../examples/ex7.csv"a","b","c" "1","2","3" "1","2","3"对于单个字符的分隔符,可以使用python内建的csv方法。只要给csv.reader一个打开的文件即可:import csv f = open('../examples/ex7.csv') reader = csv.reader(f)迭代这个reader:for line in reader: print(line)['a', 'b', 'c'] ['1', '2', '3'] ['1', '2', '3']接下来,我们可以根据自己的需要来处理数据。一步步来,首先,把文件读取成一个list of lines:with open('../examples/ex7.csv') as f: lines = list(csv.reader(f))把lines分成header line和data lines:header, values = lines[0], lines[1:]然后我们可以用一个字典表达式来构造一个有列的字典,以及用zip(\*values)反转行为列:data_dict = {h: v for h, v in zip(header, zip(*values))} data_dict{'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}header['a', 'b', 'c']print([x for x in zip(*values)])[('1', '1'), ('2', '2'), ('3', '3')]CSV有很多功能。我们可以定义一个新的分隔符格式,比如字符串的引号,行结束时的回车,这里我们利用csv.Dialect来构造一个子类:class my_dialect(csv.Dialect): lineterminator = '\n' delimiter = ';' quotechar = '"' quoting = csv.QUOTE_MINIMAL f = open('../examples/ex7.csv') reader = csv.reader(f, dialect=my_dialect)for line in reader: print(line)['a,"b","c"'] ['1,"2","3"'] ['1,"2","3"']当然,也可以设定一个分隔符参数给csv.reader,而不用单独定义一个子类:reader = csv.reader(f, delimiter='|')for line in reader: print(line) f.close()对于一些更复杂的文件,比如用多种字符来做分隔符,就不能知网用csv模块来处理了。这种情况下,要先做string的split,或者用re.split写入的话,可以用csv.write。它可以写入与csv.reader中设定一样的文件:with open('../examples/mydata.csv', 'w') as f: writer = csv.writer(f, dialect=my_dialect) writer.writerow(('one', 'two', 'three')) writer.writerow(('1', '2', '3')) writer.writerow(('4', '5', '6')) writer.writerow(('7', '8', '9'))!cat ../examples/mydata.csv one;two;three 1;2;3 4;5;6 7;8;94 JSON DataJSON (short for JavaScript Object Notation)已经是发送HTTP请求的标准数据格式了。这种格式比起表个性的CSV更自由一些:obj = """ {"name": "Wes", "places_lived": ["United States", "Spain", "Germany"], "pet": null, "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]}, {"name": "Katie", "age": 38, "pets": ["Sixes", "Stache", "Cisco"]}] } """JSON是很接近python代码的,除了他的缺失值为null和一些其他的要求。基本的类型是object(dicts), array(lists), strings, numbers, booleans, and nulls. 所以的key必须是string。有很多读取JSON的库,这里用json,它也是python内建的库。把JSON string变为python格式,用json.loads:import jsonresult = json.loads(obj) result{'name': 'Wes', 'pet': None, 'places_lived': ['United States', 'Spain', 'Germany'], 'siblings': [{'age': 30, 'name': 'Scott', 'pets': ['Zeus', 'Zuko']}, {'age': 38, 'name': 'Katie', 'pets': ['Sixes', 'Stache', 'Cisco']}]}使用json.dumps,可以把python object转换为JSON:asjson = json.dumps(result)如何把JSON转变为DataFrame或其他一些结构呢。可以把a list of dicts(JSON object)传给DataFrame constructor而且可以自己指定传入的部分:siblings = pd.DataFrame(result['siblings'], columns=['name', 'age']) siblingspandas.read_json可以自动把JSON数据转变为series或DataFrame:!cat ../examples/example.json[{"a": 1, "b": 2, "c": 3}, {"a": 4, "b": 5, "c": 6}, {"a": 7, "b": 8, "c": 9}]5, “c”: 6},{“a”: 7, “b”: 8, “c”: 9}]pandas.read_json假设JSON数组中的每一个Object,是表格中的一行:data = pd.read_json('../examples/example.json') data如果想要输出结果为JSON,用to_json方法:print(data.to_json()){"a":{"0":1,"1":4,"2":7},"b":{"0":2,"1":5,"2":8},"c":{"0":3,"1":6,"2":9}}print(data.to_json(orient='records'))[{"a":1,"b":2,"c":3},{"a":4,"b":5,"c":6},{"a":7,"b":8,"c":9}]orient='records'表示输出的数据结构是 列->值 的形式:records : list like [{column -> value}, ... , {column -> value}]5 XML and HTML: Web Scraping (网络爬取)python有很多包用来读取和写入HTML和XML格式。比如:lxml, Beautiful Soup, html5lib。其中lxml比较快,其他一些包则能更好的处理一些复杂的HTML和XML文件。pandas有一个内建的函数,叫read_html, 这个函数利用lxml和Beautiful Soup这样的包来自动解析HTML,变为DataFrame。这里我们必须要先下载这些包才能使用read_html:conda install lxml pip install beautifulsoup4 html5libpandas.read_html函数有很多额外选项,但是默认会搜索并试图解析含有<tagble>tag的表格型数据。结果是a list of dataframe:tables = pd.read_html('../examples/fdic_failed_bank_list.html')len(tables)1tables[ Bank Name City ST CERT \ 0 Allied Bank Mulberry AR 91 1 The Woodbury Banking Company Woodbury GA 11297 2 First CornerStone Bank King of Prussia PA 35312 3 Trust Company Bank Memphis TN 9956 4 North Milwaukee State Bank Milwaukee WI 20364 .. ... ... .. ... 542 Superior Bank, FSB Hinsdale IL 32646 543 Malta National Bank Malta OH 6629 544 First Alliance Bank & Trust Co. Manchester NH 34264 545 National State Bank of Metropolis Metropolis IL 3815 546 Bank of Honolulu Honolulu HI 21029 Acquiring Institution Closing Date \ 0 Today's Bank September 23, 2016 1 United Bank August 19, 2016 2 First-Citizens Bank & Trust Company May 6, 2016 3 The Bank of Fayette County April 29, 2016 4 First-Citizens Bank & Trust Company March 11, 2016 .. ... ... 542 Superior Federal, FSB July 27, 2001 543 North Valley Bank May 3, 2001 544 Southern New Hampshire Bank & Trust February 2, 2001 545 Banterra Bank of Marion December 14, 2000 546 Bank of the Orient October 13, 2000 Updated Date 0 November 17, 2016 1 November 17, 2016 2 September 6, 2016 3 September 6, 2016 4 June 16, 2016 .. ... 542 August 19, 2014 543 November 18, 2002 544 February 18, 2003 545 March 17, 2005 546 March 17, 2005 [547 rows x 7 columns]]failures = tables[0] failures.head()这里我们做一些数据清洗和分析,比如按年计算bank failure的数量:close_timestamps = pd.to_datetime(failures['Closing Date'])close_timestamps.dt.year.value_counts()2010 157 2009 140 2011 92 2012 51 2008 25 ... 2004 4 2001 4 2007 3 2003 3 2000 2 Name: Closing Date, dtype: int64Parsing XML with lxml.objectifyXML(eXtensible Markup Language)是另一种常见的数据格式,支持阶层型、嵌套的数据。这里我们演示如何用lxml来解析一个XML格式文件。纽约都会交通局发布了巴士和地铁的时间表。每一个地跌或巴士都有一个不同的文件(比如Performance_NNR.xml对应Metro-North Railroad):<INDICATOR> <INDICATOR_SEQ>373889</INDICATOR_SEQ> <PARENT_SEQ></PARENT_SEQ> <AGENCY_NAME>Metro-North Railroad</AGENCY_NAME> <INDICATOR_NAME>Escalator Availability</INDICATOR_NAME> <DESCRIPTION>Percent of the time that escalators are operational systemwide. The availability rate is based on physical observations performed the morning of regular business days only. This is a new indicator the agency began reporting in 2009.</DESCRIPTION> <PERIOD_YEAR>2011</PERIOD_YEAR> <PERIOD_MONTH>12</PERIOD_MONTH> <CATEGORY>Service Indicators</CATEGORY> <FREQUENCY>M</FREQUENCY> <DESIRED_CHANGE>U</DESIRED_CHANGE> <INDICATOR_UNIT>%</INDICATOR_UNIT> <DECIMAL_PLACES>1</DECIMAL_PLACES> <YTD_TARGET>97.00</YTD_TARGET> <YTD_ACTUAL></YTD_ACTUAL> <MONTHLY_TARGET>97.00</MONTHLY_TARGET> <MONTHLY_ACTUAL></MONTHLY_ACTUAL> </INDICATOR> 使用lxml.objectify,我们可以解析文件,通过getroot,得到一个指向XML文件中root node的指针:from lxml import objectify path = '../datasets/mta_perf/Performance_MNR.xml' parsed = objectify.parse(open(path)) root = parsed.getroot()root.INDICATOR 返回一个生成器,每次调用能生成一个<INDICATOR>XML元素。每一个记录,我们产生一个dict,tag name(比如YTD_ACTUAL)作为字典的key:data = [] skip_fields = ['PARENT_SEQ', 'INDICATOR_SEQ', 'DESIRED_CHANGE', 'DECIMAL_PLACES'] for elt in root.INDICATOR: el_data = {} for child in elt.getchildren(): if child.tag in skip_fields: continue el_data[child.tag] = child.pyval data.append(el_data)然后我们把这个dict变为DataFrame:perf = pd.DataFrame(data) perf.head()XML数据能得到比这个例子更复杂的情况。每个tag都有数据。比如一个而HTML链接,也是一个有效的XML:from io import StringIO tag = '<a href="http://www.google.com">Google</a>' root = objectify.parse(StringIO(tag)).getroot()我们可以访问任何区域的tag(比如href):root<Element a at 0x114860948>root.get('href')'http://www.google.com'root.text'Google'
0
0
0
浏览量1118
清晨我上码

Pivot Tables and Cross-Tabulation 数据透视表和交叉表

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)
0
0
0
浏览量2017
清晨我上码

pandas教程:US Baby Names 1880–2010 1880年至2010年美国婴儿姓名

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>
0
0
0
浏览量2014
清晨我上码

Creating Model Descriptions with Patsy 利用Patsy创建模型

13.2 Creating Model Descriptions with Patsy(利用Patsy创建模型描述)Patsy是一个python库,用于描述统计模型(尤其是线性模型),方法是通过一个叫做公式语法(formula syntax)的字符串来描述。这种公式语法的灵感来源于R和S语言中的公式语法。Patsy的公式是有特殊格式的字符串,像下面这样:y ~ x0 + x1这种a + b的语法并不代表将a和b相加,而是代表为模型创建的设计矩阵的术语(terms in the design matrix)。patsy.dmatrices函数,取一个公式字符串和一个数据集(可以使DataFrame或dict),然后为线性模型产生设计矩阵:import numpy as np import pandas as pddata = pd.DataFrame({'x0': [1, 2, 3, 4, 5], 'x1': [0.01, -0.01, 0.25, -4.1, 0.], 'y': [-1.5, 0., 3.6, 1.3, -2.]})dataimport patsyy, X = patsy.dmatrices('y ~ x0 + x1', data)我们得到:yDesignMatrix with shape (5, 1) y -1.5 0.0 3.6 1.3 -2.0 Terms: 'y' (column 0)XDesignMatrix with shape (5, 3) Intercept x0 x1 1 1 0.01 1 2 -0.01 1 3 0.25 1 4 -4.10 1 5 0.00 Terms: 'Intercept' (column 0) 'x0' (column 1) 'x1' (column 2)这些Patsy DesignMatrix实例是Numpy的ndarrays,附有额外的元数据(metadata):np.asarray(y)array([[-1.5], [ 0. ], [ 3.6], [ 1.3], [-2. ]])np.asarray(X)array([[ 1. , 1. , 0.01], [ 1. , 2. , -0.01], [ 1. , 3. , 0.25], [ 1. , 4. , -4.1 ], [ 1. , 5. , 0. ]])我们可能奇怪X中的Intercept是从哪里来的。这其实是线性模型的一个惯例,比如普通最小二乘回归法(ordinary least squares regression)。我们可以去掉这个截距(intercept),通过加添术语+0给模型:patsy.dmatrices('y ~ x0 + x1 + 0', data)[1]DesignMatrix with shape (5, 2) x0 x1 1 0.01 2 -0.01 3 0.25 4 -4.10 5 0.00 Terms: 'x0' (column 0) 'x1' (column 1)这种Patsy对象可以直接传入一个算法,比如numpy.linalg.lstsq,来进行普通最小二乘回归的计算:coef, resid, _, _ = np.linalg.lstsq(X, y)coefarray([[ 0.31290976], [-0.07910564], [-0.26546384]])coef = pd.Series(coef.squeeze(), index=X.design_info.column_names) coefIntercept 0.312910 x0 -0.079106 x1 -0.265464 dtype: float641 Data Transformations in Patsy Formulas(Patsy公式的数据变换)我们可以把python和Patsy公式混合起来。当评估公式的时候,库会尝试找到封闭域中的公式:y, X = patsy.dmatrices('y ~ x0 + np.log(np.abs(x1) + 1)', data)XDesignMatrix with shape (5, 3) Intercept x0 np.log(np.abs(x1) + 1) 1 1 0.00995 1 2 0.00995 1 3 0.22314 1 4 1.62924 1 5 0.00000 Terms: 'Intercept' (column 0) 'x0' (column 1) 'np.log(np.abs(x1) + 1)' (column 2)一些常用的变量变换,包括标准化(standardizing (平均值0,方差1)和中心化(减去平均值)。Patsy有内建的函数可以做到这些:y, X = patsy.dmatrices('y ~ standardize(x0) + center(x1)', data)XDesignMatrix with shape (5, 3) Intercept standardize(x0) center(x1) 1 -1.41421 0.78 1 -0.70711 0.76 1 0.00000 1.02 1 0.70711 -3.33 1 1.41421 0.77 Terms: 'Intercept' (column 0) 'standardize(x0)' (column 1) 'center(x1)' (column 2)作为建模的一部分,我们可能会在一个数据及上训练模型,然后在另一个数据及上评价模型。当使用中心化或标准化这样的转换时,我们必须注意,必须用模型在新数据集上做预测。这叫做状态变换(stateful transformations)。因为我们必须用原本在训练集上得到的平均值和标准差,用在新的数据集上。通过保存原先数据集中的信息,patsy.build_design_matrices函数能把变换用在新的数据集上:new_data = pd.DataFrame({ 'x0': [6, 7, 8, 9], 'x1': [3.1, -0.5, 0, 2.3], 'y': [1, 2, 3, 4]})new_X = patsy.build_design_matrices([X.design_info], new_data) new_X[DesignMatrix with shape (4, 3) Intercept standardize(x0) center(x1) 1 2.12132 3.87 1 2.82843 0.27 1 3.53553 0.77 1 4.24264 3.07 Terms: 'Intercept' (column 0) 'standardize(x0)' (column 1) 'center(x1)' (column 2)]因为加号在Patsy公式中不代表加法,如果想要把两个列通过名字相加,必须把他们用I函数包起来:y, X = patsy.dmatrices('y ~ I(x0 + x1)', data) XDesignMatrix with shape (5, 2) Intercept I(x0 + x1) 1 1.01 1 1.99 1 3.25 1 -0.10 1 5.00 Terms: 'Intercept' (column 0) 'I(x0 + x1)' (column 1)Patsy有一些其他的内建转换,得来patsy.builtins模块里。更多的信息请参考文档。Categorical数据有特殊的类用于变换,下面进行介绍。2 Categorical Data and Patsy(Categorical数据和Patsy)非数值型数据可以通过很多种方式变为一个模型设计矩阵。这个话题很大,这里只做简单介绍。当我们在Patsy公式中使用非数值术语时,这些类型数据默认会被转换为哑变量。如果有截距,一个层级上的截距会被舍弃,防止出现共线性:data = pd.DataFrame({'key1': ['a', 'a', 'b', 'b', 'a', 'b', 'a', 'b'], 'key2': [0, 1, 0, 1, 0, 1, 0, 0], 'v1': [1, 2, 3, 4, 5, 6, 7, 8], 'v2': [-1, 0, 2.5, -0.5, 4.0, -1.2, 0.2, -1.7] })y, X = patsy.dmatrices('v2 ~ key1', data) XDesignMatrix with shape (8, 2) Intercept key1[T.b] 1 0 1 0 1 1 1 1 1 0 1 1 1 0 1 1 Terms: 'Intercept' (column 0) 'key1' (column 1)如果从模型中舍弃截距,每个类型的列会被包含在模型设计矩阵中:y, X = patsy.dmatrices('v2 ~ key1 + 0', data) XDesignMatrix with shape (8, 2) key1[a] key1[b] 1 0 1 0 0 1 0 1 1 0 0 1 1 0 0 1 Terms: 'key1' (columns 0:2)数值型列可以通过C函数,变为类型列:y, X = patsy.dmatrices('v2 ~ C(key2)', data) XDesignMatrix with shape (8, 2) Intercept C(key2)[T.1] 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 Terms: 'Intercept' (column 0) 'C(key2)' (column 1)当我们在一个模型中使用多个类型术语时,会变得更复杂一些,之前用key1:key2的形式来包含有交集的术语,这种方法可以用于使用多个术语,例如,一个方法分析模型(analysis of variance (ANOVA) models):data['key2'] = data['key2'].map({0: 'zero', 1: 'one'}) datay, X = patsy.dmatrices('v2 ~ key1 + key2', data) XDesignMatrix with shape (8, 3) Intercept key1[T.b] key2[T.zero] 1 0 1 1 0 0 1 1 1 1 1 0 1 0 1 1 1 0 1 0 1 1 1 1 Terms: 'Intercept' (column 0) 'key1' (column 1) 'key2' (column 2)y, X = patsy.dmatrices('v2 ~ key1 + key2 + key1:key2', data) XDesignMatrix with shape (8, 4) Intercept key1[T.b] key2[T.zero] key1[T.b]:key2[T.zero] 1 0 1 0 1 0 0 0 1 1 1 1 1 1 0 0 1 0 1 0 1 1 0 0 1 0 1 0 1 1 1 1 Terms: 'Intercept' (column 0) 'key1' (column 1) 'key2' (column 2) 'key1:key2' (column 3)Patsy还提供一些其他转换类型数据的方案,包括按特定顺序来变换。具体的可以查看文档。
0
0
0
浏览量622
清晨我上码

pandas教程:USDA Food Database USDA食品数据库

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
0
0
0
浏览量2016
清晨我上码

pandas教程:Interacting with Web APIs API和数据库的交互

6.3 Interacting with Web APIs (网络相关的API交互)很多网站都有公开的API,通过JSON等格式提供数据流。有很多方法可以访问这些API,这里推荐一个易用的requests包。找到github里pandas最新的30个issues,制作一个GET HTTP request, 通过使用requests包:import pandas as pd import numpy as npimport requestsurl = 'https://api.github.com/repos/pandas-dev/pandas/issues'resp = requests.get(url)resp<Response [200]>response的json方法能返回一个dict,包含可以解析为python object的JSON:data = resp.json() data[0]['title']'Optimize data type'data[0]{'assignee': None, 'assignees': [], 'author_association': 'NONE', 'body': 'Hi guys, i\'m user of mysql\r\nwe have an "function" PROCEDURE ANALYSE\r\nhttps://dev.mysql.com/doc/refman/5.5/en/procedure-analyse.html\r\n\r\nit get all "dataframe" and show what\'s the best "dtype", could we do something like it in Pandas?\r\n\r\nthanks!', 'closed_at': None, 'comments': 1, 'comments_url': 'https://api.github.com/repos/pandas-dev/pandas/issues/18272/comments', 'created_at': '2017-11-13T22:51:32Z', 'events_url': 'https://api.github.com/repos/pandas-dev/pandas/issues/18272/events', 'html_url': 'https://github.com/pandas-dev/pandas/issues/18272', 'id': 273606786, 'labels': [], 'labels_url': 'https://api.github.com/repos/pandas-dev/pandas/issues/18272/labels{/name}', 'locked': False, 'milestone': None, 'number': 18272, 'repository_url': 'https://api.github.com/repos/pandas-dev/pandas', 'state': 'open', 'title': 'Optimize data type', 'updated_at': '2017-11-13T22:57:27Z', 'url': 'https://api.github.com/repos/pandas-dev/pandas/issues/18272', 'user': {'avatar_url': 'https://avatars0.githubusercontent.com/u/2468782?v=4', 'events_url': 'https://api.github.com/users/rspadim/events{/privacy}', 'followers_url': 'https://api.github.com/users/rspadim/followers', 'following_url': 'https://api.github.com/users/rspadim/following{/other_user}', 'gists_url': 'https://api.github.com/users/rspadim/gists{/gist_id}', 'gravatar_id': '', 'html_url': 'https://github.com/rspadim', 'id': 2468782, 'login': 'rspadim', 'organizations_url': 'https://api.github.com/users/rspadim/orgs', 'received_events_url': 'https://api.github.com/users/rspadim/received_events', 'repos_url': 'https://api.github.com/users/rspadim/repos', 'site_admin': False, 'starred_url': 'https://api.github.com/users/rspadim/starred{/owner}{/repo}', 'subscriptions_url': 'https://api.github.com/users/rspadim/subscriptions', 'type': 'User', 'url': 'https://api.github.com/users/rspadim'}}data中的每一个元素都是一个dict,这个dict就是在github上找到的issue页面上的信息。我们可以把data传给DataFrame并提取感兴趣的部分:issues = pd.DataFrame(data, columns=['number', 'title', 'labels', 'state']) issues6.4 Interacting with Databases(与数据库的交互)如果在工作中,大部分数据并不会以text或excel的格式存储。最广泛使用的是SQL-based的关系型数据库(SQL Server,PostgreSQL,MySQL)。选择数据库通常取决于性能,数据整合性,实际应用的可扩展性。读取SQL到DataFrame非常直观,pandas中有一些函数能简化这个过程。举个例子,这里创建一个SQLite数据库,通过使用python内建的sqlite3 driver:import sqlite3 import pandas as pdquery = """ CREATE TABLE test (a VARCHAR(20), b VARCHAR(20), c REAL, d INTEGER );"""con = sqlite3.connect('../examples/mydata.sqlite')con.execute(query)<sqlite3.Cursor at 0x1049931f0>con.commit()然后我们插入几行数据:data = [('Atlanta', 'Georgia', 1.25, 6), ('Tallahassee', 'Florida', 2.6, 3), ('Sacramento', 'California', 1.7, 5)]stmt = "INSERT INTO test VALUES(?, ?, ?, ?)"con.executemany(stmt, data)<sqlite3.Cursor at 0x1049932d0>con.commit()大部分python的SQL驱动(PyODBC, psycopg2, MySQLdb, pymssql, 等)返回a list of tuple,当从一个表格选择数据的时候:cursor = con.execute('select * from test')rows = cursor.fetchall()rows[('Atlanta', 'Georgia', 1.25, 6), ('Tallahassee', 'Florida', 2.6, 3), ('Sacramento', 'California', 1.7, 5)]我们可以把list of tuples传递给DataFrame,但是我们也需要column names,包含cursor的description属性:cursor.description(('a', None, None, None, None, None, None), ('b', None, None, None, None, None, None), ('c', None, None, None, None, None, None), ('d', None, None, None, None, None, None))pd.DataFrame(rows, columns=[x[0] for x in cursor.description])我们不希望每次询问数据库的时候都重复以上步骤,这样对计算机很不好(逐步对计算机系统或文件做小改动导致大的损害)。SQLAlchemy计划是一个六星的Python SQL工具箱,它能抽象出不同SQL数据库之间的不同。pandas有一个read_sql函数,能让我们从SQLAlchemy connection从读取数据。这里我们用SQLAlchemy连接到同一个SQLite数据库,并从之前创建的表格读取数据:import sqlalchemy as sqladb = sqla.create_engine('sqlite:///../examples/mydata.sqlite')pd.read_sql('select * from test', db)
0
0
0
浏览量829
清晨我上码

pandas教程:Data Transformation 数据变换、删除和替换

7.2 Data Transformation(数据变换)1 删除重复值import pandas as pd import numpy as npdata = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'], 'k2': [1, 1, 2, 3, 3, 4, 4]}) dataDataFrame方法duplicated返回的是一个boolean Series,表示一个row是否是重复的(根据前一行来判断):data.duplicated()0 False 1 False 2 False 3 False 4 False 5 False 6 True dtype: booldrop_duplicateds返回一个DataFrame,会删除重复的部分:data.drop_duplicates()上面两种方法都默认考虑所有列;另外,我们可以指定一部分来检测重复值。假设我们只想检测’k1’列的重复值:data['v1'] = range(7) datadata.drop_duplicates(['k1'])duplicated和drop_duplicated默认保留第一次观测到的数值组合。设置keep='last'能返回最后一个:data.drop_duplicates(['k1', 'k2'], keep='last')2 Transforming Data Using a Function or Mapping(用函数和映射来转换数据)有时候我们可能希望做一些数据转换。比如下面一个例子,有不同种类的肉:data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon', 'Pastrami', 'corned beef', 'Bacon', 'pastrami', 'honey ham', 'nova lox'], 'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]}) data假设你想加一列,表明每种肉来源的动物是什么。我们可以写一个映射:meat_to_animal = { 'bacon': 'pig', 'pulled pork': 'pig', 'pastrami': 'cow', 'corned beef': 'cow', 'honey ham': 'pig', 'nova lox': 'salmon' }用于series的map方法接受一个函数,或是一个字典,包含着映射关系,但这里有一个小问题,有些肉是大写,有些是小写。因此,我们先用str.lower把所有的值变为小写:lowercased = data['food'].str.lower() lowercased0 bacon 1 pulled pork 2 bacon 3 pastrami 4 corned beef 5 bacon 6 pastrami 7 honey ham 8 nova lox Name: food, dtype: objectdata['animal'] = lowercased.map(meat_to_animal) data我们也可以用一个函数解决上面的问题:data['food'].map(lambda x: meat_to_animal[x.lower()])0 pig 1 pig 2 pig 3 cow 4 cow 5 pig 6 cow 7 pig 8 salmon Name: food, dtype: object使用map是一个很简便的方法,用于element-wise转换和其他一些数据清洗操作。3 Replacing Values(替换值)其实fillna是一个特殊换的替换操作。map可以用于修改一个object里的部分值,但是replace能提供一个更简单和更灵活的方法做到这点。下面是一个series:data = pd.Series([1., -999., 2., -999., -1000., 3.]) data0 1.0 1 -999.0 2 2.0 3 -999.0 4 -1000.0 5 3.0 dtype: float64这里-999可能是用来表示缺失值的标识符。用NA来替代的话,用replace,会产生一个新series(除非使用inplace=True):data.replace(-999, np.nan)0 1.0 1 NaN 2 2.0 3 NaN 4 -1000.0 5 3.0 dtype: float64如果想要一次替换多个值,直接用一个list即可:data.replace([-999, -1000], np.nan)0 1.0 1 NaN 2 2.0 3 NaN 4 NaN 5 3.0 dtype: float64对于不同的值用不同的替换值,也是导入一个list:data.replace([-999, -1000], [np.nan, 0])0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64参数也可以是一个dict:data.replace({-999: np.nan, -1000: 0})0 1.0 1 NaN 2 2.0 3 NaN 4 0.0 5 3.0 dtype: float64注意:data.replace方法和data.str.replace方法是不同的,后者会对string进行element-wise替换。4 Renaming Axis Indexes(重命名Axis Indexes)像是series里的value一样,axis label也能类似地是函数或映射来转换,产生一个新的object。当然也可以设置in-place不产生新的数据:data = pd.DataFrame(np.arange(12).reshape((3, 4)), index=['Ohio', 'Colorado', 'New York'], columns=['one', 'two', 'three', 'four']) data与series相同,axis index有一个map方法:transform = lambda x: x[:4].upper() transform<function __main__.<lambda>>data.indexIndex(['Ohio', 'Colorado', 'New York'], dtype='object')data.index.map(transform)array(['OHIO', 'COLO', 'NEW '], dtype=object)可以赋值给index,以in-place的方式修改DataFrame:data.index = data.index.map(transform) data如果你想要创建一个转换后的版本,而且不用修改原始的数据,可以用rename:data.rename(index=str.title, columns=str.upper)注意,rename能用于dict一样的object,data.rename(index={'OHIO': 'INDIANA'}, columns={'three': 'pekaboo'})rename能让你避免陷入手动赋值给index和columns的杂务中。可以用inplace直接修改原始数据:data.rename(index={'OHIO': 'INDIANA'}, inplace=True) data5 Discretization and Binning(离散化和装箱)连续型数据经常被离散化或分散成bins(分箱)来分析。假设你有一组数据,你想把人分到不同的年龄组里:ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]我们把这些分到四个bin里,18~25, 26~35, 36~60, >60。可以用pandas里的cut:bins = [18, 25, 35, 60, 100] cats = pd.cut(ages, bins) cats[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]] Length: 12 Categories (4, object): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]返回的是一个特殊的Categorical object。我们看到的结果描述了pandas.cut如何得到bins。可以看作是一个string数组用来表示bin的名字,它内部包含了一个categories数组,用来记录不同类别的名字,并伴有表示ages的label(可以通过codes属性查看):cats.codesarray([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)cats.categoriesIndex(['(18, 25]', '(25, 35]', '(35, 60]', '(60, 100]'], dtype='object')pd.value_counts(cats)(18, 25] 5 (35, 60] 3 (25, 35] 3 (60, 100] 1 dtype: int64这里pd.value_counts(cats)是pandas.cut后bin的数量。这里我们注意一下区间。括号表示不包含,方括号表示包含。你可以自己设定哪一边关闭(right=False):pd.cut(ages, [18, 26, 36, 61, 100], right=False)[[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36, 61), [36, 61), [26, 36)] Length: 12 Categories (4, object): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]你也可以用一个list或数组给labels选项来设定bin的名字:group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']pd.cut(ages, bins, labels=group_names)[Youth, Youth, Youth, YoungAdult, Youth, ..., YoungAdult, Senior, MiddleAged, MiddleAged, YoungAdult] Length: 12 Categories (4, object): [Youth < YoungAdult < MiddleAged < Senior]如果你只是给一个bins的数量来cut,而不是自己设定每个bind的范围,cut会根据最大值和最小值来计算等长的bins。比如下面我们想要做一个均匀分布的四个bins:data = np.random.rand(20)pd.cut(data, 4, precision=2)[(0.77, 0.98], (0.33, 0.55], (0.77, 0.98], (0.55, 0.77], (0.55, 0.77], ..., (0.77, 0.98], (0.11, 0.33], (0.11, 0.33], (0.33, 0.55], (0.11, 0.33]] Length: 20 Categories (4, object): [(0.11, 0.33] < (0.33, 0.55] < (0.55, 0.77] < (0.77, 0.98]]precision=2选项表示精确到小数点后两位。一个近似的函数,qcut,会按照数据的分位数来分箱。取决于数据的分布,用cut通常不能保证每一个bin有一个相同数量的数据点。而qcut是按百分比来切的,所以可以得到等数量的bins:data = np.random.randn(1000) # Normally distributedcats = pd.qcut(data, 4) # Cut into quartiles cats[(-0.717, -0.0981], (-0.717, -0.0981], (-0.0981, 0.639], (0.639, 3.434], [-2.86, -0.717], ..., (-0.0981, 0.639], (-0.717, -0.0981], (-0.0981, 0.639], (0.639, 3.434], (-0.0981, 0.639]] Length: 1000 Categories (4, object): [[-2.86, -0.717] < (-0.717, -0.0981] < (-0.0981, 0.639] < (0.639, 3.434]]pd.value_counts(cats)(0.639, 3.434] 250 (-0.0981, 0.639] 250 (-0.717, -0.0981] 250 [-2.86, -0.717] 250 dtype: int64类似的,在cut中我们可以自己指定百分比:cats2 = pd.cut(data, [0, 0.1, 0.5, 0.9, 1.]) # 累进的百分比 cats2[NaN, NaN, (0.1, 0.5], NaN, NaN, ..., (0.1, 0.5], NaN, (0.5, 0.9], NaN, (0.5, 0.9]] Length: 1000 Categories (4, object): [(0, 0.1] < (0.1, 0.5] < (0.5, 0.9] < (0.9, 1]]pd.value_counts(cats2)(0.1, 0.5] 135 (0.5, 0.9] 124 (0, 0.1] 40 (0.9, 1] 21 dtype: int64在之后的章节我们还会用到cut和qcut,这些离散函数对于量化和群聚分析很有用。6 Detecting and Filtering Outliers(检测和过滤异常值)过滤或转换异常值是数组操作的一个重头戏。下面的DataFrame有正态分布的数据:data = pd.DataFrame(np.random.randn(1000, 4)) data.describe()假设我们想要找一个列中,绝对值大于3的数字:data.head()col = data[2] col.head()0 0.494755 1 -0.323590 2 0.302201 3 1.612787 4 -1.718367 Name: 2, dtype: float64col[np.abs(col) > 3]339 -3.303616 932 3.002853 Name: 2, dtype: float64选中所有绝对值大于3的行,可以用any方法在一个boolean DataFrame上:data[(np.abs(data) > 3)].head()data[(np.abs(data) > 3).any(1)] # any中axis=1表示column下面是把绝对值大于3的数字直接变成-3或3:data[np.abs(data) > 3] = np.sign(data) * 3data[21:23]data.describe()np.sign(data)会根据值的正负号来得到1或-1:np.sign(data).head()7 Permutation and Random Sampling(排列和随机采样)排列(随机排序)一个series或DataFrame中的row,用numpy.random.permutation函数很容易就能做到。调用permutation的时候设定好你想要进行排列的axis,会产生一个整数数组表示新的顺序:df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4))) dfsampler = np.random.permutation(5) samplerarray([4, 3, 2, 1, 0])这个数组能被用在基于iloc上的indexing或take函数:df.take(sampler)为了选中一个随机的子集,而且没有代替功能(既不影响原来的值,返回一个新的series或DataFrame),可以用sample方法:df.sample(n=3)如果想要生成的样本带有替代功能(即允许重复),给sample中设定replace=True:choices = pd.Series([5, 7, -1, 6, 4]) draws = choices.sample(n=10, replace=True) draws4 4 4 4 1 7 1 7 1 7 1 7 4 4 3 6 1 7 1 7 dtype: int648 Computing Indicator/Dummy Variables(计算指示器/假变量)另一种在统计模型上的转换或机器学习应用是把一个categorical variable(类别变量)变为一个dummy or indicator matrix(假或指示器矩阵)。如果DataFrame中的一列有k个不同的值,我们可以用一个矩阵或DataFrame用k列来表示,1或0。pandas有一个get_dummies函数实现这个工作,当然,你自己设计一个其实也不难。这里举个例子:df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6)}) dfpd.get_dummies(df['key'])在一些情况里,如果我们想要给column加一个prefix, 可以用data.get_dummies里的prefix参数来实现:dummies = pd.get_dummies(df['key'], prefix='key')df_with_dummy = df[['data1']].join(dummies) df_with_dummy如果DataFrame中的a row属于多个类别,事情会变得复杂一些。我们来看一下MoviesLens 1M 数据集:mnames = ['movie_id', 'title', 'genres']movies = pd.read_table('../datasets/movielens/movies.dat', sep='::', header=None, names=mnames, engine='python') movies[:10]给每个genre添加一个指示变量比较麻烦。首先我们先取出所有不同的类别:all_genres = [] for x in movies.genres: all_genres.extend(x.split('|')) genres = pd.unique(all_genres) genresarray(['Animation', "Children's", 'Comedy', 'Adventure', 'Fantasy', 'Romance', 'Drama', 'Action', 'Crime', 'Thriller', 'Horror', 'Sci-Fi', 'Documentary', 'War', 'Musical', 'Mystery', 'Film-Noir', 'Western'], dtype=object)一种构建indicator dataframe的方法是先构建一个全是0的DataFrame:zero_matrix = np.zeros((len(movies), len(genres))) zero_matrix.shape(3883, 18)dummies = pd.DataFrame(zero_matrix, columns=genres) dummies.head()然后迭代每一部movie,并设置每一行中的dummies为1。使用dummies.columns来计算每一列的genre的指示器:gen = movies.genres[0] gen.split('|')['Animation', "Children's", 'Comedy']dummies.columns.get_indexer(gen.split('|'))array([0, 1, 2])然后,使用.iloc,根据索引来设定值:for i, gen in enumerate(movies.genres): indices = dummies.columns.get_indexer(gen.split('|')) dummies.iloc[i, indices] = 1dummies.head()然后,我们可以结合这个和movies:movies_windic = movies.join(dummies.add_prefix('Genre_')) movies_windic.iloc[0]movie_id 1 title Toy Story (1995) genres Animation|Children's|Comedy Genre_Animation 1 Genre_Children's 1 Genre_Comedy 1 Genre_Adventure 0 Genre_Fantasy 0 Genre_Romance 0 Genre_Drama 0 Genre_Action 0 Genre_Crime 0 Genre_Thriller 0 Genre_Horror 0 Genre_Sci-Fi 0 Genre_Documentary 0 Genre_War 0 Genre_Musical 0 Genre_Mystery 0 Genre_Film-Noir 0 Genre_Western 0 Name: 0, dtype: object对于一个很大的数据集,这种构建多个成员指示变量的方法并不会加快速度。写一个低层级的函数来直接写一个numpy array,并把写过整合到DataFrame会更快一些。一个有用的recipe诀窍是把get_dummies和离散函数(比如cut)结合起来:np.random.seed(12345)values = np.random.rand(10) valuesarray([ 0.92961609, 0.31637555, 0.18391881, 0.20456028, 0.56772503, 0.5955447 , 0.96451452, 0.6531771 , 0.74890664, 0.65356987])bins = [0, 0.2, 0.4, 0.6, 0.8, 1.]pd.cut(values, bins)[(0.8, 1], (0.2, 0.4], (0, 0.2], (0.2, 0.4], (0.4, 0.6], (0.4, 0.6], (0.8, 1], (0.6, 0.8], (0.6, 0.8], (0.6, 0.8]] Categories (5, object): [(0, 0.2] < (0.2, 0.4] < (0.4, 0.6] < (0.6, 0.8] < (0.8, 1]]pd.get_dummies(pd.cut(values, bins))
0
0
0
浏览量442
清晨我上码

pandas使用教程:pandas数据排序sort_values和分组groupby

pandas数据排序导入数据df = pd.read_excel('team.xlsx') dfdf.sort_values(by='Q1') # 按Q1列数据升序排列 df.sort_values(by='Q1', ascending=False) # 降序 team Q1 Q2 Q3 Q4 name Lincoln4 C 98 93 1 20 Max E 97 75 41 3 Elijah B 97 89 15 46 Aaron A 96 75 55 8 Eorge C 93 96 71 78 ... ... ... ... ... ... Lewis B 4 34 77 28 Finn E 4 1 55 32 Liam B 2 80 24 25 Harley B 2 99 12 13 Sebastian C 1 14 68 48 df.sort_values(['team', 'Q1'], ascending=[True, False]) # team升序,Q1降序 team Q1 Q2 Q3 Q4 name Aaron A 96 75 55 8 Henry A 91 15 75 17 Nathan A 87 77 62 13 Dylan A 86 87 65 20 Blake A 78 23 93 9 ... ... ... ... ... ... Eli E 11 74 58 91 Jude E 8 45 13 65 Rory9 E 8 12 58 27 Jackson5 E 6 10 15 33 Finn E 4 1 55 32数据分组聚合pandas可以实现类似SQL的groupby的功能: df.groupby('team').sum() # 按团队分组对应列相加 df.groupby('team').mean() # 按团队分组对应列求平均 Q1 Q2 Q3 Q4 team A 62.705882 37.588235 51.470588 46.058824 B 44.318182 55.363636 54.636364 51.636364 C 48.000000 54.272727 48.545455 51.227273 D 45.263158 62.684211 65.315789 63.105263 E 48.150000 50.650000 44.050000 51.650000使用agg聚合功能,对不同列可以使用不同的聚合函数df.groupby('team').agg({'Q1': sum, # 总和 'Q2': 'count', # 总数 'Q3':'mean', # 平均 'Q4': max}) # 最大值 Q1 Q2 Q3 Q4 team A 1066 17 51.470588 97 B 975 22 54.636364 99 C 1056 22 48.545455 98 D 860 19 65.315789 99 E 963 20 44.050000 98数据转换对数据表进行转置,对数据以A-Q1、E-Q4两点连成的折线为轴对数据进行翻转。可以发现team索引变成了列索引。df.groupby('team').sum().T team A B C D E Q1 1066 975 1056 860 963 Q2 639 1218 1194 1191 1013 Q3 875 1202 1068 1241 881 Q4 783 1136 1127 1199 1033
0
0
0
浏览量1760
清晨我上码

pandas教程:Hierarchical Indexing 分层索引、排序和统计

Chapter 8 Data Wrangling: Join, Combine, and Reshape(数据加工:加入, 结合, 变型)在很多应用中,数据通常散落在不同的文件或数据库中,并不方便进行分析。这一章主要关注工具,能帮我们combine, join, rearrange数据。8.1 Hierarchical Indexing(分层索引)Hierarchical Indexing是pandas中一个重要的特性,能让我们在一个轴(axis)上有多个index levels(索引层级)。它可以让我们在低维格式下处理高维数据。这里给出一个简单的例子,构建一个series,其index是a list of lists:import pandas as pd import numpy as npdata = pd.Series(np.random.randn(9), index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'], [1, 2, 3, 1, 3, 1, 2, 2, 3]])dataa 1 0.636082 2 -1.413061 3 -0.530704 b 1 -0.041634 3 -0.042303 c 1 0.429911 2 0.783350 d 2 0.284328 3 -0.360963 dtype: float64其中我们看到的是把MultiIndex作为index(索引)的,美化过后series。data.indexMultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]], labels=[[0, 0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1, 1, 2]])对于这种分层索引对象,partial indexing(部分索引)也是能做到的,这种方法可以让我们简洁地选中数据的一部分:data['b']1 -0.041634 3 -0.042303 dtype: float64data['b': 'c']b 1 -0.041634 3 -0.042303 c 1 0.429911 2 0.783350 dtype: float64data.loc[['b', 'd']]b 1 -0.041634 3 -0.042303 d 2 0.284328 3 -0.360963 dtype: float64selection(选中)对于一个内部层级(inner level)也是可能的:data.loc[:, 2]a -1.413061 c 0.783350 d 0.284328 dtype: float64分层索引的作用是改变数据的形状,以及做一些基于组的操作(group-based)比如做一个数据透视表(pivot table)。例子,我们可以用unstack来把数据进行重新排列,产生一个DataFrame:data.unstack()相反的操作是stack:data.unstack().stack()a 1 0.636082 2 -1.413061 3 -0.530704 b 1 -0.041634 3 -0.042303 c 1 0.429911 2 0.783350 d 2 0.284328 3 -0.360963 dtype: float64之后的章节会对unstack和stack做更多介绍。对于dataframe,任何一个axis(轴)都可以有一个分层索引:frame = pd.DataFrame(np.arange(12).reshape((4, 3)), index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]], columns=[['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']]) frame每一层级都可以有一个名字(字符串或任何python对象)。如果有的话,这些会显示在输出中:frame.index.names = ['key1', 'key2']frame.columns.names = ['state', 'color']frame这里我们要注意区分行标签(row label)中索引的名字’state’和’color’。如果想要选中部分列(partial column indexing)的话,可以选中一组列(groups of columns):frame['Ohio']MultiIndex能被同名函数创建,而且可以重复被使用;在DataFrame中给列创建层级名可以通过以下方式:pd.MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']], names=['state', 'color'])MultiIndex(levels=[['Colorado', 'Ohio'], ['Green', 'Red']], labels=[[1, 1, 0], [0, 1, 0]], names=['state', 'color'])1 Reordering and Sorting Levels(重排序和层级排序)有时候我们需要在一个axis(轴)上按层级进行排序,或者在一个层级上,根据值来进行排序。swaplevel会取两个层级编号或者名字,并返回一个层级改变后的新对象(数据本身并不会被改变):frame.swaplevel('key1', 'key2')另一方面,sort_index则是在一个层级上,按数值进行排序。比如在交换层级的时候,通常也会使用sort_index,来让结果按指示的层级进行排序:frame.sort_index(level=1)frame.sort_index(level='color') frame.sort_index(level='state') # 这两个语句都会报错(按照我的理解,level指的是key1和key2,key1是level=0,key2是level=1。可以看到下面的结果和上面是一样的:)frame.sort_index(level='key2') frame.swaplevel(0, 1).sort_index(level=0) # 把key1余key2交换后,按key2来排序如果index是按词典顺序那种方式来排列的话(比如从外层到内层按a,b,c这样的顺序),在这种多层级的index对象上,数据选择的效果会更好一些。这是我们调用sort_index(level=0) or sort_index()2 Summary Statistics by Level (按层级来归纳统计数据)在DataFrame和Series中,一些描述和归纳统计数据都是有一个level选项的,这里我们可以指定在某个axis下,按某个level(层级)来汇总。比如上面的DataFrame,我们可以按 行 或 列的层级来进行汇总:frameframe.sum(level='key2')frame.sum(level='color', axis=1)3 Indexing with a DataFrame’s columns(利用DataFrame的列来索引)把DataFrame里的一列或多列作为行索引(row index)是一件很常见的事;另外,我们可能还希望把行索引变为列。这里有一个例子:frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1), 'c': ['one', 'one', 'one', 'two', 'two', 'two', 'two'], 'd': [0, 1, 2, 0, 1, 2, 3]}) frameDataFrame的set_index会把列作为索引,并创建一个新的DataFrame:frame2 = frame.set_index(['c', 'd']) frame2默认删除原先的列,当然我们也可以留着:frame.set_index(['c', 'd'], drop=False)另一方面,reset_index的功能与set_index相反,它会把多层级索引变为列:frame2.reset_index()
0
0
0
浏览量340
清晨我上码

pandas使用教程:apply函数、聚合函数agg和transform

apply函数调用apply函数描述性统计import numpy as np df.loc[:,'Q1':'Q4'].apply(np.mean) df.loc[:,'Q1':'Q4'].apply('mean') #两种写法均可 Q1 49.20 Q2 52.55 Q3 52.67 Q4 52.78 dtype: float64df.loc[:,'Q1':'Q4'].apply(np.cumsum) #累加函数 apply函数lambda自定义#每季度最值之差 df.loc[:,'Q1':'Q4'].apply(lambda x: x.max() - x.min()) Q1 97 Q2 98 Q3 98 Q4 97 dtype: int64#最大值对应索引 df.loc[:,'Q1':'Q4'].apply(lambda x: x.idxmax()) Q1 Lincoln4 Q2 Harley Q3 Lfie Q4 Ronnie dtype: object聚合函数aggregate/agg#计算每列总和 平均数 df.iloc[:,1:].agg(['sum','mean']) #使用匿名函数lambda df.iloc[:,1:].agg(['sum',lambda x:x.mean()]) #自定义函数 三种方法 def avg(x): return x.mean() df.iloc[:,1:].agg(['sum',avg]) Q1 Q2 Q3 Q4 sum 4920.0 5255.00 5267.00 5278.00 mean 49.2 52.55 52.67 52.78用字典实现聚合指定为哪些列应用哪些聚合函数时,需要把包含列名与标量(或标量列表)的字典传递给 DataFrame.agg。df.iloc[:,1:].agg({'Q1': 'mean', 'Q2': 'sum'}) Q1 49.2 Q2 5255.0 dtype: float64df.iloc[:,1:].agg({'Q1':['mean','max'],'Q2':'sum'}) Q1 Q2 max 98.0 NaN mean 49.2 NaN sum NaN 5255.0transform函数transform方法的返回结果与原始数据的索引相同,大小相同。与 .agg API 类似,该 API 支持同时处理多种操作,不用一个一个操作。#将Q1列全部转换为整数 df['Q1'].transform(np.round) name Liver 89 Arry 36 Ack 57 Eorge 93 Oah 65 .. Gabriel 48 Austin7 21 Lincoln4 98 Eli 11 Ben 21 Name: Q1, Length: 100, dtype: int64多函数 Transformtransform() 调用多个函数时,生成多层索引 DataFrame。第一层是原始数据集的列名;第二层是 transform() 调用的函数名。#transform调用两个函数 df['Q1'].transform([np.round,lambda x:x+1]) round_ <lambda> name Liver 89 90 Arry 36 37 Ack 57 58 Eorge 93 94 Oah 65 66 ... ... ... Gabriel 48 49 Austin7 21 22 Lincoln4 98 99 Eli 11 12 Ben 21 22重置索引与更换标签#生成一个4x4的dataframe s = pd.DataFrame(np.random.randint(0,100,(4,4)),index=['a','b','c','d'],columns=['s','w','x','r']) s w x r a 6 28 84 27 b 1 56 92 66 c 46 46 3 91 d 4 81 1 51行重置索引s.reindex(['d','c','b','a']) s w x r d 4 81 1 51 c 46 46 3 91 b 1 56 92 66 a 6 28 84 27行和列同时重置索引s.reindex(index=['d','c','b','a'],columns=['r','x','w','s']) r x w s d 51 1 81 4 c 91 3 46 46 b 66 92 56 1 a 27 84 28 6
0
0
0
浏览量1166
清晨我上码

Date and Time Data Types and Tools 日期和时间数据类型及其工具

Chapter 11 Time Series(时间序列)时间序列指能在任何能在时间上观测到的数据。很多时间序列是有固定频率(fixed frequency)的,意思是数据点会遵照某种规律定期出现,比如每15秒,每5分钟,或每个月。时间序列也可能是不规律的(irregular),没有一个固定的时间规律。如何参照时间序列数据取决于我们要做什么样的应用,我们可能会遇到下面这些:Timestamps(时间戳),具体的某一个时刻Fixed periods(固定的时期),比如2007年的一月,或者2010年整整一年Intervals of time(时间间隔),通常有一个开始和结束的时间戳。Periods(时期)可能被看做是Intervals(间隔)的一种特殊形式。Experiment or elapsed time(实验或经过的时间);每一个时间戳都是看做是一个特定的开始时间(例如,在放入烤箱后,曲奇饼的直径在每一秒的变化程度)这一章主要涉及前三个类型。pandas也支持基于timedeltas的index,本书不会对timedelta index做介绍,感兴趣的可以查看pandas的文档。11.1 Date and Time Data Types and Tools(日期和时间数据类型及其工具)python有标准包用来表示时间和日期数据。datetime, time, calendar,这些模块经常被使用。datetime.datetime类型,或简单写为datetime,被广泛使用:import pandas as pdfrom datetime import datetimenow = datetime.now()nowdatetime.datetime(2017, 12, 1, 12, 12, 0, 375896)now.year, now.month, now.day(2017, 12, 1)datetime能保存日期和时间到微妙级别。timedelta表示两个不同的datetime对象之间的时间上的不同:delta = datetime(2011, 1, 7) - datetime(2008, 6, 24, 8, 15) deltadatetime.timedelta(926, 56700)delta.days926delta.seconds56700我们可以在一个datetime对象上,添加或减少一个或多个timedelta,这样可以产生新的变化后的对象:from datetime import timedeltastart = datetime(2011, 1, 7)start + timedelta(12)datetime.datetime(2011, 1, 19, 0, 0)start - 2 * timedelta(12)datetime.datetime(2010, 12, 14, 0, 0)1 Converting Between String and Datetime(字符串与时间的转换)我们可以对datetime对象,以及pandas的Timestamp对象进行格式化,这部分之后会介绍,使用str或strftime方法,传入一个特定的时间格式就能进行转换:stamp = datetime(2011, 1, 3)str(stamp) '2011-01-03 00:00:00'stamp.strftime('%Y-%m-%d')'2011-01-03'我们可以利用上面的format codes(格式码;时间日期格式)把字符串转换为日期,这要用到datetime.strptime:value = '2011-01-03'datetime.strptime(value, '%Y-%m-%d')datetime.datetime(2011, 1, 3, 0, 0)datestrs = ['7/6/2011', '8/6/2011'][datetime.strptime(x, '%m/%d/%Y') for x in datestrs][datetime.datetime(2011, 7, 6, 0, 0), datetime.datetime(2011, 8, 6, 0, 0)]对于一个一直的时间格式,使用datetime.strptime来解析日期是很好的方法。但是,如果每次都要写格式的话很烦人,尤其是对于一些比较常见的格式。在这种情况下,我们可以使用第三方库dateutil中的parser.parse方法(这个库会在安装pandas的时候自动安装):from dateutil.parser import parseparse('2011-01-03')datetime.datetime(2011, 1, 3, 0, 0)dateutil能够解析很多常见的时间表示格式:parse('Jan 31, 1997 10:45 PM')datetime.datetime(1997, 1, 31, 22, 45)在国际上,日在月之前是很常见的(译者:美国是把月放在日前面的),所以我们可以设置dayfirst=True来指明最前面的是否是日:parse('6/12/2011', dayfirst=True)datetime.datetime(2011, 12, 6, 0, 0)pandas通常可以用于处理由日期组成的数组,不论是否是DataFrame中的行索引或列。to_datetime方法能解析很多不同种类的日期表示。标准的日期格式,比如ISO 8601,能被快速解析:datestrs = ['2011-07-06 12:00:00', '2011-08-06 00:00:00']pd.to_datetime(datestrs)DatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00:00'], dtype='datetime64[ns]', freq=None)还能处理一些应该被判断为缺失的值(比如None, 空字符串之类的):idx = pd.to_datetime(datestrs + [None]) idxDatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00:00', 'NaT'], dtype='datetime64[ns]', freq=None)idx[2]NaTpd.isnull(idx)array([False, False, True], dtype=bool)Nat(Not a Time)在pandas中,用于表示时间戳为空值(null value)。dateutil.parse是一个很有用但不完美的工具。它可能会把一些字符串识别为日期,例如,'42’就会被解析为2042年加上今的日期。datetime对象还有一些关于地区格式(locale-specific formatting)的选项,用于处理不同国家或不同语言的问题。例如,月份的缩写在德国和法国,与英语是不同的。
0
0
0
浏览量870
清晨我上码

pandas教程:String Manipulation 字符串处理和正则表达式re

7.3 String Manipulation(字符串处理)python很多内建方法很适合处理string。而且对于更复杂的模式,可以配合使用正则表达式。而pandas则混合了两种方式。1 String Object Methods(字符串对象方法)大部分string处理,使用内建的一些方法就足够了。比如,可以用split来分割用逗号区分的字符串:val = 'a,b, guido'val.split(',')['a', 'b', ' guido']split经常和strip一起搭配使用来去除空格(包括换行符): pieces = [x.strip() for x in val.split(',')] pieces['a', 'b', 'guido']可以使用+号把::和字符串连起来:first, second, third = piecesfirst + '::' + second + '::' + third'a::b::guido'但这种方法并不python,更快的方法是直接用join方法:'::'.join(pieces)'a::b::guido'其他一些方法适合锁定子字符串位置相关的。用in关键字是检测substring最好的方法,当然,index和find也能完成任务:'guido' in valTrueval.index(',')1val.find(':')-1注意index和find的区别。如果要找的string不存在的话,index会报错。而find会返回-1:val.index(':')--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-11-280f8b2856ce> in <module>() ----> 1 val.index(':') ValueError: substring not foundcount会返回一个substring出现的次数:val.count(',')2replace会取代一种出现方式(pattern)。也通常用于删除pattern,传入一个空字符串即可:val.replace(',', '::')'a::b:: guido'val.replace(',', '')'ab guido'2 Regular Expressions(正则表达式)正则表达式能让我们寻找更复杂的pattern。通常称一个表达式为regex,由正则表达语言来代表一个字符串模式。可以使用python内建的re模块来使用。关于正则表达式,有很多教学资源,可以自己找几篇来学一些,这里不会介绍太多。re模块有以下三个类别:patther matching(模式匹配), substitution(替换), splitting(分割)。通常这三种都是相关的,一个regex用来描述一种pattern,这样会有很多种用法。这里举个例子,假设我们想要根据空格(tabs,spaces,newlines)来分割一个字符串。用于描述一个或多个空格的regex是\s+:import retext = "foo bar\t baz \tqux"re.split('\s+', text)['foo', 'bar', 'baz', 'qux']当调用re.split('\s+', text)的时候,正则表达式第一次被compile编译,并且split方法会被调用搜索text。我们可以自己编译regex,用re.compile,可以生成一个可以多次使用的regex object:regex = re.compile('\s+')regex.split(text)['foo', 'bar', 'baz', 'qux']如果想要得到符合regex的所有结果,以一个list结果返回,可以使用findall方法:regex.findall(text)[' ', '\t ', ' \t']为了防止\在正则表达式中的逃逸,推荐使用raw string literal,比如r'C:\x',而不是使用'C:\\x使用re.compile创建一个regex object是被强烈推荐的,如果你打算把一个表达式用于很多string上的话,这样可以节省CPU的资源。match和search,与findall关系紧密。不过findall会返回所有匹配的结果,而search只会返回第一次匹配的结果。更严格地说,match只匹配string开始的部分。这里举个例子说明,我们想要找到所有的邮件地址:text = """Dave dave@google.com Steve steve@gmail.com Rob rob@gmail.com Ryan ryan@yahoo.com """ pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'# re.IGNORECASE makes the regex case-insensitive regex = re.compile(pattern, flags=re.IGNORECASE)使用findall找到一组邮件地址:regex.findall(text)['dave@google.com', 'steve@gmail.com', 'rob@gmail.com', 'ryan@yahoo.com']search返回text中的第一个匹配结果。match object能告诉我们找到的结果在text中开始和结束的位置:m = regex.search(text)m<_sre.SRE_Match object; span=(5, 20), match='dave@google.com'>text[m.start():m.end()]'dave@google.com'regex.match返回None,因为它只会在pattern存在于strng开头的情况下才会返回匹配结果:print(regex.match(text))None而sub返回一个新的string,把pattern出现的地方替换为我们指定的string:print(regex.sub('REDACTED', text))Dave REDACTED Steve REDACTED Rob REDACTED Ryan REDACTED 假设你想要找到邮件地址,同时,想要把邮件地址分为三个部分,username, domain name, and domain suffix.(用户名,域名,域名后缀)。需要给每一个pattern加一个括号:pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'regex = re.compile(pattern, flags=re.IGNORECASE)match object会返回一个tuple,包含多个pattern组份,通过groups方法:m = regex.match('wesm@bright.net')m.groups()('wesm', 'bright', 'net')findall会返回a list of tuples:regex.findall(text)[('dave', 'google', 'com'), ('steve', 'gmail', 'com'), ('rob', 'gmail', 'com'), ('ryan', 'yahoo', 'com')]sub也能访问groups的结果,不过要使用特殊符号 \1, \2。\1表示第一个匹配的group,\2表示第二个匹配的group,以此类推:print(regex.sub(r'Username: \1, Domain: \2, Suffix: \3', text))Dave Username: dave, Domain: google, Suffix: com Steve Username: steve, Domain: gmail, Suffix: com Rob Username: rob, Domain: gmail, Suffix: com Ryan Username: ryan, Domain: yahoo, Suffix: com 3 Vectorized String Functions in pandas(pandas中的字符串向量化函数)一些复杂的数据清理中,string会有缺失值:import numpy as np import pandas as pddata = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com', 'Rob': 'rob@gmail.com', 'Wes': np.nan}data = pd.Series(data) dataDave dave@google.com Rob rob@gmail.com Steve steve@gmail.com Wes NaN dtype: objectdata.isnull()Dave False Rob False Steve False Wes True dtype: bool可以把一些字符串方法和正则表达式(用lambda或其他函数)用于每一个value上,通过data.map,但是这样会得到NA(null)值。为了解决这个问题,series有一些数组导向的方法可以用于字符串操作,来跳过NA值。这些方法可以通过series的str属性;比如,我们想检查每个电子邮箱地址是否有'gmail' with str.contains:data.str<pandas.core.strings.StringMethods at 0x111f305c0>data.str.contains('gmail')Dave False Rob True Steve True Wes NaN dtype: object正则表达式也可以用,配合任意的re选项,比如IGNORECASE:pattern'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'data.str.findall(pattern, flags=re.IGNORECASE)Dave [(dave, google, com)] Rob [(rob, gmail, com)] Steve [(steve, gmail, com)] Wes NaN dtype: object有很多方法用于向量化。比如str.get或index索引到str属性:matches = data.str.match(pattern, flags=re.IGNORECASE) matches/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/ipykernel/__main__.py:1: FutureWarning: In future versions of pandas, match will change to always return a bool indexer. if __name__ == '__main__': Dave (dave, google, com) Rob (rob, gmail, com) Steve (steve, gmail, com) Wes NaN dtype: object为了访问嵌套list里的元素,我们可以传入一个index给函数:matches.str.get(1)Dave google Rob gmail Steve gmail Wes NaN dtype: objectmatches.str.get(0)Dave dave Rob rob Steve steve Wes NaN dtype: object也可以使用这个语法进行切片:data.str[:5]Dave dave@ Rob rob@g Steve steve Wes NaN dtype: object
0
0
0
浏览量1125
清晨我上码

Resampling and Frequency Conversion 重采样和频度转换

11.6 Resampling and Frequency Conversion(重采样和频度转换)重采样(Resampling)指的是把时间序列的频度变为另一个频度的过程。把高频度的数据变为低频度叫做降采样(downsampling),把低频度变为高频度叫做增采样(upsampling)。并不是所有的重采样都会落入上面这几个类型,例如,把W-WED(weekly on Wednesday)变为W-FRI`,既不属于降采样,也不属于增采样。pandas对象自带resample方法,用于所有的频度变化。resample有一个和groupby类似的API;我们可以用resample来对数据进行分组,然后调用聚合函数(aggregation function):import numpy as np import pandas as pdrng = pd.date_range('2000-01-01', periods=100, freq='D')ts = pd.Series(np.random.randn(len(rng)), index=rng) ts2000-01-01 0.141136 2000-01-02 0.955511 2000-01-03 -0.334537 2000-01-04 0.927611 2000-01-05 0.522567 2000-01-06 0.843023 2000-01-07 0.108661 2000-01-08 0.805668 2000-01-09 -0.470524 2000-01-10 1.162150 2000-01-11 -0.754087 2000-01-12 -1.846421 2000-01-13 -0.322607 2000-01-14 0.769992 2000-01-15 -0.596838 2000-01-16 0.865629 2000-01-17 -0.394363 2000-01-18 1.050334 2000-01-19 0.203739 2000-01-20 0.112178 2000-01-21 -1.858528 2000-01-22 0.921361 2000-01-23 -1.034003 2000-01-24 -0.319369 2000-01-25 0.626385 2000-01-26 2.319831 2000-01-27 0.640064 2000-01-28 0.762187 2000-01-29 -0.053246 2000-01-30 0.500993 ... 2000-03-11 -1.036658 2000-03-12 0.569500 2000-03-13 -0.279623 2000-03-14 -1.593708 2000-03-15 -1.552634 2000-03-16 0.983931 2000-03-17 0.269289 2000-03-18 0.870814 2000-03-19 1.642178 2000-03-20 -0.109097 2000-03-21 -1.891613 2000-03-22 -1.867747 2000-03-23 -0.173888 2000-03-24 0.879418 2000-03-25 0.814583 2000-03-26 -1.683395 2000-03-27 -0.141228 2000-03-28 0.392206 2000-03-29 -1.288983 2000-03-30 1.052897 2000-03-31 -0.297663 2000-04-01 1.050265 2000-04-02 -0.072390 2000-04-03 1.482098 2000-04-04 -0.276297 2000-04-05 0.686525 2000-04-06 1.368484 2000-04-07 0.294756 2000-04-08 1.237246 2000-04-09 1.372567 Freq: D, dtype: float64ts.resample('M').mean()2000-01-31 -0.207554 2000-02-29 0.299003 2000-03-31 -0.095402 2000-04-30 -0.146846 Freq: M, dtype: float64ts.resample('M', kind='period').mean()2000-01 0.210165 2000-02 -0.051811 2000-03 -0.131131 2000-04 0.793695 Freq: M, dtype: float64resample是一个灵活且高效的方法,可以用于处理大量的时间序列。1 Downsampling(降采样)把数据聚合为规律、低频度是一个很普通的时间序列任务。用于处理的数据不必是有固定频度的;我们想要设定的频度会定义箱界(bin edges),根据bin edges会把时间序列分割为多个片段,然后进行聚合。例如,转换为月度,比如'M'或'BM',我们需要把数据以月为间隔进行切割。每一个间隔都是半开放的(half-open);一个数据点只能属于一个间隔,所有间隔的合集,构成整个时间范围(time frame)。当使用resample去降采样数据的时候,有很多事情需要考虑:在每个间隔里,哪一边要闭合怎样对每一个聚合的bin贴标签,可以使用间隔的开始或结束为了演示一下,下面用一个一分钟的数据来举例:rng = pd.date_range('2000-01-01', periods=12, freq='T')ts = pd.Series(np.arange(12), index=rng) ts2000-01-01 00:00:00 0 2000-01-01 00:01:00 1 2000-01-01 00:02:00 2 2000-01-01 00:03:00 3 2000-01-01 00:04:00 4 2000-01-01 00:05:00 5 2000-01-01 00:06:00 6 2000-01-01 00:07:00 7 2000-01-01 00:08:00 8 2000-01-01 00:09:00 9 2000-01-01 00:10:00 10 2000-01-01 00:11:00 11 Freq: T, dtype: int64假设我们想要按5分钟一个数据块来进行聚合,然后对每一个组计算总和:ts.resample('5min', closed='right').sum()1999-12-31 23:55:00 0 2000-01-01 00:00:00 15 2000-01-01 00:05:00 40 2000-01-01 00:10:00 11 Freq: 5T, dtype: int64我们传入的频度定义了每个bin的边界按5分钟递增。默认,bin的左边界是闭合的,所以00:00值是属于00:00到00:05间隔的。设定closed='right',会让间隔的右边闭合:ts.resample('5min', closed='right').sum()1999-12-31 23:55:00 0 2000-01-01 00:00:00 15 2000-01-01 00:05:00 40 2000-01-01 00:10:00 11 Freq: 5T, dtype: int64默认,每一个bin的左边的时间戳,会被用来作为结果里时间序列的标签。通过设置label='right',我们可以使用bin右边的时间戳来作为标签:ts.resample('5min', closed='right', label='right').sum()2000-01-01 00:00:00 0 2000-01-01 00:05:00 15 2000-01-01 00:10:00 40 2000-01-01 00:15:00 11 Freq: 5T, dtype: int64最后,我们可能想要对结果的索引进行位移,比如在右边界减少一秒。想要实现的话,传递一个字符串或日期偏移给loffset:ts.resample('5min', closed='right', label='right', loffset='-1s').sum()1999-12-31 23:59:59 0 2000-01-01 00:04:59 15 2000-01-01 00:09:59 40 2000-01-01 00:14:59 11 Freq: 5T, dtype: int64我们也可以使用shift方法来实现上面loffset的效果。Open-High-Low-Close (OHLC) resampling(股价图重取样)Open-High-Low-Close: 开盘-盘高-盘低-收盘图;股票图;股价图在经济界,一个比较流行的用法,是对时间序列进行聚合,计算每一个桶(bucket)里的四个值:first(open),last(close),maximum(high),minimal(low),即开盘-收盘-盘高-盘低,四个值。使用ohlc聚合函数可以得到这四个聚合结果:ts.resample('5min').ohlc()2 Upsampling and Interpolation(增采样和插值)把一个低频度转换为高频度,是不需要进行聚合的。下面是一个有周数据的DataFrame:frame = pd.DataFrame(np.random.randn(2, 4), index=pd.date_range('1/1/2000', periods=2, freq='W-WED'), columns=['Colorado', 'Texas', 'New York', 'Ohio']) frame当我们对这个数据进行聚合的的时候,每个组只有一个值,以及gap(间隔)之间的缺失值。在不使用任何聚合函数的情况下,我们使用asfreq方法将其转换为高频度:df_daily = frame.resample('D').asfreq() df_daily假设我们想要用每周的值来填写非周三的部分。这种方法叫做填充(filling)或插值(interpolation),可以使用fillna或reindex方法来实现重采样:frame.resample('D').ffill()我们可以选择只对一部分的周期进行填写:frame.resample('D').ffill(limit=2)注意,新的日期索引不能与旧的有重叠:frame.resample('W-THU').ffill()3 Resampling with Periods(对周期进行重采样)对周期的索引进行重采样的过程,与之前时间戳的方法相似:frame = pd.DataFrame(np.random.randn(24, 4), index=pd.period_range('1-2000', '12-2001', freq='M'), columns=['Colorado', 'Texas', 'New York', 'Ohio']) frame[:5]annual_frame = frame.resample('A-DEC').mean() annual_frame增采样需要考虑的要多一些,比如在重采样前,选择哪一个时间跨度作为结束,就像asfreq方法那样。convertion参数默认是'start',但也能用'end':# Q-DEC: Quarterly, year ending in December annual_frame.resample('Q-DEC').ffill()annual_frame.resample('Q-DEC', convention='end').ffill()增采样和降采样的规则更严格一些:降采样中,目标频度必须是原频度的子周期(subperiod)增采样中,目标频度必须是原频度的母周期(superperiod)如果不满足上面的规则,会报错。主要会影响到季度,年度,周度频度;例如,用Q-MAR定义的时间跨度只与A-MAR, A-JUN, A-SEP, A-DEC进行对齐(line up with):annual_frame.resample('Q-MAR').ffill()
0
0
0
浏览量770
清晨我上码

pandas教程:Binary Data Formats 二进制数据格式

6.2 Binary Data Formats (二进制数据格式)最简单的以二进制的格式来存储数据的方法(也被叫做serialization,序列化),就是用python内建的pickle。所有的pandas object都有一个to_pickle方法,可以用来存储数据为pickle格式:import pandas as pd import numpy as npframe = pd.read_csv('../examples/ex1.csv') frameframe.to_pickle('../examples/frame_pickle')!ls ../examples/array_archive.npz [31mmacrodata.csv[m[m array_compressed.npz mydata.csv [31marray_ex.txt[m[m [31mout.csv[m[m [31mcsv_mindex.csv[m[m [31msegismundo.txt[m[m [31mex1.csv[m[m sink.txt [31mex1.xlsx[m[m some_array.npy [31mex2.csv[m[m [31mspx.csv[m[m [31mex3.csv[m[m [31mstinkbug.png[m[m [31mex3.txt[m[m [31mstock_px.csv[m[m [31mex4.csv[m[m [31mstock_px_2.csv[m[m [31mex5.csv[m[m [31mtest_file.csv[m[m [31mex6.csv[m[m [31mtips.csv[m[m [31mex7.csv[m[m tmp.txt [31mexample.json[m[m [31mtseries.csv[m[m [31mfdic_failed_bank_list.html[m[m [31mvolume.csv[m[m frame_pickle [31myahoo_price.pkl[m[m [31mipython_bug.py[m[m [31myahoo_volume.pkl[m[m用内建的pickle可以直接读取任何pickle文件,或者直接用pandas.read_pickle:pd.read_pickle('../examples/frame_pickle')注意:pickle只推荐用于短期存储。因为这种格式无法保证长期稳定;比如今天pickled的一个文件,可能在库文件更新后无法读取。python还支持另外两种二进制数据格式:HDF5和MessagePack。下面会介绍一个HDF5,但是我们鼓励你多尝试一个不同的文件格式,看看他们能有多快,是否符合你数据分析的要求。另外一些可用的存储格式有:bcolz 和 Feather。1 Using HDF5 FormatHDF5格式是用来存储大量的科学数组数据的。这种格式还能用于其他一些语言。其中HDF表示hierarchical data format。每一个HDF5格式能春初多个数据集,并支持metadata。元数据(meta data)——“data about data” 关于数据的数据,一般是结构化数据(如存储在数据库里的数据,规定了字段的长度、类型等)。元数据是指从信息资源中抽取出来的用于说明其特征、内容的结构化的数据(如题名,版本、出版数据、相关说明,包括检索点等),用于组织、描述、检索、保存、管理信息和知识资源。HDF5 支持多种压缩模式的on-the-fly compression(即时压缩),能让数据中一些重复的部分存储地更有效。HDF5对于处理大数据集是一个很好的选择,因为他不会把所有数据一次性读取到内存里,我们可以从很大的数组中有效率地读取一小部分。能用PyTables或h5py来访问HDF5数据,pandas也有提供一个high-level的交互界面。HDFStore类像dict一样能用来处理low-level细节:frame = pd.DataFrame({'a': np.random.randn(100)}) store = pd.HDFStore('../examples/mydata.h5')frame.head()cat ../examples/mydata.csvone;two;three 1;2;3 4;5;6 7;8;9store['obj1'] = frame store['obj1_col'] = frame['a'] store<class 'pandas.io.pytables.HDFStore'> File path: ../examples/mydata.h5 /obj1 frame (shape->[100,1]) /obj1_col series (shape->[100]) HDF5中的object能用像dict一样的API来提取:store['obj1']100 rows × 1 columnsHDFStore支持两种存储架构,fixed和table。后者通常更慢一些,但支持查询操作:store.put('obj2', frame, format='table')store.select('obj2', where=['index >= 10 and index <= 15'])put是存储的另一种写法,类似于之前的store['obj2'] = frame,但这种协防能让我们设置存储格式。pandas.read_hdf函数也很方便:frame.to_hdf('../examples/mydata.h5', 'obj3', format='table')pd.read_hdf('../examples/mydata.h5', 'obj3', where=['index < 5'])注意:如果我们是把数据存在远端服务器上,比如Amazon S3或HDFS,使用一些为分布式存储实际的二进制格式会更适合一些,比如Apache Parquet。如果是在本地处理很大数据量的话,推荐尝试PyTables和h5py看是否符合你的要求。因为很多数据分析问题都受限于I/O,所以用HDF5这样的工具能加快应用。注意:HDF5不是数据库(database)。它最适合一次写入,多次读取的数据库。尽管数据可以在任何时间多次写入一个文件,如果多个使用者同时写入的话,文件会被破坏。2 Reading Microsoft Excel Files(读取微软的excel文件)pandas支持读取表格型数据(excel 2003或更高)文件,使用ExcelFile class或pandas.read_excel函数。这些工具需要一些富家的包xlrd和openpyxl来分别读取XLS和XLSX文件。你可以通过pip或conda来安装。使用ExcelFile,创建一个instance,通过给xls或xlsx一个路径:xlsx = pd.ExcelFile('../examples/ex1.xlsx')保存在sheet里的数据,可以通过parse来读取为DataFrame:pd.read_excel(xlsx, 'Sheet1')如果要读取一个文件中的多个sheet,用ExcelFile会更快。但让你也能把文件名直接传递给pandas.read_excel:frame = pd.read_excel('../examples/ex1.xlsx', 'Sheet1') frame如果要把pandas数据写为Excel格式,你必须先创建一个ExcelWrite,然后用to_excel方法:writer = pd.ExcelWriter('../examples/ex2.xlsx')frame.to_excel(writer, 'Sheet1')writer.save()如果不适用ExcelWriter的话,可以直接传给to_excel一个path:frame.to_excel('../examples/ex2.xlsx')
0
0
0
浏览量1287
清晨我上码

pandas将dataframe数据导入MySQL

mysql操作创建数据库test和tablecreate database test;CREATE TABLE car (文章ID int, 链接 VARCHAR(255), 标题 VARCHAR(255), 发文机关 VARCHAR(255), 发文字号 VARCHAR(255), 来源 VARCHAR(255), 主题分类 VARCHAR(255), 公文种类 VARCHAR(255), 文件内容 LONGBLOB )pymysql操作python安装pymysqlpymysql连接数据库connect = pymysql.connect( host=self.MYSQL_HOST, db=self.MYSQL_DB, port=3306, user=self.MYSQ_USER, passwd=self.MYSQL_PWD, charset='utf8', use_unicode=False )创建游标cursor = connect.cursor()插入数据def insert_mysql(self, data_json): """ 数据插入mysql :param data_json: :return: """ sql = "insert into {}(文章ID,链接,标题,发文机关,发文字号,来源,主题分类,公文种类,文件内容) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)".format(mysql_table) try: self.cursor.execute(sql, (data_json['文章ID'], data_json['链接'], data_json['标题'],data_json['发文机关'], data_json['发文字号'],data_json['来源'],data_json['主题分类'],data_json['公文种类'],data_json['文件内容'])) self.connect.commit() print('数据插入成功') except Exception as e: print('e= ', e) print('数据插入错误')完整代码import pymysql import pandas as pd mysql_host = 'localhost' mysql_db = 'test' mysql_user = 'root' mysql_pwd = 'root' mysql_table = 'car' class MYSQL: def __init__(self): # MySQL self.MYSQL_HOST = mysql_host self.MYSQL_DB = mysql_db self.MYSQ_USER = mysql_user self.MYSQL_PWD = mysql_pwd self.connect = pymysql.connect( host=self.MYSQL_HOST, db=self.MYSQL_DB, port=3306, user=self.MYSQ_USER, passwd=self.MYSQL_PWD, charset='utf8', use_unicode=False ) print(self.connect) self.cursor = self.connect.cursor() def create_table(self): self.cursor.execute("""CREATE TABLE car (文章ID int, 链接 VARCHAR(255), 标题 VARCHAR(255), 发文机关 VARCHAR(255), 发文字号 VARCHAR(255), 来源 VARCHAR(255), 主题分类 VARCHAR(255), 公文种类 VARCHAR(255), 文件内容 LONGBLOB )""") def insert_mysql(self, data_json): """ 数据插入mysql :param data_json: :return: """ sql = "insert into {}(文章ID,链接,标题,发文机关,发文字号,来源,主题分类,公文种类,文件内容) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)".format(mysql_table) try: self.cursor.execute(sql, (data_json['文章ID'], data_json['链接'], data_json['标题'],data_json['发文机关'], data_json['发文字号'],data_json['来源'],data_json['主题分类'],data_json['公文种类'],data_json['文件内容'])) self.connect.commit() print('数据插入成功') except Exception as e: print('e= ', e) print('数据插入错误')导入数据并启动def main(): mysql = MYSQL() df = pd.read_excel('汽车行业政策文本研究.xlsx') print(df.columns) # orient='records', 表示将DataFrame的数据转换成我想要的json格式 data_json = df.to_dict(orient='records') for dt in data_json: print(dt) mysql.insert_mysql(dt) if __name__ == '__main__': main()使用pandas导入MySQL数据库连接引擎‘mysql+pymysql://[user]:[pwd]@localhost:3306/[database]?charset=utf8’import pymysql from sqlalchemy import create_engine pymysql.install_as_MySQLdb() engine = create_engine('mysql+pymysql://root:root@localhost:3306/test?charset=utf8') 使用to_sql方法name : str Name of SQL table.con : sqlalchemy.engine.(Engine or Connection) or sqlite3.Connectionschema : str, optional Specify the schema (if database flavor supports this). If None, use default schema.if_exists : {‘fail’, ‘replace’, ‘append’}, default ‘fail’   How to behave if the table already exists.   fail: Raise a ValueError.   replace: Drop the table before inserting new values.   append: Insert new values to the existing table.index : bool, default True   Write DataFrame index as a column. Uses index_label as the column name in the table.index_label : str or sequence, default None   Column label for index column(s). If None is given (default) and index is True, then the index names are used. A sequence should be given if the DataFrame uses MultiIndex.chunksize : int, optional Specify the number of rows in each batch to be written at a time. By default, all rows will be written at once.dtype : dict or scalar, optionalSpecifying the datatype for columns. If a dictionary is used, the keys should be the column names and the values should be the SQLAlchemy types or strings for the sqlite3 legacy mode. If a scalar is provided, it will be applied to all columns.method : {None, ‘multi’, callable}, optional  Controls the SQL insertion clause used:RaisesValueErrorWhen the table already exists and if_exists is ‘fail’ (the default).import pandas as pd df = pd.read_excel('汽车行业政策文本研究.xlsx') #将data写入数据库,如果表存在就替换,将data的index也写入数据表,写入字段名称为id_name df.to_sql('qiche',con=engine,schema='test',chunksize=10000,index=False,if_exists='replace')pandas读取sqlread_sql_tablepd.read_sql_table可以直接读取数据库的整个tablepd.read_sql_table('car',con=engine,schema='test')read_sql_querypd.read_sql_query通过执行sql_query来读取部分表格内容#sql_query = 'select * from car;' sql_query = 'select * from car where 公文种类 = "公告";' df_read = pd.read_sql_query(sql_query, engine) df_read
0
0
0
浏览量405
清晨我上码

Array-Oriented Programming with Arrays 数组导向编程

向量化的数组运算比纯python同等程度的运算要快很多。一个简单的例子,假设我们想要评价函数sqrt(x^2 + y^2)。np.meshgrid函数取两个1维的数组,产生一个2位的矩阵,对应于所有两个数组中(x, y)的组合:import numpy as np在进行书中的内容之前,先举个例子说明meshgrid的效果。meshgrid函数用两个坐标轴上的点在平面上画网格。用法:[X,Y]=meshgrid(x,y)[X,Y]=meshgrid(x)与[X,Y]=meshgrid(x,x)是等同的[X,Y,Z]=meshgrid(x,y,z)生成三维数组,可用来计算三变量的函数和绘制三维立体图这里,主要以[X,Y]=meshgrid(x,y)为例,来对该函数进行介绍。[X,Y] = meshgrid(x,y) 将向量x和y定义的区域转换成矩阵X和Y,其中矩阵X的行向量是向量x的简单复制,而矩阵Y的列向量是向量y的简单复制(注:下面代码中X和Y均是数组,在文中统一称为矩阵了)。假设x是长度为m的向量,y是长度为n的向量,则最终生成的矩阵X和Y的维度都是 nm (注意不是mn)。m, n = (5, 3) x = np.linspace(0, 1, m) y = np.linspace(0, 1, n) X, Y = np.meshgrid(x, y) xarray([ 0. , 0.25, 0.5 , 0.75, 1. ])yarray([ 0. , 0.5, 1. ])Xarray([[ 0. , 0.25, 0.5 , 0.75, 1. ], [ 0. , 0.25, 0.5 , 0.75, 1. ], [ 0. , 0.25, 0.5 , 0.75, 1. ]])Yarray([[ 0. , 0. , 0. , 0. , 0. ], [ 0.5, 0.5, 0.5, 0.5, 0.5], [ 1. , 1. , 1. , 1. , 1. ]])可以看到X和Y的shape都是3x5import matplotlib.pyplot as plt %matplotlib inline plt.style.use('ggplot') plt.plot(X, Y, marker='.', color='blue', linestyle='none')[<matplotlib.lines.Line2D at 0x107cddd30>, <matplotlib.lines.Line2D at 0x107cddeb8>, <matplotlib.lines.Line2D at 0x107ce5198>, <matplotlib.lines.Line2D at 0x107ce5358>, <matplotlib.lines.Line2D at 0x107ce5518>]可以用zip得到网格平面上坐标点的数据:z = [i for i in zip(X.flat, Y.flat)] z[(0.0, 0.0), (0.25, 0.0), (0.5, 0.0), (0.75, 0.0), (1.0, 0.0), (0.0, 0.5), (0.25, 0.5), (0.5, 0.5), (0.75, 0.5), (1.0, 0.5), (0.0, 1.0), (0.25, 1.0), (0.5, 1.0), (0.75, 1.0), (1.0, 1.0)]好了,下面继续进入书中的内容points = np.arange(-5, 5, 0.01) # 1000 equally spaced points xs, ys = np.meshgrid(points, points) # xs和ys是一样的 ysarray([[-5. , -5. , -5. , ..., -5. , -5. , -5. ], [-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99], [-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98], ..., [ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97], [ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98], [ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]])z = np.sqrt(xs ** 2 + ys ** 2) zarray([[ 7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985, 7.06400028], [ 7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815, 7.05692568], [ 7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354, 7.04985815], ..., [ 7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603, 7.04279774], [ 7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354, 7.04985815], [ 7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815, 7.05692568]])这里我们用matplotlib把图画出来:plt.imshow(z, cmap=plt.cm.gray); plt.colorbar() plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")<matplotlib.text.Text at 0x10dca7c18>1 Expressing Conditional Logic as Array Operations (像数组操作一样表示逻辑条件)numpy.where函数是一个向量版的三相表达式,x if condition else y。假设我们有一个布尔数组和两个数组:xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5]) yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5]) cond = np.array([True, False, True, True, False])假设如果cond中为true,我们去xarr中对应的值,否则就取yarr中的值。列表表达式的话会这么写:result = [(x if c else y) for x, y, c in zip(xarr, yarr, cond)] result[1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]这么做的话会有很多问题。首先,对于很大的数组,会比较慢。第二,对于多维数组不起作用。但np.where能让我们写得更简洁:result = np.where(cond, xarr, yarr) resultarray([ 1.1, 2.2, 1.3, 1.4, 2.5])np.where中第二个和第三个参数不用必须是数组。where在数据分析中一个典型的用法是基于一个数组,产生一个新的数组值。假设我们有一个随机数字生成的矩阵,我们想要把所有的正数变为2,所有的负数变为-2。用where的话会非常简单:arr = np.random.randn(4, 4) arrarray([[ 2.18194474, 0.15001978, -0.77191684, 0.18716397], [ 1.2083149 , -0.22911585, 1.30880201, 0.14197253], [ 0.65639111, -1.28394185, 0.65706167, 1.14277598], [-0.32639966, -0.26880881, -0.10225964, 0.4739671 ]])arr > 0array([[ True, True, False, True], [ True, False, True, True], [ True, False, True, True], [False, False, False, True]], dtype=bool)np.where(arr > 0, 2, -2)array([[ 2, 2, -2, 2], [ 2, -2, 2, 2], [ 2, -2, 2, 2], [-2, -2, -2, 2]])我们可以结合标量和数组。比如只把整数变为2,其他仍未原来的数字:np.where(arr > 0, 2, arr) # set only positive value to 2array([[ 2. , 2. , -0.77191684, 2. ], [ 2. , -0.22911585, 2. , 2. ], [ 2. , -1.28394185, 2. , 2. ], [-0.32639966, -0.26880881, -0.10225964, 2. ]])2 Mathematical and Statistical Methods (数学和统计方法)一些能计算统计值的数学函数能基于整个数组,或者沿着一个axis(轴)。可以使用aggregations(often called reductions,汇总,或被叫做降维),比如sum, mean, and std(标准差).下面是一些aggregate statistics(汇总统计):arr = np.random.randn(5, 4) arrarray([[-1.53575656, -1.39268394, -1.02284353, -1.03165049], [ 0.53301867, 0.50258973, -0.49389656, 0.24610963], [ 0.95377174, -1.57268184, 0.42969986, 1.22912566], [ 0.73686692, -2.82328155, 0.48018497, -1.38046692], [ 0.94164808, 0.19599722, -0.88779738, -0.87556277]])arr.mean()-0.33838045197794597np.mean(arr)-0.33838045197794597arr.sum()-6.767609039558919mean, sum这样的函数能接受axis作为参数来计算统计数字,返回的结果维度更少:arr.mean(axis=1)array([-1.24573363, 0.19695537, 0.25997886, -0.74667415, -0.15642871])arr.sum(axis=0)array([ 1.62954886, -5.09006038, -1.49465263, -1.81244489])这里arr.mean(1)表示,compute mean acros the columns(计算各列之间的平均值)。arr.sum(0)表示,compute sum down the rows(计算各行总和)。其他一些方法,像cumsum和cumprod不做汇总,而是产生一个中间结果的数组:arr = np.array([0, 1, 2, 3, 4, 5, 6, 7]) arr.cumsum()array([ 0, 1, 3, 6, 10, 15, 21, 28])上面的计算是一个累加的结果,0+1=1,1+2=3,3+3=6以此类推。np.cumsum?对于多维数组,accumulation functions(累积函数)比如cumsum,返回的是同样大小的数组,但是部分聚合会沿着指示的轴向较低维度进行切片:arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) arrarray([[0, 1, 2], [3, 4, 5], [6, 7, 8]])arr.cumsum(axis=0) # 沿着行加法array([[ 0, 1, 2], [ 3, 5, 7], [ 9, 12, 15]])arr.cumprod(axis=1) # 沿着列乘法array([[ 0, 0, 0], [ 3, 12, 60], [ 6, 42, 336]])3 Methods for Boolean Arrays(布尔数组的方法)sum是用来计算布尔数组中有多少个true的:arr = np.random.randn(100) (arr > 0).sum() # Number of positive values46有两个其他方法,any和all,对于布尔数组特别有用。any检测数组中只要有一个ture返回就是true,而all检测数组中都是true才会返回true。bools = np.array([False, False, True, False])bools.any()Truebools.all()False4 Sorting(排序)numpy中也有sort方法:np.random.randn? # 返回符合正态分布的数值arr = np.random.randn(6) arrarray([ 1.93663555, -1.29810982, 0.83366006, 0.51674613, 2.32879117, 1.07342758])arr.sort()arrarray([-1.29810982, 0.51674613, 0.83366006, 1.07342758, 1.93663555, 2.32879117])如果是多维数组,还可以按axis来排序:arr = np.random.randn(5, 3) arrarray([[-0.76658562, -1.00222899, 0.39039437], [ 0.23100317, -1.0581081 , 1.69177329], [ 1.0239365 , 0.84698669, -0.97911915], [ 0.76255951, 0.27828523, 0.41807172], [ 0.40792019, -1.19514714, -1.41666804]])arr.sort(1)arrarray([[-1.00222899, -0.76658562, 0.39039437], [-1.0581081 , 0.23100317, 1.69177329], [-0.97911915, 0.84698669, 1.0239365 ], [ 0.27828523, 0.41807172, 0.76255951], [-1.41666804, -1.19514714, 0.40792019]])上面是直接调用数组的sort方法,会改变原有数组的顺序。但如果使用np.sort()函数的话,会生成一个新的排序后的结果。一个计算分位数的快捷方法是先给数组排序,然后选择某个排名的值:large_arr = np.random.randn(1000) large_arr.sort()large_arr[int(0.05 * len(large_arr))] # 5% quantile-1.69086079738722435 Unique and Other Set Logic (单一性和其他集合逻辑)Numpy也有一些基本的集合操作用于一维数组。np.unique,能返回排好序且不重复的值:names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe']) np.unique(names)array(['Bob', 'Joe', 'Will'], dtype='<U4')ints = np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])np.unique(ints)array([1, 2, 3, 4])如果用纯python代码来实现的话,要这么写:sorted(set(names))['Bob', 'Joe', 'Will']np.in1d, 测试一个数组的值是否在另一个数组里,返回一个布尔数组:values = np.array([6, 0, 0, 3, 2, 5, 6])np.in1d(values, [2, 3, 6])array([ True, False, False,  True,  True, False,  True], dtype=bool)
0
0
0
浏览量346
清晨我上码

pandas使用教程:pandas计数count和索引设置set_index、reset_index

计数函数count导入excel数据表import pandas as pd df = pd.read_excel('team.xlsx') df name team Q1 Q2 Q3 Q4 0 Liver E 89 21 24 64 1 Arry C 36 37 37 57 2 Ack A 57 60 18 84 3 Eorge C 93 96 71 78 4 Oah D 65 49 61 86 ... ... ... ... ... ... ... 95 Gabriel C 48 59 87 74 96 Austin7 C 21 31 30 43 97 Lincoln4 C 98 93 1 20 98 Eli E 11 74 58 91 99 Ben E 21 43 41 74 100 rows × 6 columnscount计数(默认按行计数)df.count() name 100 team 100 Q1 100 Q2 100 Q3 100 Q4 100 dtype: int64count按列计数df.count(axis='columns') 0 6 1 6 2 6 3 6 4 6 .. 95 6 96 6 97 6 98 6 99 6 Length: 100, dtype: int64count多级索引将team设为行索引,df.set_index(["team", "name"]).count(level="team") Q1 Q2 Q3 Q4 team A 17 17 17 17 B 22 22 22 22 C 22 22 22 22 D 19 19 19 19 E 20 20 20 20效果等同于对team列先分组再计数df.groupby(by='team').count() name Q1 Q2 Q3 Q4 team A 17 17 17 17 17 B 22 22 22 22 22 C 22 22 22 22 22 D 19 19 19 19 19 E 20 20 20 20 20计数函数value_counts统计team列个数df['team'].value_counts() C 22 B 22 E 20 D 19 A 17 Name: team, dtype: int64统计team列个数,并按value值升序排序df['team'].value_counts(ascending=True) A 17 D 19 E 20 B 22 C 22 Name: team, dtype: int64统计team列个数,按value值升序排序并计算百分比df['team'].value_counts(ascending=True,normalize=True) A 0.17 D 0.19 E 0.20 B 0.22 C 0.22 Name: team, dtype: float64索引设置set_index将指定一列设置为索引 df.set_index('name') 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 ... ... ... ... ... ... Gabriel C 48 59 87 74 Austin7 C 21 31 30 43 Lincoln4 C 98 93 1 20 Eli E 11 74 58 91 Ben E 21 43 41 74 100 rows × 5 columns如果添加参数drop = False,表示不删除原来的name一列。inplace=True时,表示在原来的dataframe上修改,inplace=False时,表示不更改源dataframe。索引重设reset_index对于之前set_index之后的dataframe进行处理。更改原索引为新的一列,重新设置数字索引。drop = True时,原来的索引列name被删除。drop = False时,原来的索引列name继续保留到dataframe中。df.reset_index(drop=True) name team Q1 Q2 Q3 Q4 0 Liver E 89 21 24 64 1 Arry C 36 37 37 57 2 Ack A 57 60 18 84 3 Eorge C 93 96 71 78 4 Oah D 65 49 61 86 ... ... ... ... ... ... ... 95 Gabriel C 48 59 87 74 96 Austin7 C 21 31 30 43 97 Lincoln4 C 98 93 1 20 98 Eli E 11 74 58 91 99 Ben E 21 43 41 74 100 rows × 6 columns
0
0
0
浏览量761
清晨我上码

Pandas实用指南

深入介绍Pandas库,涵盖基础操作、数据清洗、分组聚合、合并连接、时间序列处理等核心功能。通过实例和实践,帮助学员高效运用Pandas进行数据分析,适合数据科学家和分析师
0
0
0
浏览量663
清晨我上码

【Opencv--实现抠图】求出连通区域所占的矩形框

1. 需求和方法1.1 需求本项目所要扣的物体是裂缝,也适用于其他的物体目标。1.2 方法   先对图像进行二值化;   再对图像求连通区域(Opencv–API);   挑选最大的连通区域位置(原图像0位置除外);   对该连通区域进行保存。2. 先验知识计算不规则连通区域图像二值化函数膨胀、腐蚀、开/闭操作3. 代码实现import cv2 import numpy as np def getRec(img): img2 = img.copy() kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) # 可能存在断的裂缝,存在多个连通区域,此时进行膨胀操作 img = cv2.dilate(img, kernel, 3) # 求黑白图像中的连通区域 _, _, stats, _ = cv2.connectedComponentsWithStats(img, connectivity=8) img1 = np.zeros_like(img) a = [s[-1] for s in stats[1:]] a = np.array(a) # 选择最大的连通区域,作为裂缝区域 ind = np.argmax(a) + 1 # 存储裂缝区域的外接矩形 x, y, h, w = stats[ind][0], stats[ind][1], stats[ind][2], stats[ind][3] img1 = img2[y:y+w, x:x+h] return img1 img_name = 'a.jpg' img = cv2.imread("image/ori/" + img_name, 0) img1 = getRec(img) cv2.imwrite('image/final/'+ img_name, img1)4. 前后效果4.1 原图像4.2 处理后
0
0
0
浏览量1226

履历