10 分钟入门 Mars DataFrame#
本页面是一个对 Mars DataFrame 的简短介绍,内容修改自 10 分钟入门 pandas 。
如果没有说明,我们默认导入下面的包:
In [1]: import mars
In [2]: import mars.tensor as mt
In [3]: import mars.dataframe as md
Now create a new default session.
In [4]: mars.new_session()
Out[4]: <mars.deploy.oscar.session.SyncSession at 0x7f56913c16d0>
创建对象#
通过传入一个包含值的 list 来创建 Series
实例,并使用默认的整数索引:
In [5]: s = md.Series([1, 3, 5, mt.nan, 6, 8])
In [6]: s.execute()
Out[6]:
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
通过一个 Mars Tensor 来创建 DataFrame
实例,并使用时间日期索引和列标签:
In [7]: dates = md.date_range('20130101', periods=6)
In [8]: dates.execute()
Out[8]:
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[ns]', freq='D')
In [9]: df = md.DataFrame(mt.random.randn(6, 4), index=dates, columns=list('ABCD'))
In [10]: df.execute()
Out[10]:
A B C D
2013-01-01 0.329327 -0.029431 -0.026842 1.305130
2013-01-02 0.651000 -0.608360 -0.001008 -1.196569
2013-01-03 0.653025 -1.190904 -0.597635 -1.205199
2013-01-04 -0.576515 1.126155 0.007135 -0.000057
2013-01-05 0.316763 0.290937 0.497930 -0.005145
2013-01-06 1.039720 -0.869810 -0.894397 -1.853436
通过值可转换为序列的字典来创建 DataFrame
实例:
In [11]: df2 = md.DataFrame({'A': 1.,
....: 'B': md.Timestamp('20130102'),
....: 'C': md.Series(1, index=list(range(4)), dtype='float32'),
....: 'D': mt.array([3] * 4, dtype='int32'),
....: 'E': 'foo'})
....:
In [12]: df2.execute()
Out[12]:
A B C D E
0 1.0 2013-01-02 1.0 3 foo
1 1.0 2013-01-02 1.0 3 foo
2 1.0 2013-01-02 1.0 3 foo
3 1.0 2013-01-02 1.0 3 foo
最终生成的 DataFrame
中,每列的类型均不相同。
In [13]: df2.dtypes
Out[13]:
A float64
B datetime64[ns]
C float32
D int32
E object
dtype: object
查看数据#
下面是显示 DataFrame 中开头和结尾若干行的方法:
In [14]: df.head().execute()
Out[14]:
A B C D
2013-01-01 0.329327 -0.029431 -0.026842 1.305130
2013-01-02 0.651000 -0.608360 -0.001008 -1.196569
2013-01-03 0.653025 -1.190904 -0.597635 -1.205199
2013-01-04 -0.576515 1.126155 0.007135 -0.000057
2013-01-05 0.316763 0.290937 0.497930 -0.005145
In [15]: df.tail(3).execute()
Out[15]:
A B C D
2013-01-04 -0.576515 1.126155 0.007135 -0.000057
2013-01-05 0.316763 0.290937 0.497930 -0.005145
2013-01-06 1.039720 -0.869810 -0.894397 -1.853436
显示索引和列:
In [16]: df.index.execute()
Out[16]:
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[ns]', freq='D')
In [17]: df.columns.execute()
Out[17]: Index(['A', 'B', 'C', 'D'], dtype='object')
DataFrame.to_tensor()
将 DataFrame 中的数据转换为 Mars Tensor 表示。注意当 DataFrame
中的列类型不同时,该操作可能代价很高。这也揭示了 DataFrame 和 Tensor 之间一项最基本的差异:在 Tensor 中,对于整个 Tensor 对象只有一个 dtype,但 DataFrame 对每列都有一个 dtype。当调用 DataFrame.to_tensor()
时,Mars DataFrame 将会找出一个可存储 DataFrame 中 所有 对象的 dtype,这可能是一个 object
,并将导致 DataFrame 中的每个值都被转换为一个 Python 对象。
在上面的 df
对象中,DataFrame
实例中的值均为浮点数,因而 DataFrame.to_tensor()
执行速度会很快,且不需要数据复制。
In [18]: df.to_tensor().execute()
Out[18]:
array([[ 3.29327288e-01, -2.94305001e-02, -2.68415066e-02,
1.30513044e+00],
[ 6.50999985e-01, -6.08359921e-01, -1.00752166e-03,
-1.19656929e+00],
[ 6.53024888e-01, -1.19090372e+00, -5.97635480e-01,
-1.20519898e+00],
[-5.76514802e-01, 1.12615549e+00, 7.13546231e-03,
-5.67695757e-05],
[ 3.16762710e-01, 2.90936713e-01, 4.97929948e-01,
-5.14491676e-03],
[ 1.03971953e+00, -8.69809590e-01, -8.94397213e-01,
-1.85343644e+00]])
而对于 df2
对象,DataFrame
实例中有不同的数据类型,因而 DataFrame.to_tensor()
执行代价就相对较高了。
In [19]: df2.to_tensor().execute()
Out[19]:
array([[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'foo'],
[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'foo']],
dtype=object)
备注
DataFrame.to_tensor()
在输出结果中 不保留 索引或列标签。
describe()
将会为你的数据显示一份简单的统计摘要:
In [20]: df.describe().execute()
Out[20]:
A B C D
count 6.000000 6.000000 6.000000 6.000000
mean 0.402220 -0.213569 -0.169136 -0.492546
std 0.548040 0.851468 0.496826 1.146062
min -0.576515 -1.190904 -0.894397 -1.853436
25% 0.319904 -0.804447 -0.454937 -1.203042
50% 0.490164 -0.318895 -0.013925 -0.600857
75% 0.652519 0.210845 0.005100 -0.001329
max 1.039720 1.126155 0.497930 1.305130
按坐标排序:
In [21]: df.sort_index(axis=1, ascending=False).execute()
Out[21]:
D C B A
2013-01-01 1.305130 -0.026842 -0.029431 0.329327
2013-01-02 -1.196569 -0.001008 -0.608360 0.651000
2013-01-03 -1.205199 -0.597635 -1.190904 0.653025
2013-01-04 -0.000057 0.007135 1.126155 -0.576515
2013-01-05 -0.005145 0.497930 0.290937 0.316763
2013-01-06 -1.853436 -0.894397 -0.869810 1.039720
按值排序:
In [22]: df.sort_values(by='B').execute()
Out[22]:
A B C D
2013-01-03 0.653025 -1.190904 -0.597635 -1.205199
2013-01-06 1.039720 -0.869810 -0.894397 -1.853436
2013-01-02 0.651000 -0.608360 -0.001008 -1.196569
2013-01-01 0.329327 -0.029431 -0.026842 1.305130
2013-01-05 0.316763 0.290937 0.497930 -0.005145
2013-01-04 -0.576515 1.126155 0.007135 -0.000057
选择数据#
备注
尽管在交互式分析场景下,使用标准的 Python / Numpy 表达式选择和设置 DataFrame 数据非常自然且便于使用,但对生产代码,我们推荐使用经过优化的数据访问方法,即 .at
、.iat
、.loc
和 .iloc
。
获取数据#
选择一列,将返回一个 Series
实例。这一操作等价于 df.A
:
In [23]: df['A'].execute()
Out[23]:
2013-01-01 0.329327
2013-01-02 0.651000
2013-01-03 0.653025
2013-01-04 -0.576515
2013-01-05 0.316763
2013-01-06 1.039720
Freq: D, Name: A, dtype: float64
通过 []
选择数据,将在行中选取。
In [24]: df[0:3].execute()
Out[24]:
A B C D
2013-01-01 0.329327 -0.029431 -0.026842 1.305130
2013-01-02 0.651000 -0.608360 -0.001008 -1.196569
2013-01-03 0.653025 -1.190904 -0.597635 -1.205199
In [25]: df['20130102':'20130104'].execute()
Out[25]:
A B C D
2013-01-02 0.651000 -0.608360 -0.001008 -1.196569
2013-01-03 0.653025 -1.190904 -0.597635 -1.205199
2013-01-04 -0.576515 1.126155 0.007135 -0.000057
按标签选择数据#
通过行标签选择一行数据:
In [26]: df.loc['20130101'].execute()
Out[26]:
A 0.329327
B -0.029431
C -0.026842
D 1.305130
Name: 2013-01-01 00:00:00, dtype: float64
在特定坐标上指定标签:
In [27]: df.loc[:, ['A', 'B']].execute()
Out[27]:
A B
2013-01-01 0.329327 -0.029431
2013-01-02 0.651000 -0.608360
2013-01-03 0.653025 -1.190904
2013-01-04 -0.576515 1.126155
2013-01-05 0.316763 0.290937
2013-01-06 1.039720 -0.869810
在多个坐标上指定标签,带有这些标签的 所有数据 均会被选取:
In [28]: df.loc['20130102':'20130104', ['A', 'B']].execute()
Out[28]:
A B
2013-01-02 0.651000 -0.608360
2013-01-03 0.653025 -1.190904
2013-01-04 -0.576515 1.126155
在特定坐标上降低返回对象的维度:
In [29]: df.loc['20130102', ['A', 'B']].execute()
Out[29]:
A 0.65100
B -0.60836
Name: 2013-01-02 00:00:00, dtype: float64
获得一个常量:
In [30]: df.loc['20130101', 'A'].execute()
Out[30]: 0.32932728849094994
快速获取一个常数(和前述方法等价):
In [31]: df.at['20130101', 'A'].execute()
Out[31]: 0.32932728849094994
按位置选择#
通过传入的整数选择相应位置的数据:
In [32]: df.iloc[3].execute()
Out[32]:
A -0.576515
B 1.126155
C 0.007135
D -0.000057
Name: 2013-01-04 00:00:00, dtype: float64
通过整数切片来选择数据,与 Numpy / Python 行为一致:
In [33]: df.iloc[3:5, 0:2].execute()
Out[33]:
A B
2013-01-04 -0.576515 1.126155
2013-01-05 0.316763 0.290937
通过整数列表选择相应位置的数据,与 Numpy / Python 行为一致:
In [34]: df.iloc[[1, 2, 4], [0, 2]].execute()
Out[34]:
A C
2013-01-02 0.651000 -0.001008
2013-01-03 0.653025 -0.597635
2013-01-05 0.316763 0.497930
显示对行切片:
In [35]: df.iloc[1:3, :].execute()
Out[35]:
A B C D
2013-01-02 0.651000 -0.608360 -0.001008 -1.196569
2013-01-03 0.653025 -1.190904 -0.597635 -1.205199
显示对列切片:
In [36]: df.iloc[:, 1:3].execute()
Out[36]:
B C
2013-01-01 -0.029431 -0.026842
2013-01-02 -0.608360 -0.001008
2013-01-03 -1.190904 -0.597635
2013-01-04 1.126155 0.007135
2013-01-05 0.290937 0.497930
2013-01-06 -0.869810 -0.894397
显示获取某个位置的常数:
In [37]: df.iloc[1, 1].execute()
Out[37]: -0.6083599205701119
快速获取一个常数(和前述方法等价):
In [38]: df.iat[1, 1].execute()
Out[38]: -0.6083599205701119
布尔索引#
使用一行布尔值选择数据:
In [39]: df[df['A'] > 0].execute()
Out[39]:
A B C D
2013-01-01 0.329327 -0.029431 -0.026842 1.305130
2013-01-02 0.651000 -0.608360 -0.001008 -1.196569
2013-01-03 0.653025 -1.190904 -0.597635 -1.205199
2013-01-05 0.316763 0.290937 0.497930 -0.005145
2013-01-06 1.039720 -0.869810 -0.894397 -1.853436
从 DataFrame 选择满足某个布尔条件的值:
In [40]: df[df > 0].execute()
Out[40]:
A B C D
2013-01-01 0.329327 NaN NaN 1.30513
2013-01-02 0.651000 NaN NaN NaN
2013-01-03 0.653025 NaN NaN NaN
2013-01-04 NaN 1.126155 0.007135 NaN
2013-01-05 0.316763 0.290937 0.497930 NaN
2013-01-06 1.039720 NaN NaN NaN
数据操作#
统计#
除缺失值外的常见操作:
计算描述统计值:
In [41]: df.mean().execute()
Out[41]:
A 0.402220
B -0.213569
C -0.169136
D -0.492546
dtype: float64
在另一条坐标轴上进行相同的操作:
In [42]: df.mean(1).execute()
Out[42]:
2013-01-01 0.394546
2013-01-02 -0.288734
2013-01-03 -0.585178
2013-01-04 0.139180
2013-01-05 0.275121
2013-01-06 -0.644481
Freq: D, dtype: float64
在维度不同的对象上进行操作,这需要进行对齐。此外,Mars DataFrame 会自动在给定的坐标轴上对数据进行广播操作。
In [43]: s = md.Series([1, 3, 5, mt.nan, 6, 8], index=dates).shift(2)
In [44]: s.execute()
Out[44]:
2013-01-01 NaN
2013-01-02 NaN
2013-01-03 1.0
2013-01-04 3.0
2013-01-05 5.0
2013-01-06 NaN
Freq: D, dtype: float64
In [45]: df.sub(s, axis='index').execute()
Out[45]:
A B C D
2013-01-01 NaN NaN NaN NaN
2013-01-02 NaN NaN NaN NaN
2013-01-03 -0.346975 -2.190904 -1.597635 -2.205199
2013-01-04 -3.576515 -1.873845 -2.992865 -3.000057
2013-01-05 -4.683237 -4.709063 -4.502070 -5.005145
2013-01-06 NaN NaN NaN NaN
应用函数#
在数据上应用函数:
In [46]: df.apply(lambda x: x.max() - x.min()).execute()
Out[46]:
A 1.616234
B 2.317059
C 1.392327
D 3.158567
dtype: float64
字符串方法#
如同下面的例子展示的那样,Series 对象通过 str 属性提供了一系列字符串操作方法以便于操作每一个元素。注意通过 str 进行的模式匹配通常会默认(在某些情形下会一直)用到 正则表达式 。更多的信息可在 向量化字符串方法 中查看。
In [47]: s = md.Series(['A', 'B', 'C', 'Aaba', 'Baca', mt.nan, 'CABA', 'dog', 'cat'])
In [48]: s.str.lower().execute()
Out[48]:
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object
数据合并#
拼接#
Mars DataFrame 提供一系列的方法方便地将 Series 和 DataFrame 对象连接到一起。这些方法基于一系列在索引上的集合逻辑以及关系代数上的功能来实现 Join / 合并这样的操作。
通过 concat()
: 拼接 DataFrame 对象:
In [49]: df = md.DataFrame(mt.random.randn(10, 4))
In [50]: df.execute()
Out[50]:
0 1 2 3
0 0.222750 -1.328874 0.026822 0.195442
1 1.883414 -1.343194 -1.313136 -0.717305
2 0.684763 -1.080073 -1.118718 0.096842
3 0.463415 1.353095 -1.630595 0.098278
4 -2.014634 -0.053998 -2.494387 2.054582
5 -0.703942 0.485780 2.133801 1.672256
6 1.452680 -0.929228 0.858590 0.474431
7 0.230083 -0.174632 -0.413591 0.150441
8 0.183757 -1.131558 0.604879 -0.721567
9 -0.565335 -0.026183 0.480193 0.356467
# break it into pieces
In [51]: pieces = [df[:3], df[3:7], df[7:]]
In [52]: md.concat(pieces).execute()
Out[52]:
0 1 2 3
0 0.222750 -1.328874 0.026822 0.195442
1 1.883414 -1.343194 -1.313136 -0.717305
2 0.684763 -1.080073 -1.118718 0.096842
3 0.463415 1.353095 -1.630595 0.098278
4 -2.014634 -0.053998 -2.494387 2.054582
5 -0.703942 0.485780 2.133801 1.672256
6 1.452680 -0.929228 0.858590 0.474431
7 0.230083 -0.174632 -0.413591 0.150441
8 0.183757 -1.131558 0.604879 -0.721567
9 -0.565335 -0.026183 0.480193 0.356467
Join#
SQL 样式的数据合并。参考 Database style joining 章节以获取更多信息。
In [53]: left = md.DataFrame({'key': ['foo', 'foo'], 'lval': [1, 2]})
In [54]: right = md.DataFrame({'key': ['foo', 'foo'], 'rval': [4, 5]})
In [55]: left.execute()
Out[55]:
key lval
0 foo 1
1 foo 2
In [56]: right.execute()
Out[56]:
key rval
0 foo 4
1 foo 5
In [57]: md.merge(left, right, on='key').execute()
Out[57]:
key lval rval
0 foo 1 4
1 foo 1 5
2 foo 2 4
3 foo 2 5
另一个可供参考的例子如下:
In [58]: left = md.DataFrame({'key': ['foo', 'bar'], 'lval': [1, 2]})
In [59]: right = md.DataFrame({'key': ['foo', 'bar'], 'rval': [4, 5]})
In [60]: left.execute()
Out[60]:
key lval
0 foo 1
1 bar 2
In [61]: right.execute()
Out[61]:
key rval
0 foo 4
1 bar 5
In [62]: md.merge(left, right, on='key').execute()
Out[62]:
key lval rval
0 foo 1 4
1 bar 2 5
分组#
当提到“分组”时,我们指的是下面一个或多个步骤组成的过程:
拆分 :根据某些条件将数据拆分成组
应用函数 :对每一组数据分别应用某个函数
合并 :将结果合并为一组数据
In [63]: df = md.DataFrame({'A': ['foo', 'bar', 'foo', 'bar',
....: 'foo', 'bar', 'foo', 'foo'],
....: 'B': ['one', 'one', 'two', 'three',
....: 'two', 'two', 'one', 'three'],
....: 'C': mt.random.randn(8),
....: 'D': mt.random.randn(8)})
....:
In [64]: df.execute()
Out[64]:
A B C D
0 foo one 1.216915 0.544496
1 bar one -0.777546 0.430164
2 foo two 2.129245 0.951616
3 bar three 1.975048 0.814219
4 foo two -0.121642 0.430608
5 bar two -0.562698 -1.265521
6 foo one -0.934362 -0.275044
7 foo three 0.807355 -0.763585
分组,然后在结果上执行 sum()
函数。
In [65]: df.groupby('A').sum().execute()
Out[65]:
C D
A
bar 0.634804 -0.021138
foo 3.097512 0.888090
我们也可以利用多列进行分组,这将形成一个多重索引。在此结果上,我们也可以执行 sum 函数。
In [66]: df.groupby(['A', 'B']).sum().execute()
Out[66]:
C D
A B
bar one -0.777546 0.430164
three 1.975048 0.814219
two -0.562698 -1.265521
foo one 0.282553 0.269451
three 0.807355 -0.763585
two 2.007604 1.382223
绘图#
我们使用标准的约定来引用 matplotlib API:
In [67]: import matplotlib.pyplot as plt
In [68]: plt.close('all')
In [69]: ts = md.Series(mt.random.randn(1000),
....: index=md.date_range('1/1/2000', periods=1000))
....:
In [70]: ts = ts.cumsum()
In [71]: ts.plot()
Out[71]: <AxesSubplot:>
在 DataFrame 中, plot()
方法可用于方便地绘制带有标签的行数据:
In [72]: df = md.DataFrame(mt.random.randn(1000, 4), index=ts.index,
....: columns=['A', 'B', 'C', 'D'])
....:
In [73]: df = df.cumsum()
In [74]: plt.figure()
Out[74]: <Figure size 640x480 with 0 Axes>
In [75]: df.plot()
Out[75]: <AxesSubplot:>
In [76]: plt.legend(loc='best')
Out[76]: <matplotlib.legend.Legend at 0x7f5691130850>
读取和写入数据#
CSV#
In [77]: df.to_csv('foo.csv').execute()
Out[77]:
Empty DataFrame
Columns: []
Index: []
In [78]: md.read_csv('foo.csv').execute()
Out[78]:
Unnamed: 0 A B C D
0 2000-01-01 2.029454 -0.991979 -0.377055 0.160321
1 2000-01-02 1.867585 -1.388289 0.774497 1.473048
2 2000-01-03 2.272194 -0.103845 0.664114 1.737556
3 2000-01-04 1.261450 -1.069347 2.236852 2.228680
4 2000-01-05 0.315678 0.211495 4.101824 2.832870
.. ... ... ... ... ...
995 2002-09-22 -35.199514 -4.208297 17.866823 -30.635666
996 2002-09-23 -34.060133 -4.577472 17.282638 -31.745116
997 2002-09-24 -33.903483 -2.988547 16.764647 -32.916958
998 2002-09-25 -33.484611 -2.310996 16.140174 -33.788386
999 2002-09-26 -34.025874 -2.245415 17.686845 -32.749834
[1000 rows x 5 columns]