技巧:pandas处理操作集合

初始化 一个新的DataFrame

#  利用 列名 生成一个空的  DataFrame
columns = df.columns
new_df =  pd.DataFrame(columns=columns)


# 利用词典 来生成一个空的 DataFrame

data = [{a:1,b:2},{a:1,b:23}]
new_df =  pd.DataFrame(data)

nan 判断

age_null = pd.isnull(titanic_survival[‘age’])

平移数据 shift

df = pd.DataFrame({'A':[1,2,3,4,5,6],'B':[1,1,12,2,9,3]})
df['A_2'] = df['A'].shift(periods=2, axis=0,fill_value=0)
print(df)

print("- - "*12)
df_1 = df.shift(periods=2, axis=1)

print(df_1)

输出:

  A   B  A_2
0  1   1    0
1  2   1    0
2  3  12    1
3  4   2    2
4  5   9    3
5  6   3    4
- - - - - - - - - - - - - - - - - - - - - - - - 
    A   B  A_2
0 NaN NaN  1.0
1 NaN NaN  2.0
2 NaN NaN  3.0
3 NaN NaN  4.0
4 NaN NaN  5.0
5 NaN NaN  6.0

  • periods:移动的幅度,int类型,默认值为1。移动的列数,上下移动通过数字正负来判断
  • axis:控制移动的方向,如果为0或者’index’,则上下移动,如果为1或者’columns’,则左右移动

diff 函数

diff函数是用来将数据进行某种移动之后与原数据进行比较得出的差异数据。默认参数:periods=1,执行了两个步骤:

  • df.shift(periods=1, axis=0)
  • df - df.shift()

这里diff只有periods一个参数,用来控制上下移动的列数。

df = pd.DataFrame({'A':[1,2,3,9,5,6],'B':[1,1,12,2,9,3]})
print(df['A'].diff())


输出:


0    NaN
1    1.0
2    1.0
3    6.0
4   -4.0
5    1.0
Name: A, dtype: float64

agg 一些参数含义

  • mean:平均值
  • std:标准差
  • skew:偏度,衡量随机分布的不均衡性,偏度=0,数值相对均匀的分布在两侧
  • kurtosis:峰度,概率密度在均值处峰值高低的特征
import numpy as np
from scipy import stats
 
x = np.random.randn(10000)
mu = np.mean(x, axis=0)
sigma = np.std(x, axis=0)
skew = stats.skew(x)
kurtosis = stats.kurtosis(x)

遍历pandas 数据

# 遍历
    for index,row in list.iterrows():
         print(row['a'])
         print(row['b'])


# df.apply()遍历行并访问函数的多个列。

def valuation_formula(x, y):
    return x * y * 0.5
 
df['price'] = df.apply(lambda row: valuation_formula(row['x'], row['y']), axis=1)

这种遍历很慢,不建议用,但是有一些需求可能也会用到

nan判断及处理

利用pd.isnull(x) 对nan数据的处理

def tranNanToFive(x):
    if pd.isnull(x):
        return 200
    return x

dfNewTwo['c'] = dfNewTwo['c'].map(tranNanToFive) 

可以有效的nan数据转换,当然也可以删除列数据为nan的行数据,例如如下:

# 删除为 nan的数据,根据列数据来判断,删除行数据
dfNewTwo.drop(dfNewTwo[np.isnan(dfNewTwo['c'])].index, inplace=True)

多列 过滤方法

可以用 | 、& 这两种运算符,但是使用这种方式,必须是使用 () 括号,不然会报错

showTtrData = replyData_sub[(replyData_sub['mt']>8) & (replyData_sub['mt']<13)]

更新 部分数据

有时候 需要更新pandas部分数据,因为有些值为null,需要更新一下。

dict_test = {1:2,2:3}
aaa = pd.DataFrame({'A':[1,2,2],'B':['1',np.nan,'12']})


## 根据坐标 来更新
for i in range(len(aaa)) :
    if pd.isnull(aaa.loc[i,'B']):
        aaa.loc[i,'B'] =  dict_test[aaa.loc[i,'A']]

print(aaa)

更改pandas 列数据 标签

bodyType 列原本是一个数字类型,现在改为类目类型,这样就表明限制了这些数据的范围,我看到有一些源码会对lightGbm算法里做限制,限制某些类的类型,可能是有帮助吧

df_last["bodyType"] = df_last["bodyType"].astype('category')

将某一列标签化的简单方法

dictData = dict(zip(data['bodyType'].unique(), range(data['bodyType'].nunique())))
print(dictData)

备注:

  • data[‘bodyType’].unique():获取这一列数据的不重复的字段值
  • data[‘bodyType’].nunique():获取这一列数据 不重复 字段 的个数
  • range(data[‘bodyType’].nunique()):也就是获取0到一个范围值
  • zip:两个数组融合为一个元组
  • dict:将元组转换为词典,如果是list就不可以转换为词典

这样实现方法很简单,但是有一个问题,就是恢复的

copy

需要一个新的DataFrame,跟之前一模一样的DataFrame,可以利用copy函数

    dfNew = df.copy()

dfNew就是一个新的DataFrame。

统计某列 数据出现次数

col_count = dict(data['bodyType'].value_counts())  #统计某列元素的值的个数
print(col_count)

比较直接了当

统计占比

统计次数不了解占比情况,如是我们来看下占比情况:


df = pd.DataFrame({'year': [2019, 2019, 2018], 'month': [10, 9, 8],  
                  'C': [1, 2, 3]})

df["year"].value_counts(1)

会输出统计次数和占比情况

指定两列形成词典

modelData = test.set_index('id')['model']
print(modelData[1])

利用 set_index 选择id 这一列作为index,然后再选取“model”列作为列数据,这样就可以通过id来读取model 数据,当然也可以通过其他列作为词典的key,例子如下:

modelData = train.drop_duplicates('model').set_index('model')['bodyType']
print(modelData['3c974920a76ac9c1'])

通过drop_duplicates 去重,这样作为保证词典的key中没有重复值。

一个Dataframe 更新 另一个Dataframe

场景大概是这样的:我有两个Dataframe,现在我要用一个Dataframe列来更新另一个Dataframe,如何来做呢?至少更新的时候,我们可能需要指定一个key,因为这个key可以作为这一行的标识,类似id的作用,有两种方法:

import pandas as pd
import numpy as np
data1 = pd.DataFrame(np.arange(0,25).reshape(5,5),columns= list('abcde'),index=list('mnopq'))
data2 = pd.DataFrame(np.arange(30,46).reshape(4,4),columns= list('abcd'),index=list('mnop'))
data2.loc[:,'a'] = np.arange(0,16,5)

### 这个方法可以帮助我们来做这件事
for r1 in range(data1.shape[0]):
    for r2 in range(data2.shape[0]):
        #只有data2的值不为空时才更新data1
        if (data1.iloc[r1]['a'] == data2.iloc[r2]['a']) and pd.isna(data2.iloc[r2]['b']) != True:
            #下面不能写为data1.iloc[r1]['b'],这样写数据不会更新
            data1.iloc[r1,data1.columns == 'b'] = data2.iloc[r2,data1.columns == 'b']

print(data1)

但是上面的时间复杂度有点高,当dataframe数据比较多的时候,上面的方法特别耗时,cpu都可能会达到100%,
于是还有一种更新方式:通过词典的方式,这种方式也叫简单粗暴,代码如下:

 dic_id_sal = {}
 for row in sub.itertuples():
     dic_id_sal[getattr(row, 'id')] = getattr(row, 'forecastVolum')
        
testData['salesVolume'] = testData['id'].map(dic_id_sal)

这种方式比较快,大家也可以对比一下。

空的DataFrame

df_empty = pd.DataFrame(columns=['A', 'B', 'C', 'D'])

df_empty['A'] = [1,2]

只给出列名, 没有给数据,这样在后面就可以去塞进去数据。

更换列顺序、随机插入列

想要更换列顺序,大致的操作就是先把该列pop出来,然后在insert。

df = pd.DataFrame(np.arange(0,25).reshape(5,5),columns= list('abcde'),index=list('abcpq'))
#last_col = df.pop(df.columns[-1])  # 根据坐标位置来 pop 数据
last_col = df.pop('c')  # 根据列名来 pop 数据
print(df)
df.insert(0, last_col.name, last_col)
print(df)

平移数据

平移数据一般用到获取历史数据,比如现在是8月,我要获取5、6、7月份的销量作为8月份的特征,可以通过月份数字来平移数据,这样就可以得到历史数据。

	group = "month"
	col = "sales"
    // 特征名称
    add_feat = []
    for i in range(5,8):
		// 过渡的列名
        aa = '{}_{}'.format(col,i)
       // month +1 ,5月变成6月,6月变成7月
        df[aa] = df[group] + i
        // 设置坐标,也叫设置索引,什么意思呢,这里index是主键的,数组下标的含义,可以通过下标来直接读取数据,
        //  有点像数组根据下标来读取数据,aa 作为下标,
        df_last = df[~df[col].isnull()].set_index(aa)
        // 通过map 方法对df group列 根据aa下标 来获取销量数据,aa对应的是上一个月的销量数据,并生成新的列名
        
        bb = 'shift_{}_{}_{}'.format(col,group,i)
        df[bb] = df[group].map(df_last[col])

        // 删除用来过渡的列
        del df[aa]
        // 获取新的特征名称
        add_feat.append(bb)
    
    return df, add_feat

append 增加一列数据

import pandas as pd
from numpy.random import randint
df = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))
for i in range(5):
    s = pd.Series({'lib':randint(-1,1), 'qty1':randint(-1,1), 'qty2':randint(-1,1)})
    # 这里 Series 必须是 dict-like 类型
    df = df.append(s, ignore_index=True)
    # 这里必须选择ignore_index=True 或者给 Series 一个index值
 
    #也可以直接填写字典格式进去
    df = df.append({'lib':randint(-1,1), 'qty1':randint(-1,1), 'qty2':randint(-1,1)}, ignore_index=True)

grouped对某一列取不重复的数据


grouped.agg({"col3": "count", "col4": pd.Series.nunique})

drop_duplicates方法

对DataFrame 使用 drop_duplicates方法 来得到某列不重复的数据,不过其实可以可以一种方式,


data=pd.DataFrame({'aa':['A','A','A','A'],'bb':[50,50,30,30]})

print(data)

data_label = data.drop_duplicates('bb')
print("- - "*20)
print(data_label)

# 结论:这个drop_duplicates 用来过滤某列的,但是得到的还是这个那个数据集上的数据,只不过不太清楚多个数据集保留多个 选取哪一个?

输出结果:

  aa  bb
0  A  50
1  A  50
2  A  30
3  A  30
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  aa  bb
0  A  50
2  A  30

另一种方式也可以达到目的,但得到的是列表对象,不是dataframe对象,所以有些差别。

print(data['bb'].unique())

分组求分组后某一列的差异值

比如我们有一个class列,一个time列,我想class的数据分组,然后求分组后的time diff值,正常的写法就是:

    group_ship = df_new.groupby(['class'])
    df_new['t_diff'] = group_ship['time'].diff().iloc[1:]
  • iloc[1:]:去掉第一行记录

这里与一个问题,按class 分组后的数据 如果不是按照time排序的,那么结果计算的差值就不具备参考意义,所以这里需要排序,而且这个groupby 的diff 计算太慢了,需要更快的速度。于是就自己想了先排序,再diff,最后分组来提高速度。

df_new.sort_values(['year', 'month'], ascending=[False, True],inplace = True)
df_new['diff'] = df_new['time'].diff()

group_ship = df_new.groupby(['class'])
df_new['t_diff'] = group_ship['diff'].iloc[1:]

miss Data analyse 缺失值分析

分析出有多少个缺失值,主要是缺失值的比例。

all_data_na = (all_data.isnull().sum() / len(all_data)) * 100
all_data_na = all_data_na.drop(all_data_na[all_data_na == 0].index).sort_values(ascending=False)[:30]
missing_data = pd.DataFrame({'Missing Ratio' :all_data_na})
missing_data.head(20)

关联分析

分析出特征之间的相关性,帮助去除一些强关联的特征,这些特征太多了没帮助

corrmat = train.corr()
plt.subplots(figsize=(12,9))
sns.heatmap(corrmat, vmax=0.9, square=True)

nan数据的分组填充

利用众位数来填充,这里可以采用很多种方式,比如后一数值或者前一数值填充。

all_data['KitchenQual'] = all_data['KitchenQual'].fillna(all_data['KitchenQual'].mode()[0])

pad/ffill:用前一个非缺失值去填充该缺失值
backfill/bfill:用下一个非缺失值填充该缺失值


    df_train = df_train.fillna(method="bfill",axis=0)

另一种写法是把数据进行直接转换,transform方式:

#Group by neighborhood and fill in missing value by the median LotFrontage of all the neighborhood
all_data["LotFrontage"] = all_data.groupby("Neighborhood")["LotFrontage"].transform(
    lambda x: x.fillna(x.median()))

类型数据编码

from sklearn.preprocessing import LabelEncoder
cols = ('FireplaceQu', 'BsmtQual', 'BsmtCond', 'GarageQual', 'GarageCond', 
        'ExterQual', 'ExterCond','HeatingQC', 'PoolQC', 'KitchenQual', 'BsmtFinType1', 
        'BsmtFinType2', 'Functional', 'Fence', 'BsmtExposure', 'GarageFinish', 'LandSlope',
        'LotShape', 'PavedDrive', 'Street', 'Alley', 'CentralAir', 'MSSubClass', 'OverallCond', 
        'YrSold', 'MoSold')
# process columns, apply LabelEncoder to categorical features
for c in cols:
    lbl = LabelEncoder() 
    lbl.fit(list(all_data[c].values)) 
    all_data[c] = lbl.transform(list(all_data[c].values))

# shape        
print('Shape all_data: {}'.format(all_data.shape))

简单一点的做法,比如:


le = LabelEncoder()
items['family'] = le.fit_transform(items['family'].values)

对数据取log

# 取log对值做控制,可以调整为某一个分布情况
train["SalePrice"] = np.log1p(train["SalePrice"])

# 对预测结果再e幂云算回来
stacked_pred = np.expm1(stacked_averaged_models.predict(test.values))

根据分布情况做数据转换

查看数据分布情况

numeric_feats = all_data.dtypes[all_data.dtypes != "object"].index

# Check the skew of all numerical features
skewed_feats = all_data[numeric_feats].apply(lambda x: skew(x.dropna())).sort_values(ascending=False)
print("\nSkew in numerical features: \n")
skewness = pd.DataFrame({'Skew' :skewed_feats})
skewness.head(10)

进行转换:

skewness = skewness[abs(skewness) > 0.75]
print("There are {} skewed numerical features to Box Cox transform".format(skewness.shape[0]))

from scipy.special import boxcox1p
skewed_features = skewness.index
lam = 0.15
for feat in skewed_features:
    #all_data[feat] += 1
    all_data[feat] = boxcox1p(all_data[feat], lam)
    
#all_data[skewed_features] = np.log1p(all_data[skewed_features])

one hot encode

get_dummies 是利用pandas实现one hot encode的方式。详细参数请查看官方文档,



采样 resample

index = pd.date_range('1/1/2000', periods=9, freq='T')
series = pd.Series(range(9), index=index)

# close : 控制 开闭 区间
# lable :  控制 头尾, 

print(series.resample('5min',closed='left',label='right').sum())
print(series.resample('5min',closed='left',label='left').sum())

print(series.resample('5min',closed='right',label='left').sum())
print(series.resample('5min',closed='right',label='right').sum())

文本数据方法 slice( )

Series.str.slice(start=None, stop=None, step=None)按下标截取字符串
参数:
start : 整型或缺省
stop : 整型或缺省
step : 整型或缺省
Returns:
序列Series/索引Index

Series.str.slice_replace(start=None, stop=None, repl=None)按下标替换
参数:
start : 整型或缺省
stop : 整型或缺省
repl : 字符串或缺省,要替换的字符串
返回:
序列Series/索引Index

参考博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值