由于 pandas 旨在為人們提供可以替代R的大量數(shù)據(jù)操作和分析的功能,因此本章節(jié)會提供較為詳細的R語言的介紹以及與相關(guān)的許多第三方庫的對比說明,比如我們的 pandas 庫。在與R和CRAN庫的比較中,我們關(guān)注以下事項:
此頁面還為這些R包的用戶提供了一些翻譯指南。
要將 DataFrame 對象從 pandas 轉(zhuǎn)化為到 R 的數(shù)據(jù)類型,有一個選擇是采用HDF5文件,請參閱外部兼容性示例。
我們將從快速參考指南開始,將dplyr與pandas等效的一些常見R操作配對。
R | Pandas |
---|---|
dim(df) | df.shape |
head(df) | df.head() |
slice(df, 1:10) | df.iloc[:9] |
filter(df, col1 == 1, col2 == 1) | df.query('col1 == 1 & col2 == 1') |
df[df$col1 == 1 & df$col2 == 1,] | df[(df.col1 == 1) & (df.col2 == 1)] |
select(df, col1, col2) | df[['col1', 'col2']] |
select(df, col1:col3) | df.loc[:, 'col1':'col3'] |
select(df, -(col1:col3)) | df.drop(cols_to_drop, axis=1)但是看[1] |
distinct(select(df, col1)) | df[['col1']].drop_duplicates() |
distinct(select(df, col1, col2)) | df[['col1', 'col2']].drop_duplicates() |
sample_n(df, 10) | df.sample(n=10) |
sample_frac(df, 0.01) | df.sample(frac=0.01) |
Note
R表示列的子集 (select(df,col1:col3) 的縮寫更接近 Pandas 的寫法,如果您有列的列表,例如 df[cols[1:3] 或 df.drop(cols[1:3]),按列名執(zhí)行此操作可能會引起混亂。
R | Pandas |
---|---|
arrange(df, col1, col2) | df.sort_values(['col1', 'col2']) |
arrange(df, desc(col1)) | df.sort_values('col1', ascending=False) |
R | Pandas |
---|---|
select(df, col_one = col1) | df.rename(columns={'col1': 'col_one'})['col_one'] |
rename(df, col_one = col1) | df.rename(columns={'col1': 'col_one'}) |
mutate(df, c=a-b) | df.assign(c=df.a-df.b) |
R | Pandas |
---|---|
summary(df) | df.describe() |
gdf <- group_by(df, col1) | gdf = df.groupby('col1') |
summarise(gdf, avg=mean(col1, na.rm=TRUE)) | df.groupby('col1').agg({'col1': 'mean'}) |
summarise(gdf, total=sum(col1)) | df.groupby('col1').sum() |
R使您可以輕松地按名稱訪問列(data.frame)
df <- data.frame(a=rnorm(5), b=rnorm(5), c=rnorm(5), d=rnorm(5), e=rnorm(5))
df[, c("a", "c", "e")]
或整數(shù)位置
df <- data.frame(matrix(rnorm(1000), ncol=100))
df[, c(1:10, 25:30, 40, 50:100)]
按名稱選擇多個pandas的列非常簡單
In [1]: df = pd.DataFrame(np.random.randn(10, 3), columns=list('abc'))
In [2]: df[['a', 'c']]
Out[2]:
a c
0 0.469112 -1.509059
1 -1.135632 -0.173215
2 0.119209 -0.861849
3 -2.104569 1.071804
4 0.721555 -1.039575
5 0.271860 0.567020
6 0.276232 -0.673690
7 0.113648 0.524988
8 0.404705 -1.715002
9 -1.039268 -1.157892
In [3]: df.loc[:, ['a', 'c']]
Out[3]:
a c
0 0.469112 -1.509059
1 -1.135632 -0.173215
2 0.119209 -0.861849
3 -2.104569 1.071804
4 0.721555 -1.039575
5 0.271860 0.567020
6 0.276232 -0.673690
7 0.113648 0.524988
8 0.404705 -1.715002
9 -1.039268 -1.157892
通過整數(shù)位置選擇多個不連續(xù)的列可以通過iloc索引器屬性和 numpy.r_ 的組合來實現(xiàn)。
In [4]: named = list('abcdefg')
In [5]: n = 30
In [6]: columns = named + np.arange(len(named), n).tolist()
In [7]: df = pd.DataFrame(np.random.randn(n, n), columns=columns)
In [8]: df.iloc[:, np.r_[:10, 24:30]]
Out[8]:
a b c d e f g 7 8 9 24 25 26 27 28 29
0 -1.344312 0.844885 1.075770 -0.109050 1.643563 -1.469388 0.357021 -0.674600 -1.776904 -0.968914 -1.170299 -0.226169 0.410835 0.813850 0.132003 -0.827317
1 -0.076467 -1.187678 1.130127 -1.436737 -1.413681 1.607920 1.024180 0.569605 0.875906 -2.211372 0.959726 -1.110336 -0.619976 0.149748 -0.732339 0.687738
2 0.176444 0.403310 -0.154951 0.301624 -2.179861 -1.369849 -0.954208 1.462696 -1.743161 -0.826591 0.084844 0.432390 1.519970 -0.493662 0.600178 0.274230
3 0.132885 -0.023688 2.410179 1.450520 0.206053 -0.251905 -2.213588 1.063327 1.266143 0.299368 -2.484478 -0.281461 0.030711 0.109121 1.126203 -0.977349
4 1.474071 -0.064034 -1.282782 0.781836 -1.071357 0.441153 2.353925 0.583787 0.221471 -0.744471 -1.197071 -1.066969 -0.303421 -0.858447 0.306996 -0.028665
.. ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
25 1.492125 -0.068190 0.681456 1.221829 -0.434352 1.204815 -0.195612 1.251683 -1.040389 -0.796211 1.944517 0.042344 -0.307904 0.428572 0.880609 0.487645
26 0.725238 0.624607 -0.141185 -0.143948 -0.328162 2.095086 -0.608888 -0.926422 1.872601 -2.513465 -0.846188 1.190624 0.778507 1.008500 1.424017 0.717110
27 1.262419 1.950057 0.301038 -0.933858 0.814946 0.181439 -0.110015 -2.364638 -1.584814 0.307941 -1.341814 0.334281 -0.162227 1.007824 2.826008 1.458383
28 -1.585746 -0.899734 0.921494 -0.211762 -0.059182 0.058308 0.915377 -0.696321 0.150664 -3.060395 0.403620 -0.026602 -0.240481 0.577223 -1.088417 0.326687
29 -0.986248 0.169729 -1.158091 1.019673 0.646039 0.917399 -0.010435 0.366366 0.922729 0.869610 -1.209247 -0.671466 0.332872 -2.013086 -1.602549 0.333109
[30 rows x 16 columns]
在R中,您可能希望將數(shù)據(jù)分成幾個子集,并計算每個子集的平均值。使用名為df的data.frame并將其分成組by1和by2:
df <- data.frame(
v1 = c(1,3,5,7,8,3,5,NA,4,5,7,9),
v2 = c(11,33,55,77,88,33,55,NA,44,55,77,99),
by1 = c("red", "blue", 1, 2, NA, "big", 1, 2, "red", 1, NA, 12),
by2 = c("wet", "dry", 99, 95, NA, "damp", 95, 99, "red", 99, NA, NA))
aggregate(x=df[, c("v1", "v2")], by=list(mydf2$by1, mydf2$by2), FUN = mean)
該groupby()方法類似于基本R的 aggregate 函數(shù)。
In [9]: df = pd.DataFrame(
...: {'v1': [1, 3, 5, 7, 8, 3, 5, np.nan, 4, 5, 7, 9],
...: 'v2': [11, 33, 55, 77, 88, 33, 55, np.nan, 44, 55, 77, 99],
...: 'by1': ["red", "blue", 1, 2, np.nan, "big", 1, 2, "red", 1, np.nan, 12],
...: 'by2': ["wet", "dry", 99, 95, np.nan, "damp", 95, 99, "red", 99, np.nan,
...: np.nan]})
...:
In [10]: g = df.groupby(['by1', 'by2'])
In [11]: g[['v1', 'v2']].mean()
Out[11]:
v1 v2
by1 by2
1 95 5.0 55.0
99 5.0 55.0
2 95 7.0 77.0
99 NaN NaN
big damp 3.0 33.0
blue dry 3.0 33.0
red red 4.0 44.0
wet 1.0 11.0
有關(guān)更多詳細信息和示例,請參閱groupby文檔。
在R中選擇數(shù)據(jù)的常用方法是使用%in%使用該函數(shù)定義的數(shù)據(jù)match。運算符%in%用于返回指示是否存在匹配的邏輯向量:
s <- 0:4
s %in% c(2,4)
該isin()方法類似于R %in%運算符:
In [12]: s = pd.Series(np.arange(5), dtype=np.float32)
In [13]: s.isin([2, 4])
Out[13]:
0 False
1 False
2 True
3 False
4 True
dtype: bool
該match函數(shù)返回其第二個參數(shù)匹配位置的向量:
s <- 0:4
match(s, c(2,4))
有關(guān)更多詳細信息和示例,請參閱重塑文檔。
tapply類似于aggregate,但數(shù)據(jù)可以是一個參差不齊的數(shù)組,因為子類大小可能是不規(guī)則的。使用調(diào)用的data.frame baseball,并根據(jù)數(shù)組檢索信息team:
baseball <-
data.frame(team = gl(5, 5,
labels = paste("Team", LETTERS[1:5])),
player = sample(letters, 25),
batting.average = runif(25, .200, .400))
tapply(baseball$batting.average, baseball.example$team,
max)
在pandas我們可以使用pivot_table()方法來處理這個:
In [14]: import random
In [15]: import string
In [16]: baseball = pd.DataFrame(
....: {'team': ["team %d" % (x + 1) for x in range(5)] * 5,
....: 'player': random.sample(list(string.ascii_lowercase), 25),
....: 'batting avg': np.random.uniform(.200, .400, 25)})
....:
In [17]: baseball.pivot_table(values='batting avg', columns='team', aggfunc=np.max)
Out[17]:
team team 1 team 2 team 3 team 4 team 5
batting avg 0.352134 0.295327 0.397191 0.394457 0.396194
有關(guān)更多詳細信息和示例,請參閱重塑文檔。
該query()方法類似于基本R subset 函數(shù)。在R中,您可能希望獲取data.frame一列的值小于另一列的值的行:
df <- data.frame(a=rnorm(10), b=rnorm(10))
subset(df, a <= b)
df[df$a <= df$b,] # note the comma
在pandas,有幾種方法可以執(zhí)行子集化。您可以使用 query()或傳遞表達式,就像它是索引/切片以及標準布爾索引一樣:
In [18]: df = pd.DataFrame({'a': np.random.randn(10), 'b': np.random.randn(10)})
In [19]: df.query('a <= b')
Out[19]:
a b
1 0.174950 0.552887
2 -0.023167 0.148084
3 -0.495291 -0.300218
4 -0.860736 0.197378
5 -1.134146 1.720780
7 -0.290098 0.083515
8 0.238636 0.946550
In [20]: df[df.a <= df.b]
Out[20]:
a b
1 0.174950 0.552887
2 -0.023167 0.148084
3 -0.495291 -0.300218
4 -0.860736 0.197378
5 -1.134146 1.720780
7 -0.290098 0.083515
8 0.238636 0.946550
In [21]: df.loc[df.a <= df.b]
Out[21]:
a b
1 0.174950 0.552887
2 -0.023167 0.148084
3 -0.495291 -0.300218
4 -0.860736 0.197378
5 -1.134146 1.720780
7 -0.290098 0.083515
8 0.238636 0.946550
有關(guān)更多詳細信息和示例,請參閱查詢文檔。
使用df帶有列的R中調(diào)用的data.frame的表達式a, b將使用with如下方式進行求值:
df <- data.frame(a=rnorm(10), b=rnorm(10))
with(df, a + b)
df$a + df$b # same as the previous expression
在pandas等效表達式中,使用該 eval()方法將是:
In [22]: df = pd.DataFrame({'a': np.random.randn(10), 'b': np.random.randn(10)})
In [23]: df.eval('a + b')
Out[23]:
0 -0.091430
1 -2.483890
2 -0.252728
3 -0.626444
4 -0.261740
5 2.149503
6 -0.332214
7 0.799331
8 -2.377245
9 2.104677
dtype: float64
In [24]: df.a + df.b # same as the previous expression
Out[24]:
0 -0.091430
1 -2.483890
2 -0.252728
3 -0.626444
4 -0.261740
5 2.149503
6 -0.332214
7 0.799331
8 -2.377245
9 2.104677
dtype: float64
在某些情況下,eval()將比純Python中的評估快得多。有關(guān)更多詳細信息和示例,請參閱eval文檔。
plyr是用于數(shù)據(jù)分析的拆分應(yīng)用組合策略的R庫。這些函數(shù)圍繞R,a for arrays,lfor lists和dfor中的三個數(shù)據(jù)結(jié)構(gòu)data.frame。下表顯示了如何在Python中映射這些數(shù)據(jù)結(jié)構(gòu)。
R | Python |
---|---|
array | list |
lists | 字典(dist)或?qū)ο罅斜恚╨ist of objects) |
data.frame | dataframe |
在R中使用名為df的data.frame的表達式,比如您有一個希望按月匯總x的需求:
require(plyr)
df <- data.frame(
x = runif(120, 1, 168),
y = runif(120, 7, 334),
z = runif(120, 1.7, 20.7),
month = rep(c(5,6,7,8),30),
week = sample(1:4, 120, TRUE)
)
ddply(df, .(month, week), summarize,
mean = round(mean(x), 2),
sd = round(sd(x), 2))
在pandas等效表達式中,使用該 groupby()方法將是:
In [25]: df = pd.DataFrame({'x': np.random.uniform(1., 168., 120),
....: 'y': np.random.uniform(7., 334., 120),
....: 'z': np.random.uniform(1.7, 20.7, 120),
....: 'month': [5, 6, 7, 8] * 30,
....: 'week': np.random.randint(1, 4, 120)})
....:
In [26]: grouped = df.groupby(['month', 'week'])
In [27]: grouped['x'].agg([np.mean, np.std])
Out[27]:
mean std
month week
5 1 63.653367 40.601965
2 78.126605 53.342400
3 92.091886 57.630110
6 1 81.747070 54.339218
2 70.971205 54.687287
3 100.968344 54.010081
7 1 61.576332 38.844274
2 61.733510 48.209013
3 71.688795 37.595638
8 1 62.741922 34.618153
2 91.774627 49.790202
3 73.936856 60.773900
有關(guān)更多詳細信息和示例,請參閱groupby文檔。
使用a在R中調(diào)用的3維數(shù)組的表達式,您希望將其融合到data.frame中:
a <- array(c(1:23, NA), c(2,3,4))
data.frame(melt(a))
在Python中,既然a是一個列表,你可以簡單地使用列表理解。
In [28]: a = np.array(list(range(1, 24)) + [np.NAN]).reshape(2, 3, 4)
In [29]: pd.DataFrame([tuple(list(x) + [val]) for x, val in np.ndenumerate(a)])
Out[29]:
0 1 2 3
0 0 0 0 1.0
1 0 0 1 2.0
2 0 0 2 3.0
3 0 0 3 4.0
4 0 1 0 5.0
.. .. .. .. ...
19 1 1 3 20.0
20 1 2 0 21.0
21 1 2 1 22.0
22 1 2 2 23.0
23 1 2 3 NaN
[24 rows x 4 columns]
使用aR中調(diào)用的列表的表達式,您希望將其融合到data.frame中:
a <- as.list(c(1:4, NA))
data.frame(melt(a))
在Python中,此列表將是元組列表,因此 DataFrame()方法會根據(jù)需要將其轉(zhuǎn)換為數(shù)據(jù)幀。
In [30]: a = list(enumerate(list(range(1, 5)) + [np.NAN]))
In [31]: pd.DataFrame(a)
Out[31]:
0 1
0 0 1.0
1 1 2.0
2 2 3.0
3 3 4.0
4 4 NaN
有關(guān)更多詳細信息和示例,請參閱“進入數(shù)據(jù)結(jié)構(gòu)”文檔。
使用cheese在R中調(diào)用的data.frame的表達式,您要在其中重新整形data.frame:
cheese <- data.frame(
first = c('John', 'Mary'),
last = c('Doe', 'Bo'),
height = c(5.5, 6.0),
weight = c(130, 150)
)
melt(cheese, id=c("first", "last"))
在Python中,該melt()方法是R等價物:
In [32]: cheese = pd.DataFrame({'first': ['John', 'Mary'],
....: 'last': ['Doe', 'Bo'],
....: 'height': [5.5, 6.0],
....: 'weight': [130, 150]})
....:
In [33]: pd.melt(cheese, id_vars=['first', 'last'])
Out[33]:
first last variable value
0 John Doe height 5.5
1 Mary Bo height 6.0
2 John Doe weight 130.0
3 Mary Bo weight 150.0
In [34]: cheese.set_index(['first', 'last']).stack() # alternative way
Out[34]:
first last
John Doe height 5.5
weight 130.0
Mary Bo height 6.0
weight 150.0
dtype: float64
有關(guān)更多詳細信息和示例,請參閱重塑文檔。
在R中acast是一個表達式,它使用df在R中調(diào)用的data.frame 來轉(zhuǎn)換為更高維的數(shù)組:
df <- data.frame(
x = runif(12, 1, 168),
y = runif(12, 7, 334),
z = runif(12, 1.7, 20.7),
month = rep(c(5,6,7),4),
week = rep(c(1,2), 6)
)
mdf <- melt(df, id=c("month", "week"))
acast(mdf, week ~ month ~ variable, mean)
在Python中,最好的方法是使用pivot_table():
In [35]: df = pd.DataFrame({'x': np.random.uniform(1., 168., 12),
....: 'y': np.random.uniform(7., 334., 12),
....: 'z': np.random.uniform(1.7, 20.7, 12),
....: 'month': [5, 6, 7] * 4,
....: 'week': [1, 2] * 6})
....:
In [36]: mdf = pd.melt(df, id_vars=['month', 'week'])
In [37]: pd.pivot_table(mdf, values='value', index=['variable', 'week'],
....: columns=['month'], aggfunc=np.mean)
....:
Out[37]:
month 5 6 7
variable week
x 1 93.888747 98.762034 55.219673
2 94.391427 38.112932 83.942781
y 1 94.306912 279.454811 227.840449
2 87.392662 193.028166 173.899260
z 1 11.016009 10.079307 16.170549
2 8.476111 17.638509 19.003494
類似地dcast,使用dfR中調(diào)用的data.frame 來基于Animal和聚合信息FeedType:
df <- data.frame(
Animal = c('Animal1', 'Animal2', 'Animal3', 'Animal2', 'Animal1',
'Animal2', 'Animal3'),
FeedType = c('A', 'B', 'A', 'A', 'B', 'B', 'A'),
Amount = c(10, 7, 4, 2, 5, 6, 2)
)
dcast(df, Animal ~ FeedType, sum, fill=NaN)
# Alternative method using base R
with(df, tapply(Amount, list(Animal, FeedType), sum))
Python可以通過兩種不同的方式處理它。首先,類似于上面使用pivot_table():
In [38]: df = pd.DataFrame({
....: 'Animal': ['Animal1', 'Animal2', 'Animal3', 'Animal2', 'Animal1',
....: 'Animal2', 'Animal3'],
....: 'FeedType': ['A', 'B', 'A', 'A', 'B', 'B', 'A'],
....: 'Amount': [10, 7, 4, 2, 5, 6, 2],
....: })
....:
In [39]: df.pivot_table(values='Amount', index='Animal', columns='FeedType',
....: aggfunc='sum')
....:
Out[39]:
FeedType A B
Animal
Animal1 10.0 5.0
Animal2 2.0 13.0
Animal3 6.0 NaN
第二種方法是使用該groupby()方法:
In [40]: df.groupby(['Animal', 'FeedType'])['Amount'].sum()
Out[40]:
Animal FeedType
Animal1 A 10
B 5
Animal2 A 2
B 13
Animal3 A 6
Name: Amount, dtype: int64
有關(guān)更多詳細信息和示例,請參閱重新整形文檔或groupby文檔。
pandas具有分類數(shù)據(jù)的數(shù)據(jù)類型。
cut(c(1,2,3,4,5,6), 3)
factor(c(1,2,3,2,2,3))
在Pandas,這是完成與pd.cut和astype("category"):
In [41]: pd.cut(pd.Series([1, 2, 3, 4, 5, 6]), 3)
Out[41]:
0 (0.995, 2.667]
1 (0.995, 2.667]
2 (2.667, 4.333]
3 (2.667, 4.333]
4 (4.333, 6.0]
5 (4.333, 6.0]
dtype: category
Categories (3, interval[float64]): [(0.995, 2.667] < (2.667, 4.333] < (4.333, 6.0]]
In [42]: pd.Series([1, 2, 3, 2, 2, 3]).astype("category")
Out[42]:
0 1
1 2
2 3
3 2
4 2
5 3
dtype: category
Categories (3, int64): [1, 2, 3]
有關(guān)更多詳細信息和示例,請參閱分類介紹和 API文檔。還有一個關(guān)于R因子差異的文檔 。
由于許多潛在的 pandas 用戶對SQL有一定的了解 ,因此本頁面旨在提供一些使用pandas如何執(zhí)行各種SQL操作的示例。
如果您是 pandas 的新手,您可能需要先閱讀十分鐘入門Pandas 以熟悉本庫。
按照慣例,我們按如下方式導(dǎo)入 pandas 和 NumPy:
In [1]: import pandas as pd
In [2]: import numpy as np
大多數(shù)示例將使用tipspandas測試中找到的數(shù)據(jù)集。我們將數(shù)據(jù)讀入名為tips的DataFrame中,并假設(shè)我們有一個具有相同名稱和結(jié)構(gòu)的數(shù)據(jù)庫表。
In [3]: url = ('https://raw.github.com/pandas-dev'
...: '/pandas/master/pandas/tests/data/tips.csv')
...:
In [4]: tips = pd.read_csv(url)
In [5]: tips.head()
Out[5]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
在SQL中,使用您要選擇的以逗號分隔的列列表(或* 選擇所有列)來完成選擇:
SELECT total_bill, tip, smoker, time
FROM tips
LIMIT 5;
使用pandas,通過將列名列表傳遞給DataFrame來完成列選擇:
In [6]: tips[['total_bill', 'tip', 'smoker', 'time']].head(5)
Out[6]:
total_bill tip smoker time
0 16.99 1.01 No Dinner
1 10.34 1.66 No Dinner
2 21.01 3.50 No Dinner
3 23.68 3.31 No Dinner
4 24.59 3.61 No Dinner
在沒有列名列表的情況下調(diào)用DataFrame將顯示所有列(類似于SQL*)。
SQL中的過濾是通過WHERE子句完成的。
SELECT *
FROM tips
WHERE time = 'Dinner'
LIMIT 5;
DataFrame可以通過多種方式進行過濾; 最直觀的是使用 布爾索引。
In [7]: tips[tips['time'] == 'Dinner'].head(5)
Out[7]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
上面的語句只是將一個 Series 的 True / False 對象傳遞給 DataFrame,返回所有帶有True的行。
In [8]: is_dinner = tips['time'] == 'Dinner'
In [9]: is_dinner.value_counts()
Out[9]:
True 176
False 68
Name: time, dtype: int64
In [10]: tips[is_dinner].head(5)
Out[10]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
就像SQL的OR和AND一樣,可以使用|將多個條件傳遞給DataFrame (OR)和&(AND)。
-- tips of more than $5.00 at Dinner meals
SELECT *
FROM tips
WHERE time = 'Dinner' AND tip > 5.00;
# tips of more than $5.00 at Dinner meals
In [11]: tips[(tips['time'] == 'Dinner') & (tips['tip'] > 5.00)]
Out[11]:
total_bill tip sex smoker day time size
23 39.42 7.58 Male No Sat Dinner 4
44 30.40 5.60 Male No Sun Dinner 4
47 32.40 6.00 Male No Sun Dinner 4
52 34.81 5.20 Female No Sun Dinner 4
59 48.27 6.73 Male No Sat Dinner 4
116 29.93 5.07 Male No Sun Dinner 4
155 29.85 5.14 Female No Sun Dinner 5
170 50.81 10.00 Male Yes Sat Dinner 3
172 7.25 5.15 Male Yes Sun Dinner 2
181 23.33 5.65 Male Yes Sun Dinner 2
183 23.17 6.50 Male Yes Sun Dinner 4
211 25.89 5.16 Male Yes Sat Dinner 4
212 48.33 9.00 Male No Sat Dinner 4
214 28.17 6.50 Female Yes Sat Dinner 3
239 29.03 5.92 Male No Sat Dinner 3
-- tips by parties of at least 5 diners OR bill total was more than $45
SELECT *
FROM tips
WHERE size >= 5 OR total_bill > 45;
# tips by parties of at least 5 diners OR bill total was more than $45
In [12]: tips[(tips['size'] >= 5) | (tips['total_bill'] > 45)]
Out[12]:
total_bill tip sex smoker day time size
59 48.27 6.73 Male No Sat Dinner 4
125 29.80 4.20 Female No Thur Lunch 6
141 34.30 6.70 Male No Thur Lunch 6
142 41.19 5.00 Male No Thur Lunch 5
143 27.05 5.00 Female No Thur Lunch 6
155 29.85 5.14 Female No Sun Dinner 5
156 48.17 5.00 Male No Sun Dinner 6
170 50.81 10.00 Male Yes Sat Dinner 3
182 45.35 3.50 Male Yes Sun Dinner 3
185 20.69 5.00 Male No Sun Dinner 5
187 30.46 2.00 Male Yes Sun Dinner 5
212 48.33 9.00 Male No Sat Dinner 4
216 28.15 3.00 Male Yes Sat Dinner 5
使用notna()和isna() 方法完成NULL檢查。
In [13]: frame = pd.DataFrame({'col1': ['A', 'B', np.NaN, 'C', 'D'],
....: 'col2': ['F', np.NaN, 'G', 'H', 'I']})
....:
In [14]: frame
Out[14]:
col1 col2
0 A F
1 B NaN
2 NaN G
3 C H
4 D I
假設(shè)我們有一個與上面的DataFrame結(jié)構(gòu)相同的表。我們只能col2通過以下查詢看到IS NULL 的記錄:
SELECT *
FROM frame
WHERE col2 IS NULL;
In [15]: frame[frame['col2'].isna()]
Out[15]:
col1 col2
1 B NaN
獲取col1IS NOT NULL的項目可以完成notna()。
SELECT *
FROM frame
WHERE col1 IS NOT NULL;
In [16]: frame[frame['col1'].notna()]
Out[16]:
col1 col2
0 A F
1 B NaN
3 C H
4 D I
在pandas中,SQL的GROUP BY操作使用類似命名的 groupby()方法執(zhí)行。groupby()通常是指我們想要將數(shù)據(jù)集拆分成組,應(yīng)用某些功能(通常是聚合),然后將這些組合在一起的過程。
常見的SQL操作是獲取整個數(shù)據(jù)集中每個組中的記錄數(shù)。例如,有一個需要向我們提供提示中的性別的數(shù)量的查詢語句:
SELECT sex, count(*)
FROM tips
GROUP BY sex;
/*
Female 87
Male 157
*/
在 pandas 中可以這樣:
In [17]: tips.groupby('sex').size()
Out[17]:
sex
Female 87
Male 157
dtype: int64
請注意,在我們使用的pandas代碼中size(),沒有 count()。這是因為 count()將函數(shù)應(yīng)用于每個列,返回每個列中的記錄數(shù)。not null
In [18]: tips.groupby('sex').count()
Out[18]:
total_bill tip smoker day time size
sex
Female 87 87 87 87 87 87
Male 157 157 157 157 157 157
或者,我們可以將該count()方法應(yīng)用于單個列:
In [19]: tips.groupby('sex')['total_bill'].count()
Out[19]:
sex
Female 87
Male 157
Name: total_bill, dtype: int64
也可以一次應(yīng)用多個功能。例如,假設(shè)我們希望查看提示量與星期幾的不同之處 - agg()允許您將字典傳遞給分組的DataFrame,指示要應(yīng)用于特定列的函數(shù)。
SELECT day, AVG(tip), COUNT(*)
FROM tips
GROUP BY day;
/*
Fri 2.734737 19
Sat 2.993103 87
Sun 3.255132 76
Thur 2.771452 62
*/
In [20]: tips.groupby('day').agg({'tip': np.mean, 'day': np.size})
Out[20]:
tip day
day
Fri 2.734737 19
Sat 2.993103 87
Sun 3.255132 76
Thur 2.771452 62
通過將列列表傳遞給groupby()方法來完成多個列的分組 。
SELECT smoker, day, COUNT(*), AVG(tip)
FROM tips
GROUP BY smoker, day;
/*
smoker day
No Fri 4 2.812500
Sat 45 3.102889
Sun 57 3.167895
Thur 45 2.673778
Yes Fri 15 2.714000
Sat 42 2.875476
Sun 19 3.516842
Thur 17 3.030000
*/
In [21]: tips.groupby(['smoker', 'day']).agg({'tip': [np.size, np.mean]})
Out[21]:
tip
size mean
smoker day
No Fri 4.0 2.812500
Sat 45.0 3.102889
Sun 57.0 3.167895
Thur 45.0 2.673778
Yes Fri 15.0 2.714000
Sat 42.0 2.875476
Sun 19.0 3.516842
Thur 17.0 3.030000
可以使用join()或執(zhí)行JOIN merge()。默認情況下, join()將在其索引上加入DataFrame。每個方法都有參數(shù),允許您指定要執(zhí)行的連接類型(LEFT,RIGHT,INNER,F(xiàn)ULL)或要連接的列(列名稱或索引)。
In [22]: df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'],
....: 'value': np.random.randn(4)})
....:
In [23]: df2 = pd.DataFrame({'key': ['B', 'D', 'D', 'E'],
....: 'value': np.random.randn(4)})
....:
假設(shè)我們有兩個與DataFrames名稱和結(jié)構(gòu)相同的數(shù)據(jù)庫表。
現(xiàn)在讓我們來看看各種類型的JOIN。
SELECT *
FROM df1
INNER JOIN df2
ON df1.key = df2.key;
# merge performs an INNER JOIN by default
In [24]: pd.merge(df1, df2, on='key')
Out[24]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209
merge() 當您想要將一個DataFrame列與另一個DataFrame索引連接時,還會為這些情況提供參數(shù)。
In [25]: indexed_df2 = df2.set_index('key')
In [26]: pd.merge(df1, indexed_df2, left_on='key', right_index=True)
Out[26]:
key value_x value_y
1 B -0.282863 1.212112
3 D -1.135632 -0.173215
3 D -1.135632 0.119209
-- show all records from df1
SELECT *
FROM df1
LEFT OUTER JOIN df2
ON df1.key = df2.key;
# show all records from df1
In [27]: pd.merge(df1, df2, on='key', how='left')
Out[27]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
-- show all records from df2
SELECT *
FROM df1
RIGHT OUTER JOIN df2
ON df1.key = df2.key;
# show all records from df2
In [28]: pd.merge(df1, df2, on='key', how='right')
Out[28]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209
3 E NaN -1.044236
pandas還允許顯示數(shù)據(jù)集兩側(cè)的FULL JOIN,無論連接列是否找到匹配項。在編寫時,所有RDBMS(MySQL)都不支持FULL JOIN。
-- show all records from both tables
SELECT *
FROM df1
FULL OUTER JOIN df2
ON df1.key = df2.key;
# show all records from both frames
In [29]: pd.merge(df1, df2, on='key', how='outer')
Out[29]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E NaN -1.044236
UNION ALL可以使用concat()。
In [30]: df1 = pd.DataFrame({'city': ['Chicago', 'San Francisco', 'New York City'],
....: 'rank': range(1, 4)})
....:
In [31]: df2 = pd.DataFrame({'city': ['Chicago', 'Boston', 'Los Angeles'],
....: 'rank': [1, 4, 5]})
....:
SELECT city, rank
FROM df1
UNION ALL
SELECT city, rank
FROM df2;
/*
city rank
Chicago 1
San Francisco 2
New York City 3
Chicago 1
Boston 4
Los Angeles 5
*/
In [32]: pd.concat([df1, df2])
Out[32]:
city rank
0 Chicago 1
1 San Francisco 2
2 New York City 3
0 Chicago 1
1 Boston 4
2 Los Angeles 5
SQL的UNION類似于UNION ALL,但是UNION將刪除重復(fù)的行。
SELECT city, rank
FROM df1
UNION
SELECT city, rank
FROM df2;
-- notice that there is only one Chicago record this time
/*
city rank
Chicago 1
San Francisco 2
New York City 3
Boston 4
Los Angeles 5
*/
在 pandas 中,您可以concat()結(jié)合使用 drop_duplicates()。
In [33]: pd.concat([df1, df2]).drop_duplicates()
Out[33]:
city rank
0 Chicago 1
1 San Francisco 2
2 New York City 3
1 Boston 4
2 Los Angeles 5
-- MySQL
SELECT * FROM tips
ORDER BY tip DESC
LIMIT 10 OFFSET 5;
In [34]: tips.nlargest(10 + 5, columns='tip').tail(10)
Out[34]:
total_bill tip sex smoker day time size
183 23.17 6.50 Male Yes Sun Dinner 4
214 28.17 6.50 Female Yes Sat Dinner 3
47 32.40 6.00 Male No Sun Dinner 4
239 29.03 5.92 Male No Sat Dinner 3
88 24.71 5.85 Male No Thur Lunch 2
181 23.33 5.65 Male Yes Sun Dinner 2
44 30.40 5.60 Male No Sun Dinner 4
52 34.81 5.20 Female No Sun Dinner 4
85 34.83 5.17 Female No Thur Lunch 4
211 25.89 5.16 Male Yes Sat Dinner 4
-- Oracle's ROW_NUMBER() analytic function
SELECT * FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY day ORDER BY total_bill DESC) AS rn
FROM tips t
)
WHERE rn < 3
ORDER BY day, rn;
In [35]: (tips.assign(rn=tips.sort_values(['total_bill'], ascending=False)
....: .groupby(['day'])
....: .cumcount() + 1)
....: .query('rn < 3')
....: .sort_values(['day', 'rn']))
....:
Out[35]:
total_bill tip sex smoker day time size rn
95 40.17 4.73 Male Yes Fri Dinner 4 1
90 28.97 3.00 Male Yes Fri Dinner 2 2
170 50.81 10.00 Male Yes Sat Dinner 3 1
212 48.33 9.00 Male No Sat Dinner 4 2
156 48.17 5.00 Male No Sun Dinner 6 1
182 45.35 3.50 Male Yes Sun Dinner 3 2
197 43.11 5.00 Female Yes Thur Lunch 4 1
142 41.19 5.00 Male No Thur Lunch 5 2
同樣使用 rank (method ='first') 函數(shù)
In [36]: (tips.assign(rnk=tips.groupby(['day'])['total_bill']
....: .rank(method='first', ascending=False))
....: .query('rnk < 3')
....: .sort_values(['day', 'rnk']))
....:
Out[36]:
total_bill tip sex smoker day time size rnk
95 40.17 4.73 Male Yes Fri Dinner 4 1.0
90 28.97 3.00 Male Yes Fri Dinner 2 2.0
170 50.81 10.00 Male Yes Sat Dinner 3 1.0
212 48.33 9.00 Male No Sat Dinner 4 2.0
156 48.17 5.00 Male No Sun Dinner 6 1.0
182 45.35 3.50 Male Yes Sun Dinner 3 2.0
197 43.11 5.00 Female Yes Thur Lunch 4 1.0
142 41.19 5.00 Male No Thur Lunch 5 2.0
-- Oracle's RANK() analytic function
SELECT * FROM (
SELECT
t.*,
RANK() OVER(PARTITION BY sex ORDER BY tip) AS rnk
FROM tips t
WHERE tip < 2
)
WHERE rnk < 3
ORDER BY sex, rnk;
讓我們找到每個性別組(等級<3)的提示(提示<2)。請注意,使用rank(method='min')函數(shù)時 rnk_min對于相同的提示保持不變 (如Oracle的RANK()函數(shù))
In [37]: (tips[tips['tip'] < 2]
....: .assign(rnk_min=tips.groupby(['sex'])['tip']
....: .rank(method='min'))
....: .query('rnk_min < 3')
....: .sort_values(['sex', 'rnk_min']))
....:
Out[37]:
total_bill tip sex smoker day time size rnk_min
67 3.07 1.00 Female Yes Sat Dinner 1 1.0
92 5.75 1.00 Female Yes Fri Dinner 2 1.0
111 7.25 1.00 Female No Sat Dinner 1 1.0
236 12.60 1.00 Male Yes Sat Dinner 2 1.0
237 32.83 1.17 Male Yes Sat Dinner 2 2.0
UPDATE tips
SET tip = tip*2
WHERE tip < 2;
In [38]: tips.loc[tips['tip'] < 2, 'tip'] *= 2
DELETE FROM tips
WHERE tip > 9;
在pandas中,我們選擇應(yīng)保留的行,而不是刪除它們
In [39]: tips = tips.loc[tips['tip'] <= 9]
對于來自 SAS 的潛在用戶,本節(jié)旨在演示如何在 pandas 中做各種類似SAS的操作。
由于許多潛在的 pandas 用戶對SQL有一定的了解,因此本頁面旨在提供一些使用 pandas 如何執(zhí)行各種SQL操作的示例。
如果您是 pandas 的新手,您可能需要先閱讀十分鐘入門Pandas 以熟悉本庫。
按照慣例,我們按如下方式導(dǎo)入 pandas 和 NumPy:
In [1]: import pandas as pd
In [2]: import numpy as np
注意
在本教程中,DataFrame將通過調(diào)用顯示 pandas df.head(),它將顯示該行的前N行(默認為5行)DataFrame。這通常用于交互式工作(例如Jupyter筆記本或終端) - SAS中的等價物將是:
proc print data=df(obs=5);
run;
Pandas | SAS |
---|---|
DataFrame | 數(shù)據(jù)集(data set) |
column | 變量(variable) |
row | 觀察(observation) |
groupby | BY-group |
NaN | . |
A DataFramepandas類似于SAS數(shù)據(jù)集 - 具有標記列的二維數(shù)據(jù)源,可以是不同類型的。如本文檔所示,幾乎所有可以使用SAS DATA步驟應(yīng)用于數(shù)據(jù)集的操作也可以在pandas中完成。
A Series是表示a的一列的數(shù)據(jù)結(jié)構(gòu) DataFrame。SAS沒有針對單個列的單獨數(shù)據(jù)結(jié)構(gòu),但通常,使用a Series類似于在DATA步驟中引用列。
每一個DataFrame和Series有一個Index-這是對標簽 的行數(shù)據(jù)。SAS沒有完全類似的概念。除了在DATAstep(_N_)期間可以訪問的隱式整數(shù)索引之外,數(shù)據(jù)集的行基本上是未標記的。
在pandas中,如果未指定索引,則默認情況下也使用整數(shù)索引(第一行= 0,第二行= 1,依此類推)。雖然使用標記Index或 MultiIndex可以啟用復(fù)雜的分析,并且最終是 Pandas 理解的重要部分,但是對于這種比較,我們基本上會忽略它, Index并且只是將其DataFrame視為列的集合。有關(guān)如何有效使用的更多信息, 請參閱索引文檔Index。
通過將數(shù)據(jù)放在datalines語句之后并指定列名,可以從指定值構(gòu)建SAS數(shù)據(jù)集。
data df;
input x y;
datalines;
1 2
3 4
5 6
;
run;
DataFrame可以用許多不同的方式構(gòu)造一個pandas ,但是對于少量的值,通常很方便將它指定為Python字典,其中鍵是列名,值是數(shù)據(jù)。
In [3]: df = pd.DataFrame({'x': [1, 3, 5], 'y': [2, 4, 6]})
In [4]: df
Out[4]:
x y
0 1 2
1 3 4
2 5 6
與SAS一樣,pandas提供了從多種格式讀取數(shù)據(jù)的實用程序。tips在pandas測試(csv)中找到的數(shù)據(jù)集將用于以下許多示例中。
SAS提供將csv數(shù)據(jù)讀入數(shù)據(jù)集。PROC IMPORT
proc import datafile='tips.csv' dbms=csv out=tips replace;
getnames=yes;
run;
Pandas 方法是read_csv()類似的。
In [5]: url = ('https://raw.github.com/pandas-dev/'
...: 'pandas/master/pandas/tests/data/tips.csv')
...:
In [6]: tips = pd.read_csv(url)
In [7]: tips.head()
Out[7]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
比如,可以使用許多參數(shù)來指定數(shù)據(jù)應(yīng)該如何解析。例如,如果數(shù)據(jù)是由制表符分隔的,并且沒有列名,那么pandas命令將是:PROC IMPORT````read_csv
tips = pd.read_csv('tips.csv', sep='\t', header=None)
# alternatively, read_table is an alias to read_csv with tab delimiter
tips = pd.read_table('tips.csv', header=None)
除了text / csv之外,pandas還支持各種其他數(shù)據(jù)格式,如Excel,HDF5和SQL數(shù)據(jù)庫。這些都是通過pd.read_* 函數(shù)讀取的。有關(guān)更多詳細信息,請參閱IO文檔。
在SAS中proc導(dǎo)入相反就是proc導(dǎo)出
proc export data=tips outfile='tips2.csv' dbms=csv;
run;
類似地,在 Pandas ,相反read_csv是to_csv(),與其他的數(shù)據(jù)格式遵循類似的API。
tips.to_csv('tips2.csv')
在該DATA步驟中,可以在新列或現(xiàn)有列上使用任意數(shù)學(xué)表達式。
data tips;
set tips;
total_bill = total_bill - 2;
new_bill = total_bill / 2;
run;
pandas 通過指定個體提供了類似的矢量化操作Series中DataFrame??梢砸韵嗤姆绞椒峙湫铝?。
In [8]: tips['total_bill'] = tips['total_bill'] - 2
In [9]: tips['new_bill'] = tips['total_bill'] / 2.0
In [10]: tips.head()
Out[10]:
total_bill tip sex smoker day time size new_bill
0 14.99 1.01 Female No Sun Dinner 2 7.495
1 8.34 1.66 Male No Sun Dinner 3 4.170
2 19.01 3.50 Male No Sun Dinner 3 9.505
3 21.68 3.31 Male No Sun Dinner 2 10.840
4 22.59 3.61 Female No Sun Dinner 4 11.295
SAS中的過濾是通過一個或多個列上的if或where語句完成的。
data tips;
set tips;
if total_bill > 10;
run;
data tips;
set tips;
where total_bill > 10;
/* equivalent in this case - where happens before the
DATA step begins and can also be used in PROC statements */
run;
DataFrame可以通過多種方式進行過濾; 最直觀的是使用 布爾索引
In [11]: tips[tips['total_bill'] > 10].head()
Out[11]:
total_bill tip sex smoker day time size
0 14.99 1.01 Female No Sun Dinner 2
2 19.01 3.50 Male No Sun Dinner 3
3 21.68 3.31 Male No Sun Dinner 2
4 22.59 3.61 Female No Sun Dinner 4
5 23.29 4.71 Male No Sun Dinner 4
在SAS中,if / then邏輯可用于創(chuàng)建新列。
data tips;
set tips;
format bucket $4.;
if total_bill < 10 then bucket = 'low';
else bucket = 'high';
run;
Pandas 中的相同操作可以使用where來自的方法來完成numpy。
In [12]: tips['bucket'] = np.where(tips['total_bill'] < 10, 'low', 'high')
In [13]: tips.head()
Out[13]:
total_bill tip sex smoker day time size bucket
0 14.99 1.01 Female No Sun Dinner 2 high
1 8.34 1.66 Male No Sun Dinner 3 low
2 19.01 3.50 Male No Sun Dinner 3 high
3 21.68 3.31 Male No Sun Dinner 2 high
4 22.59 3.61 Female No Sun Dinner 4 high
SAS提供了各種功能來對日期/日期時間列進行操作。
data tips;
set tips;
format date1 date2 date1_plusmonth mmddyy10.;
date1 = mdy(1, 15, 2013);
date2 = mdy(2, 15, 2015);
date1_year = year(date1);
date2_month = month(date2);
* shift date to beginning of next interval;
date1_next = intnx('MONTH', date1, 1);
* count intervals between dates;
months_between = intck('MONTH', date1, date2);
run;
等效的pandas操作如下所示。除了這些功能外,pandas還支持Base SAS中不具備的其他時間序列功能(例如重新采樣和自定義偏移) - 有關(guān)詳細信息,請參閱時間序列文檔。
In [14]: tips['date1'] = pd.Timestamp('2013-01-15')
In [15]: tips['date2'] = pd.Timestamp('2015-02-15')
In [16]: tips['date1_year'] = tips['date1'].dt.year
In [17]: tips['date2_month'] = tips['date2'].dt.month
In [18]: tips['date1_next'] = tips['date1'] + pd.offsets.MonthBegin()
In [19]: tips['months_between'] = (
....: tips['date2'].dt.to_period('M') - tips['date1'].dt.to_period('M'))
....:
In [20]: tips[['date1', 'date2', 'date1_year', 'date2_month',
....: 'date1_next', 'months_between']].head()
....:
Out[20]:
date1 date2 date1_year date2_month date1_next months_between
0 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
1 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
2 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
3 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
4 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
SAS在DATA步驟中提供關(guān)鍵字以選擇,刪除和重命名列。
data tips;
set tips;
keep sex total_bill tip;
run;
data tips;
set tips;
drop sex;
run;
data tips;
set tips;
rename total_bill=total_bill_2;
run;
下面的 Pandas 表示相同的操作。
# keep
In [21]: tips[['sex', 'total_bill', 'tip']].head()
Out[21]:
sex total_bill tip
0 Female 14.99 1.01
1 Male 8.34 1.66
2 Male 19.01 3.50
3 Male 21.68 3.31
4 Female 22.59 3.61
# drop
In [22]: tips.drop('sex', axis=1).head()
Out[22]:
total_bill tip smoker day time size
0 14.99 1.01 No Sun Dinner 2
1 8.34 1.66 No Sun Dinner 3
2 19.01 3.50 No Sun Dinner 3
3 21.68 3.31 No Sun Dinner 2
4 22.59 3.61 No Sun Dinner 4
# rename
In [23]: tips.rename(columns={'total_bill': 'total_bill_2'}).head()
Out[23]:
total_bill_2 tip sex smoker day time size
0 14.99 1.01 Female No Sun Dinner 2
1 8.34 1.66 Male No Sun Dinner 3
2 19.01 3.50 Male No Sun Dinner 3
3 21.68 3.31 Male No Sun Dinner 2
4 22.59 3.61 Female No Sun Dinner 4
SAS中的排序是通過 PROC SORT
proc sort data=tips;
by sex total_bill;
run;
pandas對象有一個sort_values()方法,它采用列表進行排序。
In [24]: tips = tips.sort_values(['sex', 'total_bill'])
In [25]: tips.head()
Out[25]:
total_bill tip sex smoker day time size
67 1.07 1.00 Female Yes Sat Dinner 1
92 3.75 1.00 Female Yes Fri Dinner 2
111 5.25 1.00 Female No Sat Dinner 1
145 6.35 1.50 Female No Thur Lunch 2
135 6.51 1.25 Female No Thur Lunch 2
SAS使用LENGTHN 和LENGTHC 函數(shù)確定字符串的長度 。LENGTHN排除尾隨空白并LENGTHC包括尾隨空白。
data _null_;
set tips;
put(LENGTHN(time));
put(LENGTHC(time));
run;
Python使用該len函數(shù)確定字符串的長度。 len包括尾隨空白。使用len和rstrip排除尾隨空格。
In [26]: tips['time'].str.len().head()
Out[26]:
67 6
92 6
111 6
145 5
135 5
Name: time, dtype: int64
In [27]: tips['time'].str.rstrip().str.len().head()
Out[27]:
67 6
92 6
111 6
145 5
135 5
Name: time, dtype: int64
SAS使用FINDW函數(shù)確定字符串中字符的位置 。 FINDW獲取第一個參數(shù)定義的字符串,并搜索您提供的子字符串的第一個位置作為第二個參數(shù)。
data _null_;
set tips;
put(FINDW(sex,'ale'));
run;
Python使用find函數(shù)確定字符串中字符的位置 。 find搜索子字符串的第一個位置。如果找到子字符串,則該函數(shù)返回其位置。請記住,Python索引是從零開始的,如果找不到子串,函數(shù)將返回-1。
In [28]: tips['sex'].str.find("ale").head()
Out[28]:
67 3
92 3
111 3
145 3
135 3
Name: sex, dtype: int64
SAS使用SUBSTR函數(shù)根據(jù)其位置從字符串中提取子字符串 。
data _null_;
set tips;
put(substr(sex,1,1));
run;
使用pandas,您可以使用[]符號從位置位置提取字符串中的子字符串。請記住,Python索引是從零開始的。
In [29]: tips['sex'].str[0:1].head()
Out[29]:
67 F
92 F
111 F
145 F
135 F
Name: sex, dtype: object
SAS SCAN 函數(shù)返回字符串中的第n個字。第一個參數(shù)是要解析的字符串,第二個參數(shù)指定要提取的字。
data firstlast;
input String $60.;
First_Name = scan(string, 1);
Last_Name = scan(string, -1);
datalines2;
John Smith;
Jane Cook;
;;;
run;
Python使用正則表達式根據(jù)文本從字符串中提取子字符串。有更強大的方法,但這只是一個簡單的方法。
In [30]: firstlast = pd.DataFrame({'String': ['John Smith', 'Jane Cook']})
In [31]: firstlast['First_Name'] = firstlast['String'].str.split(" ", expand=True)[0]
In [32]: firstlast['Last_Name'] = firstlast['String'].str.rsplit(" ", expand=True)[0]
In [33]: firstlast
Out[33]:
String First_Name Last_Name
0 John Smith John John
1 Jane Cook Jane Jane
SAS UPCASE LOWCASE和 PROPCASE 函數(shù)改變了參數(shù)的大小寫。
data firstlast;
input String $60.;
string_up = UPCASE(string);
string_low = LOWCASE(string);
string_prop = PROPCASE(string);
datalines2;
John Smith;
Jane Cook;
;;;
run;
等效Python的功能upper,lower和title。
In [34]: firstlast = pd.DataFrame({'String': ['John Smith', 'Jane Cook']})
In [35]: firstlast['string_up'] = firstlast['String'].str.upper()
In [36]: firstlast['string_low'] = firstlast['String'].str.lower()
In [37]: firstlast['string_prop'] = firstlast['String'].str.title()
In [38]: firstlast
Out[38]:
String string_up string_low string_prop
0 John Smith JOHN SMITH john smith John Smith
1 Jane Cook JANE COOK jane cook Jane Cook
合并示例中將使用以下表格
In [39]: df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'],
....: 'value': np.random.randn(4)})
....:
In [40]: df1
Out[40]:
key value
0 A 0.469112
1 B -0.282863
2 C -1.509059
3 D -1.135632
In [41]: df2 = pd.DataFrame({'key': ['B', 'D', 'D', 'E'],
....: 'value': np.random.randn(4)})
....:
In [42]: df2
Out[42]:
key value
0 B 1.212112
1 D -0.173215
2 D 0.119209
3 E -1.044236
在SAS中,必須在合并之前顯式排序數(shù)據(jù)。使用in=虛擬變量來跟蹤是否在一個或兩個輸入幀中找到匹配來完成不同類型的連接。
proc sort data=df1;
by key;
run;
proc sort data=df2;
by key;
run;
data left_join inner_join right_join outer_join;
merge df1(in=a) df2(in=b);
if a and b then output inner_join;
if a then output left_join;
if b then output right_join;
if a or b then output outer_join;
run;
pandas DataFrames有一個merge()提供類似功能的方法。請注意,數(shù)據(jù)不必提前排序,并且通過how關(guān)鍵字可以實現(xiàn)不同的連接類型。
In [43]: inner_join = df1.merge(df2, on=['key'], how='inner')
In [44]: inner_join
Out[44]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209
In [45]: left_join = df1.merge(df2, on=['key'], how='left')
In [46]: left_join
Out[46]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
In [47]: right_join = df1.merge(df2, on=['key'], how='right')
In [48]: right_join
Out[48]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209
3 E NaN -1.044236
In [49]: outer_join = df1.merge(df2, on=['key'], how='outer')
In [50]: outer_join
Out[50]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E NaN -1.044236
與SAS一樣,pandas具有丟失數(shù)據(jù)的表示 - 這是特殊浮點值NaN(不是數(shù)字)。許多語義都是相同的,例如,丟失的數(shù)據(jù)通過數(shù)字操作傳播,默認情況下會被聚合忽略。
In [51]: outer_join
Out[51]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E NaN -1.044236
In [52]: outer_join['value_x'] + outer_join['value_y']
Out[52]:
0 NaN
1 0.929249
2 NaN
3 -1.308847
4 -1.016424
5 NaN
dtype: float64
In [53]: outer_join['value_x'].sum()
Out[53]: -3.5940742896293765
一個區(qū)別是丟失的數(shù)據(jù)無法與其哨兵值進行比較。例如,在SAS中,您可以執(zhí)行此操作以過濾缺失值。
data outer_join_nulls;
set outer_join;
if value_x = .;
run;
data outer_join_no_nulls;
set outer_join;
if value_x ^= .;
run;
這在 Pandas 中不起作用。相反,應(yīng)使用pd.isna或pd.notna函數(shù)進行比較。
In [54]: outer_join[pd.isna(outer_join['value_x'])]
Out[54]:
key value_x value_y
5 E NaN -1.044236
In [55]: outer_join[pd.notna(outer_join['value_x'])]
Out[55]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
pandas還提供了各種方法來處理丟失的數(shù)據(jù) - 其中一些方法在SAS中表達起來很有挑戰(zhàn)性。例如,有一些方法可以刪除具有任何缺失值的所有行,使用指定值替換缺失值,例如平均值或前一行的前向填充??吹?nbsp;丟失的數(shù)據(jù)文件為多。
In [56]: outer_join.dropna()
Out[56]:
key value_x value_y
1 B -0.282863 1.212112
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
In [57]: outer_join.fillna(method='ffill')
Out[57]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 1.212112
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E -1.135632 -1.044236
In [58]: outer_join['value_x'].fillna(outer_join['value_x'].mean())
Out[58]:
0 0.469112
1 -0.282863
2 -1.509059
3 -1.135632
4 -1.135632
5 -0.718815
Name: value_x, dtype: float64
SAS的PROC SUMMARY可用于按一個或多個關(guān)鍵變量進行分組,并計算數(shù)字列上的聚合。
proc summary data=tips nway;
class sex smoker;
var total_bill tip;
output out=tips_summed sum=;
run;
pandas提供了一種groupby允許類似聚合的靈活機制。有關(guān) 更多詳細信息和示例,請參閱groupby文檔。
In [59]: tips_summed = tips.groupby(['sex', 'smoker'])['total_bill', 'tip'].sum()
In [60]: tips_summed.head()
Out[60]:
total_bill tip
sex smoker
Female No 869.68 149.77
Yes 527.27 96.74
Male No 1725.75 302.00
Yes 1217.07 183.07
在SAS中,如果組聚合需要與原始幀一起使用,則必須將它們合并在一起。例如,減去吸煙者組每次觀察的平均值。
proc summary data=tips missing nway;
class smoker;
var total_bill;
output out=smoker_means mean(total_bill)=group_bill;
run;
proc sort data=tips;
by smoker;
run;
data tips;
merge tips(in=a) smoker_means(in=b);
by smoker;
adj_total_bill = total_bill - group_bill;
if a and b;
run;
pandas groupby提供了一種transform機制,允許在一個操作中簡潔地表達這些類型的操作。
In [61]: gb = tips.groupby('smoker')['total_bill']
In [62]: tips['adj_total_bill'] = tips['total_bill'] - gb.transform('mean')
In [63]: tips.head()
Out[63]:
total_bill tip sex smoker day time size adj_total_bill
67 1.07 1.00 Female Yes Sat Dinner 1 -17.686344
92 3.75 1.00 Female Yes Fri Dinner 2 -15.006344
111 5.25 1.00 Female No Sat Dinner 1 -11.938278
145 6.35 1.50 Female No Thur Lunch 2 -10.838278
135 6.51 1.25 Female No Thur Lunch 2 -10.678278
除了聚合之外,groupby還可以使用pandas 通過SAS的組處理來復(fù)制大多數(shù)其他pandas 。例如,此DATA步驟按性別/吸煙者組讀取數(shù)據(jù),并過濾到每個的第一個條目。
proc sort data=tips;
by sex smoker;
run;
data tips_first;
set tips;
by sex smoker;
if FIRST.sex or FIRST.smoker then output;
run;
在 Pandas 中,這將寫成:
In [64]: tips.groupby(['sex', 'smoker']).first()
Out[64]:
total_bill tip day time size adj_total_bill
sex smoker
Female No 5.25 1.00 Sat Dinner 1 -11.938278
Yes 1.07 1.00 Sat Dinner 1 -17.686344
Male No 5.51 2.00 Thur Lunch 2 -11.678278
Yes 5.25 5.15 Sun Dinner 2 -13.506344
pandas僅在內(nèi)存中運行,其中SAS數(shù)據(jù)集存在于磁盤上。這意味著可以在pandas中加載的數(shù)據(jù)大小受機器內(nèi)存的限制,但對數(shù)據(jù)的操作可能更快。
如果需要進行核心處理,一種可能性是 dask.dataframe 庫(目前正在開發(fā)中),它為磁盤上的pandas功能提供了一個子集DataFrame
pandas提供了一種read_sas()方法,可以讀取以XPORT或SAS7BDAT二進制格式保存的SAS數(shù)據(jù)。
libname xportout xport 'transport-file.xpt';
data xportout.tips;
set tips(rename=(total_bill=tbill));
* xport variable names limited to 6 characters;
run;
df = pd.read_sas('transport-file.xpt')
df = pd.read_sas('binary-file.sas7bdat')
您也可以直接指定文件格式。默認情況下,pandas將嘗試根據(jù)其擴展名推斷文件格式。
df = pd.read_sas('transport-file.xpt', format='xport')
df = pd.read_sas('binary-file.sas7bdat', format='sas7bdat')
XPORT是一種相對有限的格式,它的解析并不像其他一些pandas讀者那樣優(yōu)化。在SAS和pandas之間交換數(shù)據(jù)的另一種方法是序列化為csv。
# version 0.17, 10M rows
In [8]: %time df = pd.read_sas('big.xpt')
Wall time: 14.6 s
In [9]: %time df = pd.read_csv('big.csv')
Wall time: 4.86 s
對于來自 Stata 的潛在用戶,本節(jié)旨在演示如何在 pandas 中做各種類似Stata的操作。
如果您是 pandas 的新手,您可能需要先閱讀十分鐘入門Pandas 以熟悉本庫。
按照慣例,我們按如下方式導(dǎo)入 pandas 和 NumPy:
In [1]: import pandas as pd
In [2]: import numpy as np
注意
在本教程中,DataFrame將通過調(diào)用顯示 pandas df.head(),它將顯示該行的前N行(默認為5行)DataFrame。這通常用于交互式工作(例如Jupyter筆記本或終端) - Stata中的等價物將是:
list in 1/5
Pandas | Stata |
---|---|
DataFrame | 數(shù)據(jù)集(data set) |
column | 變量(variable) |
row | 觀察(observation) |
groupby | bysort |
NaN | . |
pandas 中的 DataFrame 類似于 Stata 數(shù)據(jù)集-具有不同類型的標記列的二維數(shù)據(jù)源。如本文檔所示,幾乎任何可以應(yīng)用于Stata中的數(shù)據(jù)集的操作也可以在 pandas 中完成。
Series 是表示DataFrame的一列的數(shù)據(jù)結(jié)構(gòu)。Stata 對于單個列沒有單獨的數(shù)據(jù)結(jié)構(gòu),但是通常,使用 Series 類似于引用Stata中的數(shù)據(jù)集的列。
每個 DataFrame 和 Series 在數(shù)據(jù) 行 上都有一個叫 Index-label 的標簽。在 Stata 中沒有相似的概念。在Stata中,數(shù)據(jù)集的行基本上是無標簽的,除了可以用 _n 訪問的隱式整數(shù)索引。
在pandas中,如果未指定索引,則默認情況下也使用整數(shù)索引(第一行= 0,第二行= 1,依此類推)。雖然使用標記Index或 MultiIndex可以啟用復(fù)雜的分析,并且最終是 pandas 理解的重要部分,但是對于這種比較,我們基本上會忽略它, Index并且只是將其DataFrame視為列的集合。有關(guān)如何有效使用的更多信息, 請參閱索引文檔Index。
通過將數(shù)據(jù)放在input語句之后并指定列名,可以從指定值構(gòu)建Stata數(shù)據(jù)集。
input x y
1 2
3 4
5 6
end
pandas 的 DataFrame 可以用許多不同的方式構(gòu)建,但對于少量的值,通常可以方便地將其指定為Python字典,其中鍵是列名,值是數(shù)據(jù)。
In [3]: df = pd.DataFrame({'x': [1, 3, 5], 'y': [2, 4, 6]})
In [4]: df
Out[4]:
x y
0 1 2
1 3 4
2 5 6
與Stata一樣,pandas提供了從多種格式讀取數(shù)據(jù)的實用程序。tips在pandas測試(csv)中找到的數(shù)據(jù)集將用于以下許多示例中。
Stata提供將csv數(shù)據(jù)讀入內(nèi)存中的數(shù)據(jù)集。如果文件在當前工作目錄中,我們可以按如下方式導(dǎo)入它。import delimited````tips.csv
import delimited tips.csv
pandas 方法是read_csv()類似的。此外,如果提供了網(wǎng)址,它將自動下載數(shù)據(jù)集。
In [5]: url = ('https://raw.github.com/pandas-dev'
...: '/pandas/master/pandas/tests/data/tips.csv')
...:
In [6]: tips = pd.read_csv(url)
In [7]: tips.head()
Out[7]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
比如,可以使用許多參數(shù)來指定數(shù)據(jù)應(yīng)該如何解析。例如,如果數(shù)據(jù)是由制表符分隔的,沒有列名,并且存在于當前工作目錄中,則pandas命令將為:import delimitedread_csv()
tips = pd.read_csv('tips.csv', sep='\t', header=None)
# alternatively, read_table is an alias to read_csv with tab delimiter
tips = pd.read_table('tips.csv', header=None)
pandas 還可以用于 .dta 的文件格式中。使用read_stata()函數(shù)讀取格式的Stata數(shù)據(jù)集。
df = pd.read_stata('data.dta')
除了text / csv和Stata文件之外,pandas還支持各種其他數(shù)據(jù)格式,如Excel,SAS,HDF5,Parquet和SQL數(shù)據(jù)庫。這些都是通過pd.read_* 函數(shù)讀取的。有關(guān)更多詳細信息,請參閱IO文檔。
stata 中 import delimated 的反向操作是 export delimated。
export delimited tips2.csv
類似地,在 pandas 中,read_csv 的反向操作是DataFrame.to_csv()。
tips.to_csv('tips2.csv')
pandas 還可以使用DataFrame.to_stata()方法導(dǎo)出為Stata文件格式。
tips.to_stata('tips2.dta')
在Stata中,任意數(shù)學(xué)表達式可以與新列或現(xiàn)有列上的generate和 replace命令一起使用。該drop命令從數(shù)據(jù)集中刪除列。
replace total_bill = total_bill - 2
generate new_bill = total_bill / 2
drop new_bill
pandas 通過指定個體提供了類似的矢量化操作Series中DataFrame??梢砸韵嗤姆绞椒峙湫铝?。該DataFrame.drop()方法從中刪除一列DataFrame。
In [8]: tips['total_bill'] = tips['total_bill'] - 2
In [9]: tips['new_bill'] = tips['total_bill'] / 2
In [10]: tips.head()
Out[10]:
total_bill tip sex smoker day time size new_bill
0 14.99 1.01 Female No Sun Dinner 2 7.495
1 8.34 1.66 Male No Sun Dinner 3 4.170
2 19.01 3.50 Male No Sun Dinner 3 9.505
3 21.68 3.31 Male No Sun Dinner 2 10.840
4 22.59 3.61 Female No Sun Dinner 4 11.295
In [11]: tips = tips.drop('new_bill', axis=1)
在Stata中過濾是通過 if 一個或多個列上的子句完成的。
list if total_bill > 10
DataFrame可以通過多種方式進行過濾; 最直觀的是使用 布爾索引。
In [12]: tips[tips['total_bill'] > 10].head()
Out[12]:
total_bill tip sex smoker day time size
0 14.99 1.01 Female No Sun Dinner 2
2 19.01 3.50 Male No Sun Dinner 3
3 21.68 3.31 Male No Sun Dinner 2
4 22.59 3.61 Female No Sun Dinner 4
5 23.29 4.71 Male No Sun Dinner 4
在Stata中,if子句也可用于創(chuàng)建新列。
generate bucket = "low" if total_bill < 10
replace bucket = "high" if total_bill >= 10
使用 numpy 的 where 方法可以在 pandas 中完成相同的操作。
In [13]: tips['bucket'] = np.where(tips['total_bill'] < 10, 'low', 'high')
In [14]: tips.head()
Out[14]:
total_bill tip sex smoker day time size bucket
0 14.99 1.01 Female No Sun Dinner 2 high
1 8.34 1.66 Male No Sun Dinner 3 low
2 19.01 3.50 Male No Sun Dinner 3 high
3 21.68 3.31 Male No Sun Dinner 2 high
4 22.59 3.61 Female No Sun Dinner 4 high
Stata提供了各種函數(shù)來對date / datetime列進行操作。
generate date1 = mdy(1, 15, 2013)
generate date2 = date("Feb152015", "MDY")
generate date1_year = year(date1)
generate date2_month = month(date2)
* shift date to beginning of next month
generate date1_next = mdy(month(date1) + 1, 1, year(date1)) if month(date1) != 12
replace date1_next = mdy(1, 1, year(date1) + 1) if month(date1) == 12
generate months_between = mofd(date2) - mofd(date1)
list date1 date2 date1_year date2_month date1_next months_between
等效的 pandas 操作如下所示。除了這些功能外,pandas 還支持 Stata 中不具備的其他時間序列功能(例如時區(qū)處理和自定義偏移) - 有關(guān)詳細信息,請參閱時間序列文檔。
In [15]: tips['date1'] = pd.Timestamp('2013-01-15')
In [16]: tips['date2'] = pd.Timestamp('2015-02-15')
In [17]: tips['date1_year'] = tips['date1'].dt.year
In [18]: tips['date2_month'] = tips['date2'].dt.month
In [19]: tips['date1_next'] = tips['date1'] + pd.offsets.MonthBegin()
In [20]: tips['months_between'] = (tips['date2'].dt.to_period('M')
....: - tips['date1'].dt.to_period('M'))
....:
In [21]: tips[['date1', 'date2', 'date1_year', 'date2_month', 'date1_next',
....: 'months_between']].head()
....:
Out[21]:
date1 date2 date1_year date2_month date1_next months_between
0 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
1 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
2 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
3 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
4 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
Stata 提供了選擇,刪除和重命名列的關(guān)鍵字。
keep sex total_bill tip
drop sex
rename total_bill total_bill_2
下面的 pandas 表示相同的操作。請注意,與 Stata 相比,這些操作不會發(fā)生。要使這些更改保持不變,請將操作分配回變量。
# keep
In [22]: tips[['sex', 'total_bill', 'tip']].head()
Out[22]:
sex total_bill tip
0 Female 14.99 1.01
1 Male 8.34 1.66
2 Male 19.01 3.50
3 Male 21.68 3.31
4 Female 22.59 3.61
# drop
In [23]: tips.drop('sex', axis=1).head()
Out[23]:
total_bill tip smoker day time size
0 14.99 1.01 No Sun Dinner 2
1 8.34 1.66 No Sun Dinner 3
2 19.01 3.50 No Sun Dinner 3
3 21.68 3.31 No Sun Dinner 2
4 22.59 3.61 No Sun Dinner 4
# rename
In [24]: tips.rename(columns={'total_bill': 'total_bill_2'}).head()
Out[24]:
total_bill_2 tip sex smoker day time size
0 14.99 1.01 Female No Sun Dinner 2
1 8.34 1.66 Male No Sun Dinner 3
2 19.01 3.50 Male No Sun Dinner 3
3 21.68 3.31 Male No Sun Dinner 2
4 22.59 3.61 Female No Sun Dinner 4
Stata中的排序是通過 sort
sort sex total_bill
pandas 對象有一個DataFrame.sort_values()方法,它采用列表進行排序。
In [25]: tips = tips.sort_values(['sex', 'total_bill'])
In [26]: tips.head()
Out[26]:
total_bill tip sex smoker day time size
67 1.07 1.00 Female Yes Sat Dinner 1
92 3.75 1.00 Female Yes Fri Dinner 2
111 5.25 1.00 Female No Sat Dinner 1
145 6.35 1.50 Female No Thur Lunch 2
135 6.51 1.25 Female No Thur Lunch 2
Stata 分別使用ASCII和Unicode字符串 strlen() 和 ustrlen() 函數(shù)確定字符串的長度。
generate strlen_time = strlen(time)
generate ustrlen_time = ustrlen(time)
Python 使用該 len 函數(shù)確定字符串的長度。在Python 3中,所有字符串都是Unicode字符串。len包括尾隨空白。使用len和rstrip排除尾隨空格。
In [27]: tips['time'].str.len().head()
Out[27]:
67 6
92 6
111 6
145 5
135 5
Name: time, dtype: int64
In [28]: tips['time'].str.rstrip().str.len().head()
Out[28]:
67 6
92 6
111 6
145 5
135 5
Name: time, dtype: int64
Stata使用該strpos()函數(shù)確定字符串中字符的位置。這將獲取第一個參數(shù)定義的字符串,并搜索您提供的子字符串的第一個位置作為第二個參數(shù)。
generate str_position = strpos(sex, "ale")
Python使用find()函數(shù)確定字符串中字符的位置。find搜索子字符串的第一個位置。如果找到子字符串,則該函數(shù)返回其位置。請記住,Python索引是從零開始的,如果找不到子串,函數(shù)將返回-1。
In [29]: tips['sex'].str.find("ale").head()
Out[29]:
67 3
92 3
111 3
145 3
135 3
Name: sex, dtype: int64
Stata根據(jù)substr()函數(shù)的位置從字符串中提取字符串。
generate short_sex = substr(sex, 1, 1)
使用pandas,您可以使用[]符號從位置位置提取字符串中的子字符串。請記住,Python索引是從零開始的。
In [30]: tips['sex'].str[0:1].head()
Out[30]:
67 F
92 F
111 F
145 F
135 F
Name: sex, dtype: object
Stata word()函數(shù)返回字符串中的第n個單詞。第一個參數(shù)是要解析的字符串,第二個參數(shù)指定要提取的字。
clear
input str20 string
"John Smith"
"Jane Cook"
end
generate first_name = word(name, 1)
generate last_name = word(name, -1)
Python使用正則表達式根據(jù)文本從字符串中提取字符串。有更強大的方法,但這只是一個簡單的方法。
In [31]: firstlast = pd.DataFrame({'string': ['John Smith', 'Jane Cook']})
In [32]: firstlast['First_Name'] = firstlast['string'].str.split(" ", expand=True)[0]
In [33]: firstlast['Last_Name'] = firstlast['string'].str.rsplit(" ", expand=True)[0]
In [34]: firstlast
Out[34]:
string First_Name Last_Name
0 John Smith John John
1 Jane Cook Jane Jane
所述的Stata strupper(),strlower(),strproper(), ustrupper(),ustrlower(),和ustrtitle()功能分別改變ASCII和Unicode字符串的情況下,。
clear
input str20 string
"John Smith"
"Jane Cook"
end
generate upper = strupper(string)
generate lower = strlower(string)
generate title = strproper(string)
list
等效Python的功能upper,lower和title。
In [35]: firstlast = pd.DataFrame({'string': ['John Smith', 'Jane Cook']})
In [36]: firstlast['upper'] = firstlast['string'].str.upper()
In [37]: firstlast['lower'] = firstlast['string'].str.lower()
In [38]: firstlast['title'] = firstlast['string'].str.title()
In [39]: firstlast
Out[39]:
string upper lower title
0 John Smith JOHN SMITH john smith John Smith
1 Jane Cook JANE COOK jane cook Jane Cook
合并示例中將使用以下表格
In [40]: df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'],
....: 'value': np.random.randn(4)})
....:
In [41]: df1
Out[41]:
key value
0 A 0.469112
1 B -0.282863
2 C -1.509059
3 D -1.135632
In [42]: df2 = pd.DataFrame({'key': ['B', 'D', 'D', 'E'],
....: 'value': np.random.randn(4)})
....:
In [43]: df2
Out[43]:
key value
0 B 1.212112
1 D -0.173215
2 D 0.119209
3 E -1.044236
在Stata中,要執(zhí)行合并,一個數(shù)據(jù)集必須在內(nèi)存中,另一個必須作為磁盤上的文件名引用。相比之下,Python必須DataFrames已經(jīng)在內(nèi)存中。
默認情況下,Stata執(zhí)行外部聯(lián)接,其中兩個數(shù)據(jù)集的所有觀察值在合并后都保留在內(nèi)存中。通過使用在_merge變量中創(chuàng)建的值,可以僅保留來自初始數(shù)據(jù)集,合并數(shù)據(jù)集或兩者的交集的觀察 。
* First create df2 and save to disk
clear
input str1 key
B
D
D
E
end
generate value = rnormal()
save df2.dta
* Now create df1 in memory
clear
input str1 key
A
B
C
D
end
generate value = rnormal()
preserve
* Left join
merge 1:n key using df2.dta
keep if _merge == 1
* Right join
restore, preserve
merge 1:n key using df2.dta
keep if _merge == 2
* Inner join
restore, preserve
merge 1:n key using df2.dta
keep if _merge == 3
* Outer join
restore
merge 1:n key using df2.dta
pandas 的 DataFrames 有一個DataFrame.merge()提供類似功能的方法。請注意,通過how關(guān)鍵字可以實現(xiàn)不同的連接類型。
In [44]: inner_join = df1.merge(df2, on=['key'], how='inner')
In [45]: inner_join
Out[45]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209
In [46]: left_join = df1.merge(df2, on=['key'], how='left')
In [47]: left_join
Out[47]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
In [48]: right_join = df1.merge(df2, on=['key'], how='right')
In [49]: right_join
Out[49]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209
3 E NaN -1.044236
In [50]: outer_join = df1.merge(df2, on=['key'], how='outer')
In [51]: outer_join
Out[51]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E NaN -1.044236
像Stata一樣,pandas 有缺失數(shù)據(jù)的表示 - 特殊浮點值NaN(不是數(shù)字)。許多語義都是一樣的; 例如,丟失的數(shù)據(jù)通過數(shù)字操作傳播,默認情況下會被聚合忽略。
In [52]: outer_join
Out[52]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E NaN -1.044236
In [53]: outer_join['value_x'] + outer_join['value_y']
Out[53]:
0 NaN
1 0.929249
2 NaN
3 -1.308847
4 -1.016424
5 NaN
dtype: float64
In [54]: outer_join['value_x'].sum()
Out[54]: -3.5940742896293765
一個區(qū)別是丟失的數(shù)據(jù)無法與其哨兵值進行比較。例如,在 Stata 中,您可以執(zhí)行此操作以過濾缺失值。
* Keep missing values
list if value_x == .
* Keep non-missing values
list if value_x != .
這在 pandas 中不起作用。相反,應(yīng)使用pd.isna()或pd.notna()函數(shù)進行比較。
In [55]: outer_join[pd.isna(outer_join['value_x'])]
Out[55]:
key value_x value_y
5 E NaN -1.044236
In [56]: outer_join[pd.notna(outer_join['value_x'])]
Out[56]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
pandas 還提供了多種處理丟失數(shù)據(jù)的方法,其中一些方法在Stata中表達起來很有挑戰(zhàn)性。例如,有一些方法可以刪除具有任何缺失值的所有行,用指定值(如平均值)替換缺失值,或從前一行向前填充。有關(guān)詳細信息,請參閱缺失數(shù)據(jù)文檔。
# Drop rows with any missing value
In [57]: outer_join.dropna()
Out[57]:
key value_x value_y
1 B -0.282863 1.212112
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
# Fill forwards
In [58]: outer_join.fillna(method='ffill')
Out[58]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 1.212112
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E -1.135632 -1.044236
# Impute missing values with the mean
In [59]: outer_join['value_x'].fillna(outer_join['value_x'].mean())
Out[59]:
0 0.469112
1 -0.282863
2 -1.509059
3 -1.135632
4 -1.135632
5 -0.718815
Name: value_x, dtype: float64
Stata collapse可用于按一個或多個關(guān)鍵變量進行分組,并計算數(shù)字列上的聚合。
collapse (sum) total_bill tip, by(sex smoker)
pandas提供了一種groupby允許類似聚合的靈活機制。有關(guān) 更多詳細信息和示例,請參閱groupby文檔。
In [60]: tips_summed = tips.groupby(['sex', 'smoker'])['total_bill', 'tip'].sum()
In [61]: tips_summed.head()
Out[61]:
total_bill tip
sex smoker
Female No 869.68 149.77
Yes 527.27 96.74
Male No 1725.75 302.00
Yes 1217.07 183.07
在Stata中,如果組聚合需要與原始數(shù)據(jù)集一起使用bysort,通常會使用egen()。例如,減去吸煙者組每次觀察的平均值。
bysort sex smoker: egen group_bill = mean(total_bill)
generate adj_total_bill = total_bill - group_bill
pandas groupby提供了一種transform機制,允許在一個操作中簡潔地表達這些類型的操作。
In [62]: gb = tips.groupby('smoker')['total_bill']
In [63]: tips['adj_total_bill'] = tips['total_bill'] - gb.transform('mean')
In [64]: tips.head()
Out[64]:
total_bill tip sex smoker day time size adj_total_bill
67 1.07 1.00 Female Yes Sat Dinner 1 -17.686344
92 3.75 1.00 Female Yes Fri Dinner 2 -15.006344
111 5.25 1.00 Female No Sat Dinner 1 -11.938278
145 6.35 1.50 Female No Thur Lunch 2 -10.838278
135 6.51 1.25 Female No Thur Lunch 2 -10.678278
除聚合外,pandas groupby還可用于復(fù)制bysortStata中的大多數(shù)其他處理。例如,以下示例按性別/吸煙者組列出當前排序順序中的第一個觀察結(jié)果。
bysort sex smoker: list if _n == 1
在 pandas 中,這將寫成:
In [65]: tips.groupby(['sex', 'smoker']).first()
Out[65]:
total_bill tip day time size adj_total_bill
sex smoker
Female No 5.25 1.00 Sat Dinner 1 -11.938278
Yes 1.07 1.00 Sat Dinner 1 -17.686344
Male No 5.51 2.00 Thur Lunch 2 -11.678278
Yes 5.25 5.15 Sun Dinner 2 -13.506344
pandas 和 Stata 都只在內(nèi)存中運行。這意味著能夠在 pandas 中加載的數(shù)據(jù)大小受機器內(nèi)存的限制。如果需要進行核心處理,則有一種可能性是dask.dataframe 庫,它為磁盤上的pandas功能提供了一個子集DataFrame。
更多建議: