当前位置:   article > 正文

pandas 常用操作_df.iat

df.iat

update:190705

 

访问具体位置

 

某列。

aaa = df['a'] = df.a = df.get('a')

 

某行

df[0:100],返回前100行。  直接括号[]里面写条件,优先级似乎是先匹配列,再匹配行。容易出问题,不推荐这种写法。

等价于 df.ix[0:100] 。

 

不等价于 df.loc[0:100]。 使用loc会包含第101行,即index=100的行,即最后一行,总共返回0~100,计101行。

 

df.irow(i) ,i行,不常用,不推荐。

df.iloc[i] , i行,返回series。

df.iloc[i:j],(i,j-1)行,返回dataframe。

 

 

某行某列

按offset  df.iat[0,0],第一行第一列  #iat是特化的单元素访问方法,如果只需要对单元素进行取值、赋值的操作,用起来就很舒服。

              df.loc[0,[0]]

              df.ix[0,[0]]

按name  df.loc['hello',['world']] ,'hello'行,'world'列。 特色就是目标列需要用一个list来装

              df.at[0,'world'],at理论上只支持name访问单个元素。但是由于我们日常使用时,不定义行的index时,默认的行name其实就是012345这些数字。

  1. #补充一下最常用的loc
  2. a=df.loc[0,['cre_time']]
  3. b=df.loc[0,'cre_time']
  4. print(type(a))
  5. print(type(b))
  6. #输出
  7. <class 'pandas.core.series.Series'>
  8. <class 'pandas._libs.tslibs.timestamps.Timestamp'>
  9. #可以看出,loc在访问单列时,若不加括号,效果等同于.at

 

多行多列

使用ix,对列同时支持name和offset。

N行N列 df.ix[0:3,[0,1,2]]   前3行和前3列。

N行N列 df.ix['one':'two',[0,1]  使用index的name代替offset。

N行1列  df.ix[[1, 2], [0]]

1行N列  df.ix[0,[2,3,4]]

列比较蠢,需要多一个括号来装,还不支持冒号slice语法。
 

使用loc,对列只能使用列name。

N行N列 df.loc[0:3,['hello','world']]

 

使用iloc,对列只能使用offset。

N行N列 df.iloc[0:3,[0,1,2]]

 

总结:

at与iat只能取单个。

ix与loc俩兄弟可以取多行,多行包括单行。

loc与iloc,这俩兄弟取多行时,会返回最后一行。而ix不会,直接使用[]也不会。

at与iat的关系,同loc与iloc。不加i表示只支持用name,加了i表示只能用offset(或者说以相对位置作为index)。

 

取行,支持 【单行,枚举多行,slice】 3种操作。

取列,仅支持【单列,枚举多列】2种操作。

无论什么函数,都不允许对列进行slice操作。

 

注意1:

多行多列的函数访问单个元素时,容易各种报错。

举例,df.iloc[0,[0]],访问某个单元格,但是返回是dataframe或者series类型的,某些对单个元素才合法的操作(e.g.赋值),此时不合法。

因此,访问某个单个元素时,使用 df.at 与 df.iat 是好习惯。

  1. #报错x:df.loc[0,['happy']] = 'hello'
  2. #推荐√:df.iat[0,0] = 'hello'
  3. #不推荐√: df.loc[0,'happy'] = 'hello'

哭着回来提醒一下,iat的处理速度不知道比loc高到哪里去了。

处理1200w行的数据,用loc告诉我需要100小时,iat变成9分钟,这差距。

注意2:

name访问单行/单列时,若索引指向的行/列不存在,则新建一个。

e.g. 我们常用 df['aa']=[1,2,3],本质上就是df['aa']指向了一个不存在的列,则新建之,并用传入的值来初始化。

很实用的特性。详见下文“逐行逐列添加数据”。

 

 

去重

先查重

df.duplicated()

返回一系列的bool值,表示第i行是否为重复行。

#想按每列去重,好像可以设置axis,待证。

 

确认有重复值存在了,再删除重复项

  1. #全部列进行比较,全部列的值都相同的2行才判定重复
  2. df.drop_duplicates()
  3. #只对指定列进行比较
  4. df.drop_duplicates(['happy'])
  5. #默认的方向是自前向后,所以上数下第二行开始被判定为重复丢弃。
  6. #可以手动更改方向,保留last one,删除former one
  7. df.drop_duplicates(['happy'],keep='last',inplace=True)
  8. #inplace生效时原地修改,默认为False

 

 

空缺值

判断存在

  1. df.isnull().sum()
  2. df.notnull()

填充0,或者丢弃

  1. df['happy']=df['happy'].fillna(0)
  2. df['happy']=df['happy'].dropna()
  3. #也可以不指定列,直接丢弃全部含空缺值的行
  4. df=df.dropna()

比较特殊一点的是,用另外一列的值去填na。

df['happy']=df['happy'].fillna(df['wow'])

 

修改列名

  1. #暴力全修改,注意与现存的数量必须匹配
  2. df.columns=['aa','bb','cc']
  3. #rename传入字典,把cc列改为CC列
  4. df = df.rename(columns={'cc':'CC'}) #有inplace参数

 

 

强制转为category类

df["Status"] = df["Status"].astype("category")

备注:虽然astype()函数有inplace参数,但是在我的环境内测试,即使inplace=True也无法成功更改dtype。

不知是否为bug。(pandas == 0.23.4,numpy==1.15.4)  

出于这个原因,推荐使用 =  赋值的形式。

 

去空格 

df['term']=df['term'].map(str.strip)

本质上是pd.Series.map()函数的一个特殊应用场景。

关于map请看 《pandas map与apply》

 

大小写 

  1. 大写
  2. df['term']=df['term'].map(str.upper)
  3. 小写
  4. df['term']=df['term'].map(str.lower)
  5. 首字母大写
  6. df['term']=df['term'].map(str.title)

同上,map的应用。

 

指定某列为索引

  1. df_new = df.set_index('new_index_col')
  2. df_new = df.set_index(['col1','col2'])

注意,这个操作可以用.reset_index(drop=False) 回溯。

完美回溯,无损回溯!

借用这个回溯功能,可以引出下一个非常实用的性能。

 

 

 

分组统计后设为新表

主要利用到.reset_index(drop=False)的能力。

  1. g =df.groupby(['id','time'])
  2. new_df = pd.DataFrame(g.size(),columns=['count'])
  3. new_df = ad_up.reset_index(drop=False)
  4. #得到一个新表
  5. 'id','time','count'

 

逐行逐列添加数据

 

用合法的语句访问单行/单列时,若索引指向的行/列不存在,则新建一个。

实例

  1. #预定义列,希望一行一行的添加进来
  2. df = pd.DataFrame(columns=['aa','bb'])
  3. names= ['peter','david']
  4. for i in range(2):
  5. key = boys[i]
  6. #此处我们用.loc访问index=key的一整行(省略列索引即可达到此效果)
  7. df.loc[key] = [i,i+1]
  8. #但由于index=key的这行不存在,所以会新建'peter'行,.etc
  9. print(df)

打印结果

  1. aa bb
  2. peter 0 1
  3. david 1 2

可是日常需求中,一般人名需要单独作为一列。使用上面说的.reset_index()即可

df= df.reset_index(drop=False)
  1. index aa bb
  2. 0 peter 0 1
  3. 1 david 1 2

 改columns就很简单了。

 

预定义行,希望一列一列地加数据同理。

 

注意:

当df为空时,实际上第0行是不存在的,所以使用df.iloc[0] = [....]添加第0行,会报错。single position index is out of bound.

也就是说,这种方法只对使用name形式的访问生效,即df.loc[0]。 此时的0并非offset,而是某行的name。

同理,创建不存在的列 df['hello'] = [...],同样是以列名进行访问,所以能够生效。

 

分列

  1. #想拆分df['target']这列
  2. #保留原来的index需要专门指定index
  3. new_df = pd.DataFrame(
  4. [x.split('-') for x in df.target],
  5. columns=['people','age'],index=df.index)
  6. #按左表的行索引合并
  7. df = pd.merge(df,new_df,left_index=True)

 

分级标记Cut

  1. bins = [0, 5, 10, 15, 20]
  2. level_name = ['A', 'B', 'C', 'D']
  3. df['level'] = pd.cut(df['value'], bins, labels=level_name)
  4. #依次对df['value']列进行比较
  5. #如果落在左闭右开区间内[,)
  6. #则匹配对应的level_name

注意,虽然在python里面字符串是可以比大小的,但是实测dataframe的某列若为字符串形式的,哪怕是字符串型的数字,应用pandas.cut函数也会报奇怪的错误。

如果是字符串数字,请自行处理,或者自定义一个cut。

pd.cut()返回的series,类型是category,枚举型。

 

 

删除行列

 

删除行,drop()

  1. data = data.drop([3,4])
  2. data.drop([3,4],inplace=True)

删除列,del(),pop()

  1. del data['age']
  2. data
  3. _ = data.pop('age') #pop后原df不保留该列
  4. #也可以用drop删除列
  5. df.drop(columns=['hello','world'], axis=1, inplace=True)

 

按列合并dataframe

result = pd.merge(df1, df2, on='uid', how='left/right/inner/outer’,shuffixes=('_x','_y')

 

tips:

如果2个表存在重复的列名,就会由shuffixes来为重复列名添加后缀,这样很麻烦。

我们需要手动删除一列,再对另一列改名。

其实有避免这种重复劳动的解决方案。

  1. #排除重复列名,再进行merge
  2. def single_merge(df1,df2,on='uid',how='left'):
  3. s1 = set(df1.columns)
  4. s2 = set(df2.columns)
  5. s3 = s2-(s1&s2)
  6. s3.add(on)
  7. cols_to_use = list(s3)
  8. res = pd.merge(df1,df2[cols_to_use],on=on,how=how)
  9. return res
  10. train_csv = single_merge(train_csv,listing_info,on='listing_id',how='left')

 

 

四则运算

 

series和dataframe都有

.add

.sub

.multiple

.div

注意1:

如果是add或者sub另外一个series,则要考虑行index对齐的问题,若两个series的index完全不一致,则全部nan。被这个坑了。

使用前可以将其中之一reset_index(drop=True),或者将被加/减数list()一下。

 

注意2:

跟numpy里面的四则运算的函数名字区别!

np.add(a1,a2)

np.sub(a1,a2)

np.multiply(a1,a2) ,注意numpy的乘法是multiply!

np.divide(a1,a2)   #python3中的np.divide == np.true_divide,且不能用 a=np.array([1,2,3]) ,a.divide()会报不存在

 

还有一些np.abs()之类的隐藏坑,pandas里面是没有的貌似。

 

 

时间转换

时间戳:定义为1970年之后的秒数

  1. import time,datetime
  2. #1970年秒数转time结构体
  3. time_struct = time.localtime(1462482700)
  4. print(time_struct)
  5. #time.struct_time(tm_year=2016, tm_mon=5, tm_mday=6, tm_hour=5, tm_min=11, tm_sec=40, tm_wday=4, tm_yday=127, tm_isdst=0)
  6. #这是个time结构体,可以直接访问 .tm_year等属性获取对应值
  7. #结构体转字符串
  8. time_string = time.strftime("%Y-%m-%d %H:%M:%S", time_struct)
  9. print(time_string)
  10. #2016-05-06 05:11:40
  11. #字符串转datetime对象
  12. datetime_obj = datetime.datetime.strptime(timeDateStr,"%Y-%m-%d %H:%M:%S")
  13. print(datetime_obj)
  14. print(type(datetime_obj))
  15. #2016-05-06 05:11:40
  16. #<class 'datetime.datetime'>
  17. #你别看打印出来跟string没什么区别,类型已经改了。
  18. #datetime对象转time结构体
  19. struct_time = datetime_obj.timetuple()
  20. #time结构体转时间戳 (秒数)
  21. time_stamp = time.mktime(struct_time)
  22. #datetime对象转字符串
  23. time_string = datetime.datetime.strftime(datetime_obj,"%Y-%m-%d %H:%M:%S")
  24. #等价于
  25. time_string = datetime_obj.strftime("%Y-%m-%d %H:%M:%S")

直接取数

  1. #datetime对象
  2. year = datetime_obj.year
  3. month = datetime_obj.month
  4. day = datetime_obj.day
  5. hour = datetime_obj.hour
  6. minute = datetime_obj.minute
  7. second = datetime_obj.second
  8. #time_struct同理,直接.访问属性即可。

 

补充:datetime对象相减会返回timedelta类的对象。

  1. start_time ='20190305051140'
  2. end_time ='20190308051145'
  3. start_obj = datetime.datetime.strptime(start_time,"%Y%m%d%H%M%S")
  4. end_obj = datetime.datetime.strptime(end_time,"%Y%m%d%H%M%S")
  5. i_am_time_delta = end_obj - start_obj
  6. print(i_am_time_delta)
  7. # 3 day, 00:00:00
  8. #timedelta比较特殊一点
  9. #其内部只储存days,seconds,microseconds
  10. days = i_am_time_delta.days
  11. seconds = i_am_time_delta.seconds
  12. #虽然timedelta打印出来是 3 day, 00:00:00 ,这样子的。
  13. #但后面的时分秒是用seconds算出来的
  14. #内部并不存在 .hour 和 .minute 属性。
  15. #但在构造函数里面,我们却可以指定hours 和 minutes !
  16. time_delta_1 = datetime.timedelta(days = 1) #set .days to 1
  17. time_delta_2 = datetime.timedelta(hours = 0.5)
  18. print(time_delta_2.seconds)
  19. #半个小时,30分钟,1800秒。 打印出来刚好是1800
  20. time_delta_3 = datetime.timedelta(hours = 1, minutes = 1 ,seconds = 1 )
  21. print(time_delta_3.seconds)
  22. #得到3600+60+1=3661秒
  23. #所以说,如果想对一个datetime类对象,加个1天,就可以
  24. time_obj = time_obj + datetime.timedelta(days = 1)
  25. #返回的类型,还是datetime.datetime类对象
  26. #减一天,加一个小时,以此类推

#本质上跟c里的时间处理差不多。《C++中日期处理总结》

#想看更多关于时间的api推荐这篇:http://www.cnblogs.com/lhj588/archive/2012/04/23/2466653.html

 

自带函数

  1. import pandas as pd
  2. pd.to_datetime()
  3. #将形如xx的字符串
  4. #2018-02-04 08:28:15
  5. #转为datetime类
  6. df = pd.DataFrame({'id':['aa','bb','cc'],
  7. 'time':['2018-02-04 08:28:15',
  8. '2018-02-04 09:30:11',
  9. '2018-02-04 10:31:23']})
  10. df['datetime'] = pd.to_datetime(df['time'])
  11. #df.dtypes
  12. #
  13. #得到datetime类之后,可以用.dt直接访问属性,拿到月日
  14. df['month'] = df['datetime'].dt.month
  15. df['mday'] = df['datetime'].dt.day
  16. #此外,官方示例还提供了
  17. #s.dt.hour
  18. #s.dt.second
  19. #s.dt.quarter
  20. #s.dt.date #2018-02-04

读取csv文件时,自动解析日期

  1. import pandas as pd
  2. #将time这一列解析为datetime
  3. df = pd.read_csv('mydata.csv',header=0,date_parser = 'time')
  4. #多列
  5. df = pd.read_csv('mydata.csv', parse_dates=['auditing_date', 'due_date', 'repay_date'])

 

小技巧:

由于数字运算比字符串运算更快捷。

所以用数字存储month和day,然后

month_day=month*100+day

这样得到的数字直接表示日期。比如1月5号=105,  12月31日=1231。

 

 

 

占用内存

  1. import pandas as pd
  2. memory = df.memory_usage().sum() / 1024 ** 3 #单位GB
  3. print('Data occupies {} GB memory.'.format(memory))

 

 

 

进行groupby后求众数mode

pandas:对dataframe进行groupby后求众数mode

 

 

保存为csv

df.to_csv('a.csv',header=True)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/210288
推荐阅读
相关标签
  

闽ICP备14008679号