文件的读写
pandas集成了很多文件格式的读写,R中除了文本格式,其他格式都分散在各种包中,pandas使用体验更好一点。
另外,R的read.csv
读取数据是,默认stringsAsFactors = TRUE
,这个我认为是体验不太好的地方,好在4.0版本开始修改默认为FALSE
。
强烈推荐R中的一个神级包data.table
,对大数据的读写有超高的效率,Python中也实现了类似的包,datatable
。
格式 | Python读 | Python写 | R读 | R写 |
---|---|---|---|---|
CSV | pd.read_csv | pd.to_csv | read.csv data.table::fread | write.csv data.table::fwrite |
TXT | pd.read_table | pd.to_csv | read.table | write.table |
固定宽度Text | pd.read_fwf | NA | read.fwf | NA |
Excel | pd.read_excel | pd.to_excel | readxl::read_excel | writexl::write_xlsx |
JSON | pd.read_json | pd.to_json | jsonlite::fromJSON | jsonlite::toJSON |
基本数据结构
Series
索引在pandas中是非常重要的概念,但在R中这个概念就很弱,甚至在R的DataFrame中尽量避免使用行名(R的行名相当于pandas中dataframe的索引)。
s = pd.Series(data = [100, 'a', {'dic1':5}],
index = pd.Index(['id1', 20, 'third'], name='my_idx'),
dtype = 'object',
name = 'my_name')
s.shape
s['third'] ## 返回dict
s[['third']] ## 返回series
# s = pd.Series(np.arange(5), dtype=np.float32)
s <- list(
id1 = 100,
`20` = 'a',
third = list(
dic1 = 5
)
)
length(s)
s[['third']]
s['third']
DataFrame
对DataFrame数据的操作,pandas的[]
和[[]]
操作与R正好相反。
R中$
也可以获取字段数据,但只能获取一列,而且是模糊匹配,[[]]
也可以通过exact = FALSE
实现模糊匹配字段。R的这个特性,我只在明确知道字段名称,而自己当时又特别懒的情况下,临时使用。也许是我没发现这个功能在其他地方有什么神奇效果,但我觉得这个弊大于利。
# Python
df = pd.DataFrame(
data = {
'col_0': [1,2,3],
'col_1':list('abc'),
'col_2': [1.2, 2.2, 3.2],
'mycol': range(3)},
index = ['row_%d'%i for i in range(3)])
df['col_0'] # 返回series
df[['col_0']] # 返回dataframe
df.col_0.values # 返回numpy.ndarray
df.col_0.to_numpy() # 返回numpy.ndarray
# R
df <- data.frame(
col_0 = 1:3,
col_1 = c('a', 'b', 'c'),
col_2 = c(1.2, 2.2, 3.2),
mycol = 0:2,
stringsAsFactors = FALSE
)
df["col_0"] # 返回dataframe
df[["col_0"]] # 返回vector
df$col_0 # 返回vector
df[, "col_0"] # 返回vector
df$my
df[['my', exact = FALSE]]
# R-dplyr
pull(df, col_0)
pull(df, col_0, col_1) ## 可以直接用col_1给col_0命名
常用函数
# Python
df = pd.read_csv('data/learn_pandas.csv')
# R
df <- read.csv('data/learn_pandas.csv', stringsAsFactors = FALSE)
内容 | Python | R |
---|---|---|
行名 | df.index | rownames(df) |
列名 | df.columns | colnames(df) |
字段类型 | df.dtypes | sapply(df, class) |
字段信息 | df.info | str(df) |
主要统计量 | df.describe() | summary(df) |
字段数据 | df[‘School’] | df[[‘School’]] df$School |
维度 | df.ndim | - |
维度 | df.size | prod(dim(df)) |
维度 | df.shape | dim(df) |
行数 | df.shape[0] | nrow(df) |
列数 | df.shape[1] | ncol(df) |
列数 | df.head() | head(df) |
列数 | df.tail() | tail(df) |
特征统计函数
pandas可以直接使用统计函数,对各列进行统计,使用起来非常方便。
R与pandas有所差异,只有几个可以直接实现,但R中有summary
可以直接查看所有列几个常用的统计指标,其他也可以使用apply
系列函数实现。
# Python
df_demo = df[['Height', 'Weight']]
df_demo.sum()
df_demo.mean()
df_demo.max()
# R
df_demo <- df[, c('Height', 'Weight')]
summary(df_demo)
colSums(df_demo, na.rm = TRUE)
colMeans(df_demo, na.rm = TRUE)
sapply(df_demo, max, na.rm = TRUE)
替换函数
pandas的replace功能比R的replace功能更加全面。
# Python
df['Gender'].replace({'Female':0, 'Male':1}).head()
# R
s <- c(-1, 1.2345, 100, -50)
round(s, 2)
abs(s)
ifelse(s > 2, 2, ifelse(s < 0, 0, s))
replace(replace(s, s < 0, 0), s > 2, 2)
R中的clip是graphics包,用于绘图的。想到两种方式,ifelse和replace,以为ifelse会快,结果发现replace性能更好。
# Python
s = pd.Series([-1, 1.2345, 100, -50])
s.round(2)
s.abs()
s.clip(0, 2)
# R
s <- c(-1, 1.2345, 100, -50)
round(s, 2)
abs(s)
ifelse(s > 2, 2, ifelse(s < 0, 0, s))
replace(replace(s, s < 0, 0), s > 2, 2)
expr | min | mean | median | max |
---|---|---|---|---|
ifelse | 4.959183 | 8.730916 | 7.018489 | 113.33355 |
replace | 2.135927 | 2.613291 | 2.197710 | 12.00995 |
唯一值
pandas使用unique
和nunique
分别获获取序列的唯一值和唯一值的个数。
R中也有unique
,功能相同。R中没有直接实现nunique
的函数,需要使用length
计算。
# Python
df['School'].unique()
df['School'].nunique()
# R
unique(df$School)
length(unique(df$School))
# R-dplyr
n_distinct(df$School)
pandas使用value_counts
获取每个唯一值的数量
R中有table
,dplyr的count
。
# Python
df['School'].value_counts()
# R
table(df$School)
# R-dplyr
count(df, School)
pandas和R都有duplicated
来查找重复项,功能基本相同,pandas比R多一个模式keep = False
。
df.duplicated
的关键参数是keep:
first
表示除了第一条,把其余相同值都标示为重复【默认值】last
表示除了最后一条,把其余相同值都标示为重复False
表示把所有存在相同指的都标示为重复
R中duplicated
的关键参数是fromLast
,只能直接实现first
和last
的功能,False
的功能需要结合两者使用。
# Python
df_demo = df[['Gender','Transfer','Name']]
df_demo.duplicated()
df_demo.duplicated(keep = 'last')
df_demo.duplicated(keep = False)
# R
df_demo <- df[, c('Gender','Transfer','Name')]
duplicated(df_demo)
duplicated(df_demo, fromLast = TRUE) ## rev(duplicated(rev(df_demo)))
duplicated(df_demo) | duplicated(df_demo, fromLast = TRUE)
删除重复项dp.drop_duplicates
的方式也是有三种,同df.duplicated
# Python
df_demo.drop_duplicates()
df_demo.drop_duplicates(['Gender', 'Transfer'])
df_demo.drop_duplicates(['Gender', 'Transfer'], keep = 'last')
df_demo.drop_duplicates(['Name', 'Gender'], keep = False)
# R
df_demo[!duplicated(df_demo), ]
df_demo[!duplicated(df_demo[, c('Gender', 'Transfer')]), ]
df_demo[!duplicated(df_demo[, c('Gender', 'Transfer')], fromLast = TRUE), ]
df_demo[!(duplicated(df_demo[, c('Name', 'Gender')]) | duplicated(df_demo[, c('Name', 'Gender')], fromLast = TRUE)), ]
# R-dplyr
distinct(df_demo)
df_demo %>% distinct(Gender, Transfer, .keep_all = TRUE)
# df_demo %>% group_by(Gender, Transfer) %>% filter(row_number() == 1)
df_demo %>%
group_by(Gender, Transfer) %>%
filter(row_number(desc(row_number())) == 1)
df_demo %>%
group_by(Name, Gender) %>%
filter(n() == 1)
排序函数
# Python
df_demo = df[['Grade', 'Name', 'Height', 'Weight']].set_index(['Grade','Name'])
df_demo.sort_values(by = 'Height')
df_demo.sort_values('Height', ascending=False)
df_demo.sort_values(['Weight','Height'],ascending=[True,False])
# R
df_demo <- df[, c('Grade', 'Name', 'Height', 'Weight')]
df_demo[order(df_demo$Height), ]
df_demo[order(df_demo$Height, decreasing = TRUE), ]
df_demo[order(df_demo$Weight, -df_demo$Height), ]
# R-dplyr
arrange(df_demo, Height)
arrange(df_demo, desc(Height))
arrange(df_demo, Weight, desc(Height))
apply函数
pandas的apply
函数与R的apply
系列函数功能基本相同。
pandas的mad
与R的mad
函数两种的运算逻辑是不一样的。pandas的逻辑是Mean Absolute Deviation,但称为Average Absolute Deviation更合适,R的逻辑是Median Absolute Deviation。后续pandas也会对这个进行优化,详见ENH: calculate average absolute difference by mean, median or mode #11787。
# Python
df_demo = df[['Height', 'Weight']]
df_demo.apply(lambda x:x.mean())
df_demo.apply(lambda x:x.mean(), axis=1)
## constant * Median(abs(x - center))
df_demo.apply(lambda x:(x-x.mean()).abs().mean())
df_demo.mad()
# R
df_demo <- df[, c('Height', 'Weight')]
apply(df_demo, 2, mean, na.rm = TRUE)
apply(df_demo, 1, mean, na.rm = TRUE)
apply(df_demo, 2, function(x) mean(abs(x-mean(x, na.rm = TRUE)), na.rm = TRUE))
## R的mad函数逻辑是constant * Median(abs(x - center))
apply(df_demo, 2, mad, na.rm = TRUE)
apply(df_demo, 2, function(x) 1.4826 * median(abs(x[!is.na(x)]-median(x[!is.na(x)]))))
窗口函数
滑动窗口
R base和dplyr都不支持滑动窗口函数,可以使用RcppRoll的roll系列函数、data.table的roll系列函数、roll的roll系列函数。
pandas的确是各种数据分析工具的集成,R中虽然有很多包可以实现,但pandas体验稍微好点。
# Python
s = pd.Series([1,2,3,4,5])
roller = s.rolling(window = 3)
roller.mean()
roller.sum()
s2 = pd.Series([1,2,6,16,30])
s2.rolling(3).cov()
s2.rolling(3).corr()
s2.rolling(3).apply(lambda x:x.mean())
# R
s <- 1:5
RcppRoll::roll_mean(s, 3, fill=NA, align="center") ## 默认为align
RcppRoll::roll_mean(s, 3, fill=NA, align="right")
RcppRoll::roll_mean(s, 3, fill=NA, align="left")
roll::roll_mean(s, width = 3) ## data.table::frollmean(s, 3)
roll::roll_sum(s, width = 3) ## data.table::frollsum(s, 3)
s2 <- c(1,2,6,16,30)
roll::roll_cov(s2, width = 3)
roll::roll_cor(s2, width = 3)
data.table::frollapply(s2, 3, mean)
扩展窗口
扩展窗口也就是累加窗口。
使用pandas的滑动窗口rolling和扩展窗口expanding,支持的运算函数是统一的,统一支持运算有agg、aggregate、apply、corr、count、cov、exclusions、is_datetimelike、is_freq_type、kurt、max、mean、median、min、ndim、quantile、skew、std、sum、validate、var。
# Python
s = pd.Series([1, 3, 6, 10])
s.expanding().sum() ## s.cumsum()
s.expanding().agg({"A": "sum", "B": "mean"})
# R
s <- c(1, 3, 6, 10])
cumsum(s)
指数加权窗口
练习
Ex1:口袋妖怪数据集
1 对 HP, Attack, Defense, Sp. Atk, Sp. Def, Speed 进行加总,验证是否为 Total 值
# Python
df = pd.read_csv('data/pokemon.csv')
all((df['Total'] - df[['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']].sum(axis=1)) == 0)
# R
df <- read.csv('data/pokemon.csv', stringsAsFactors = FALSE, check.names = FALSE) ## read.csv默认会将不合规行名进行替换
all(rowSums(df[, c('HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed')]) - df$Total == 0)
2 对于 # 重复的妖怪只保留第一条记录,解决以下问题:
a 求第一属性的种类数量和前三多数量对应的种类
# Python
df['Type 1'].nunique()
df['Type 1'].value_counts().head(3)
# R
length(unique(df[['Type 1']]))
head(sort(table(df[['Type 1']]), decreasing = TRUE), 3)
b 求第一属性和第二属性的组合种类
# Python
df_attr_have = df[['Type 1', 'Type 2']].drop_duplicates(keep = 'first')
df_attr_have.shape[0]
# R
df_attr_have <- dplyr::distinct(df[, c('Type 1', 'Type 2')])
nrow(df_attr_have)
c 求尚未出现过的属性组合
# Python
attr_1 = df['Type 1'].unique()
attr_2 = df['Type 2'].unique()
len(attr_1) * len(attr_2) - df_attr_have.shape[0]
attr_all = [(x, y) for x in attr_1 for y in attr_2]
attr_none_logical = [z not in [(df_attr_have['Type 1'].values[x], df_attr_have['Type 2'].values[x]) for x in range(df_attr_have.shape[0])] for z in attr_all]
np.array(attr_all)[np.array(attr_none_logical)]
# R
attr_1 <- unique(df[['Type 1']])
attr_2 <- unique(df[['Type 2']])
length(attr_1) * length(attr_2) - nrow(df_attr_haves)
attr_all <- paste(rep(attr_1, 19), rep(attr_2, 18))
attr_all[!(attr_all %in% paste(df_attr_have[['Type 1']], df_attr_have[['Type 2']]))]
df_attr_all <- expand.grid(unique(df[['Type 1']]), unique(df[['Type 2']]), KEEP.OUT.ATTRS = FALSE, stringsAsFactors = FALSE)
names(df_attr_all) <- c('Type 1', 'Type 2')
nrow(df_attr_all) - nrow(df_attr_have)
df_attr_all2 <- df_attr_all %>%
left_join(mutate(df_attr_have, is_have = 1)) %>%
filter(is.na(is_have))
3 按照下述要求,构造 Series :
a 取出物攻,超过120的替换为 high ,不足50的替换为 low ,否则设为 mid
# Python
df['Attack'].mask(df['Attack']>120, 'high').mask(df['Attack']<50, 'low').mask((50<=df['Attack'])&(df['Attack']<=120), 'mid')
# R
ifelse(
df$Attack > 120, 'high',
ifelse(df$Attack < 50, 'low','mid')
)
# R-dplyr
case_when(
df$Attack > 120 ~ 'high',
df$Attack < 50 ~ 'low',
TRUE ~ 'mid'
)
b 取出第一属性,分别用 replace 和 apply 替换所有字母为大写
# Python
df['Type 1'].replace({i:str.upper(i) for i in df['Type 1'].unique()})
df['Type 1'].apply(lambda x:str.upper(x))
# R
toupper(df[['Type 1']])
c 求每个妖怪六项能力的离差,即所有能力中偏离中位数最大的值,添加到 df 并从大到小排序
# Python
df['Deviation'] = df[['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']].apply(lambda x:np.max((x-x.median()).abs()), 1)
df.sort_values('Deviation', ascending=False).head()
# R
df$Deviation <- apply(df[, c('HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed')], 1, function(x) max(x) - median(x))
df[order(df$Deviation, decreasing = TRUE), ]
Ex2:指数加权窗口
# Python
np.random.seed(0)
s = pd.Series(np.random.randint(-1,2,30).cumsum())
s.head()
s.ewm(alpha=0.2).mean().head()
# R
s <- py$s
se <- qcc::ewma(s, lambda = 0.2)
cummean(se$data)
# Python
def ewm_func(x, alpha=0.2):
win = (1-alpha)**np.arange(x.shape[0])[::-1]
res = (win*x).sum()/win.sum()
return res
s.expanding().apply(ewm_func).head()
# Python
s.rolling(4).apply(ewm_func).head()
参考资料
总结
根据到现在的学习,在数据分析领域,pandas和R都有自己的特色,pandas在数据分析中的优点是工具集成、方法全面、使用方式统一,python基本靠pandas做到了R很多包的功能。R在数据分析中优点是工具全面、方法多样(有时候也是缺点)、统计知识(如mad
函数)。