数据结构介绍
Pandas的数据对象中都包含最基本的属性,如数据类型,索引,标签等。
要使用Pandas的数据结构首先需要引入pandas和numpy:
In [1]: import numpy as np
In [2]: import pandas as pd
有一个基本原则要牢记:数据对齐是默认的。数据和标签的关联不会被破坏,除非认为更改。
本章将做一个关于数据结构的简介,然后在其他章节中做更全面的介绍。
Series
Series 是一维标签数组,能够保存任何数据类型(整型,浮点型,字符串或其他Python对象类型)。轴标签被称为索引。创建一个最基本的Series结构,代码如下:
s = pd.Series(data, index=index)
data可以是很多类型:
- 一个 Python 字典
- 一个 ndarray 对象
- 一个标量值(如5)
index是轴标签的列表。因此,这将根据data的不同分为几种情况:
由ndarray构造
如果传递的data是一个ndarray对象,index的长度必须和data的长度保持一致。如果没有对index参数赋值,那么索引值会默认为[0, … , len(data) -1],即由0开始,与data数据等长的递增列表。
In [3]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
这里构造了一个名为s的Serise对象,其data为5个随机数,那么对应的index长度也为5, 分别为a至e的小写字母。
那么s打印出来的结果如下:
In [4]: s
Out[4]:
a -1.317092
b 0.898475
c -0.026741
d -0.090660
e -0.783084
dtype: float64
通过输入s.index可以查看索引
In [6]: s.index
Out[6]: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object')
如果不为index参数赋值,那么构造的Series对象结果为
In [7]: s = pd.Series(np.random.randn(5))
In [8]: s
Out[8]:
0 -2.750907
1 2.402623
2 -2.479244
3 1.826535
4 -1.270192
dtype: float64
注意:从v0.8.0开始,pandas可以支持索引值不唯一。
如:
In [6]: s = pd.Series(np.random.randn(5), index=[‘a’, ‘b’, ‘b’, ‘c’, ‘d’])
In [7]: s
Out[7]:
a 0.285951
b -0.153731
b 0.536586
c 2.156944
d -0.113776
dtype: float64
由dict 字典类型构造
如果传递的data是一个dict字典类型对象,并且传递了index参数,那么对应的值将从字典中取出。
In [8]: d = {'a':0, 'b':1, 'c':2}
In [11]: pd.Series(d, index=['b','c','d', 'a'])
Out[11]:
b 1
c 2
d NaN
a 0
dtype: float64
注意:NaN(not a number)是Pandas中使用的数据缺失的标记,由于data中没有包含key为’d’的数据,所以返回数据缺失,标记为NaN。
否则,index的值将由字典对象里的key值进行构造。
In [8]: d = {'a':0, 'b':1, 'c':2}
In [9]: pd.Series(d)
Out[9]:
a 0
b 1
c 2
dtype: int64
由标量值构造
如果传递的data是一个标量值,那么Index参数必须提供。其构造的二维数组对象将根据索引的长度进行重复。
In [12]: pd.Series(5, index=['b', 'c', 'd', 'a'])
Out[12]:
b 5
c 5
d 5
a 5
dtype: int64
Series类似于ndarray
Serise扮演的角色非常类似ndarray,并且它可以作为大多数Numpy函数的参数。也可以通过对索引切割来进行数据切片。
In [14]: s[0]
Out[14]: 0.28595142701029524
In [15]: s[:3]
Out[15]:
a 0.285951
b -0.153731
b 0.536586
dtype: float64
In [16]: s[s > s.median()]
Out[16]:
b 0.536586
c 2.156944
dtype: float64
In [17]: s[[4, 3, 1]]
Out[17]:
d -0.113776
c 2.156944
b -0.153731
dtype: float64
In [18]: np.exp(s)
Out[18]:
a 1.331028
b 0.857502
b 1.710159
c 8.644678
d 0.892458
dtype: float64
Series类似于dict
Series类似于定长的字典对象,你可以通过index标签获取或设置值。
In [19]: s['a']
Out[19]: 0.28595142701029524
In [20]: s['c'] = 12
In [21]: s
Out[21]:
a 0.285951
b -0.153731
b 0.536586
c 12.000000
d -0.113776
In [22]: 'b' in s
Out[22]: True
In [23]: 'e' in s
Out[23]: False
如果输入的标签不存在,那么将报异常:
In [24]: s['e']
KeyError: 'e'
如果使用get方法,不存在的标签将会返回空值或指定默认值:
In [25]: s.get('e')
In [26]: s.get('e', np.nan)
Out[26]: nan
向量操作和标签对齐
当做数据分析时, 和Numpy的数组一样,一个一个的循环遍历Series中的值通常是不必要的。Series对象也可以像ndarray一样,传递到大多数Numpy方法中。
In [27]: s + s
Out[27]:
a 0.571903
b -0.307463
b 1.073173
c 24.000000
d -0.227552
dtype: float64
In [28]: s * 2
Out[28]:
a 0.571903
b -0.307463
b 1.073173
c 24.000000
d -0.227552
dtype: float64
In [29]: np.exp(s)
Out[29]:
a 1.331028
b 0.857502
b 1.710159
c 162754.791419
d 0.892458
dtype: float64
Series和ndarray关键的区别在于,Series间的操作会自动根据标签对齐数据。因此,你可以直接编写计算,而不用考虑所涉及到的Series是否具有相同的标签。
In [30]: s[1:] + s[:-1]
Out[30]:
a NaN
b -0.307463
b 0.382855
b 0.382855
b 1.073173
c 24.000000
d NaN
dtype: float64
在未对齐的Series间操作,结果将包含索引的集合。如果标签在一个或另一个Series中未找到,结果将标记为缺失NaN。所以可以在不进行任何显示数据对齐的情况下编写代码,在交互数据分析和研究中提供了巨大的自由度和灵活性。Pandas数据结构的综合数据排列特征使Pandas有别于大多数用于标记数据的相关工具。
Name 属性
Series也有Name属性
In [31]: s = pd.Series(np.random.randn(5), name='something')
In [32]: s
Out[32]:
0 1.522774
1 0.733561
2 -0.702462
3 0.022205
4 1.704067
Name: something, dtype: float64
许多情况下,Series的Name将被自动分配,特别是下面即将看到的对于DataFrame的一维切片时。
可以通过方法pandas.Series.rename()对Series进行重命名。
In [33]: s2 = s.rename("different")
In [34]: s2.name
Out[35]: 'different'
注意s和s2分别引用的是两个不同的对象。
DataFrame
DataFrame是一个2维标签的数据结构,它的列可以存在不同的类型。你可以把它简单的想成Excel表格或SQL Table,或者是包含字典类型的Series。它是最常用的Pandas对象。和Series一样,DataFrame接受许多不同的类型输入:
- 包含1维ndarray,列表对象,字典对象或者Series对象的字典对象
- 2维的ndarray对象
- 结构化或记录型的ndarray
- Series对象
- 另一个DataFrame对象
可以通过传递索引(行标签)和列(列标签)参数来操作数据。如果传递了索引和/或列,可以得到包含索引和/或列的DataFrame结果集。因此,一个字典类型的Series加上一个特定的索引,将会丢弃所有与传递的所以不匹配的数据。
如果没有传递轴标签,他们将基于常用规则的输入数据进行创建。
由包含Series的字典或嵌套字典构造
结果的索引将是各个Series索引的并集。如果有任何嵌套的字典对象,都将先转换成Series。如果没有传递任何列,那么列将是已排序的字典对象的Key值。
In [20]: d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
...: 'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
In [21]: df = pd.DataFrame(d)
In [22]: df
Out[22]:
one two
a 1 1
b 2 2
c 3 3
d NaN 4
In [23]: pd.DataFrame(d, index=['d', 'b', 'a'])
Out[23]:
one two
d NaN 4
b 2 2
a 1 1
In [24]: pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])
Out[24]:
two three
d 4 NaN
b 2 NaN
a 1 NaN
通过访问索引和列属性,可以分别访问行和列标签:
In [25]: df.index
Out[25]: Index([u'a', u'b', u'c', u'd'], dtype='object')
df.columns
Out[26]: Index([u'one', u'two'], dtype='object')
由包含ndarray或列表的字典构造
ndarray的长度必须一致。如果一个索引被传递,它必须与数组的长度相同。如果没有索引被传递,结果将是range(n),n是数组的长度。
In [27]: d = {'one':[1., 2., 3., 4.],
...: 'two': [4., 3., 2., 1.]}
In [28]: pd.DataFrame(d)
Out[28]:
one two
0 1 4
1 2 3
2 3 2
3 4 1
In [29]: pd.DataFrame(d, index=['a', 'b', 'c', 'd'])
Out[29]:
one two
a 1 4
b 2 3
c 3 2
d 4 1
由数组构造
这个例子的处理与数组字典的完全相同
In [39]: data = np.zeros((2,), dtype=[('A', 'i4'), ('B', 'f4'), ('C', 'a10')])
In [40]: data[:] = [(1, 2., 'Hello'), (2, 3., 'World')]
In [41]: pd.DataFrame(data)
Out[41]:
A B C
0 1 2 Hello
1 2 3 World
In [42]: pd.DataFrame(data, index=['first', 'second'])
Out[42]:
A B C
first 1 2 Hello
second 2 3 World
In [43]: pd.DataFrame(data, columns=['C', 'A', 'B'])
Out[43]:
C A B
0 Hello 1 2
1 World 2 3
注意:DataFrame的工作方式与2维的ndarray并不一样
由包含字典的列表构造
In [44]: data = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
In [45]: pd.DataFrame(data)
Out[45]:
a b c
0 1 2 NaN
1 5 10 20
In [47]: pd.DataFrame(data, index=['first', 'second'])
Out[47]:
a b c
first 1 2 NaN
second 5 10 20
In [48]: pd.DataFrame(data, columns=['a', 'b'])
Out[48]:
a b
0 1 2
1 5 10
由包含元组的字典构造
In [49]: pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
...: ('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
...: ('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
...: ('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
...: ('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}})
Out[49]:
a b
a b c a b
A B 4 1 5 8 10
C 3 2 6 7 NaN
D NaN NaN NaN NaN 9
由Series构造
结果将是索引与输入的Series相同,并且有一列数据,列名与Series的名称一致(仅在没有提供其他列名的情况下)。
In [58]: s = pd.Series([1, 2, 3], index=['a', 'b', 'c'], name='first')
In [59]: s
Out[59]:
a 1
b 2
c 3
Name: first, dtype: int64
In [60]: pd.DataFrame(s)
Out[60]:
first
a 1
b 2
c 3
缺失数据
跟多的缺失数据相关内容将在其他章节介绍。为了构造一个包含缺失数据的DataFrame,对于那些缺失的值需要用到np.nan。或者,将numpy.MaskedArray作为data参数传递给DataFrame的构造函数,它所遮盖的条目将被认为是缺失的。
其他构造方式
DataFrame.from_dict
DataFrame.from_dict将获取一个嵌套字典或者数组字典,并返回一个DataFrame。它的操作方式类似于DataFrame的构造函数,除了orient参数默认为column,但是可以将它设置为index,让字典的key值作为行标签。
DataFrame.from_records
DataFrame.from_records将获取一个元组构成的列表或者一个结构化的ndarray对象。与普通的DataFrame构造函数类似,除了索引可能是结构化的dtype的特定字段。例如:
In [61]: data
Out[61]: [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
In [62]: data = np.zeros((2,), dtype=[('A', 'i4'),('B', 'f4'),('C', 'a10')])
In [63]: data[:] = [(1, 2, 'Hello'), (2, 3, 'World')]
In [64]: data
Out[64]:
array([(1, 2.0, 'Hello'), (2, 3.0, 'World')],
dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])
In [66]: pd.DataFrame.from_records(data, index='C')
Out[66]:
A B
C
Hello 1 2
World 2 3
DataFrame.from_items
DataFrame.from_items 运行机制类似于dict的构造函数,传递一个键值对序列作为参数。Key是列名(或索引名,orient=’index’),Value是列的值(或行的值)。这对于构造具有特定顺序的列的DataFrame是很有用的,而不用传递列的明确列表:
In [68]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])])
Out[68]:
A B
0 1 4
1 2 5
2 3 6
如果传递orient=’index’,key值将作为索引标签。但是在下面的例子中任需要列名:
In [73]: pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])], orient='index', columns=['one', 'two', 'three'])
Out[73]:
one two three
A 1 2 3
B 4 5 6
列的选择、添加和删除
类似于对字典操作的语法,你可以对一个DataFrame进行获取列的值,对列赋值或删除列。
In [74]: df['one']
Out[74]:
a 1
b 2
c 3
d NaN
Name: one, dtype: float64
In [75]: df['three'] = df['one'] * df['two']
In [76]: df['flag'] = df['one'] > 2
In [77]: df
Out[77]:
one two three flag
a 1 1 1 False
b 2 2 4 False
c 3 3 9 True
d NaN 4 NaN False
列可以类似于dict一样,删除或者取出
In [78]: del df['two']
In [79]: three = df.pop('three')
In [80]: df
Out[80]:
one flag
a 1 False
b 2 False
c 3 True
d NaN False
In [81]: three
Out[81]:
a 1
b 4
c 9
d NaN
Name: three, dtype: float64
当插入一个标量值时,它会自动的填满整列
In [82]: df['foo'] = 'bar'
In [83]: df
Out[83]:
one flag foo
a 1 False bar
b 2 False bar
c 3 True bar
d NaN False bar
当插入一个与DataFrame没有相同索引的Series时,它将匹配DataFrame的索引
In [84]: df['one_trunc'] = df['one'][:2]
In [85]: df
Out[85]:
one flag foo one_trunc
a 1 False bar 1
b 2 False bar 2
c 3 True bar NaN
d NaN False bar NaN
也可以插入ndarray,但是它的长度必须与DataFrame的索引长度匹配。
默认情况下,列在最后插入。insert函数可用于插入在列的制定位置:
In [87]: df
Out[87]:
one bar flag foo one_trunc
a 1 1 False bar 1
b 2 2 False bar 2
c 3 3 True bar NaN
d NaN NaN False bar NaN
通过方法分配新列
DataFrame具有assign()方法,允许你很方便的创建从现有列派生出来的新列。
In [47]: iris = pd.read_csv(u'data/iris.csv')
In [48]: iris.head()
Out[48]:
SepalLength SepalWidth PetalLength PetalWidth Name
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
In [49]: (iris.assign(sepal_ratio = iris['SepalWidth'] / iris['SepalLength']).head())
Out[49]:
SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio
0 5.1 3.5 1.4 0.2 Iris-setosa 0.686275
1 4.9 3.0 1.4 0.2 Iris-setosa 0.612245
2 4.7 3.2 1.3 0.2 Iris-setosa 0.680851
3 4.6 3.1 1.5 0.2 Iris-setosa 0.673913
4 5.0 3.6 1.4 0.2 Iris-setosa 0.720000
以上的例子中,先原有的数据中增加了一个预先计算的值。我们同样还可以传递带有一个参数的函数
In [50]: iris.assign(sepal_ratio = lambda x: (x['SepalWidth']/x['SepalLength'])).head()
Out[50]:
SepalLength SepalWidth PetalLength PetalWidth Name sepal_ratio
0 5.1 3.5 1.4 0.2 Iris-setosa 0.686275
1 4.9 3.0 1.4 0.2 Iris-setosa 0.612245
2 4.7 3.2 1.3 0.2 Iris-setosa 0.680851
3 4.6 3.1 1.5 0.2 Iris-setosa 0.673913
4 5.0 3.6 1.4 0.2 Iris-setosa 0.720000
assign 始终返回数据的副本,让原始的DataFrame保持原样。
传递一个可调用的,而不是要插入的实际值,当你没有对DataFrame引用时,这是非常有帮助的。在操作链中使用assign,这是很常见的。例如,我们可以把DataFrame限定为花萼长度大于5的数据,然后计算这个比例,进行绘图:
In [51]: (iris.query('SepalLength > 5')
...: .assign(SepalRatio = lambda x: x.SepalWidth / x.SepalLength,
...: PetalRatio = lambda x: x.PetalWidth / x.PetalLength)
...: .plot(kind='scatter', x='SepalRatio', y='PetalRatio'))
Out[51]: <matplotlib.axes._subplots.AxesSubplot at 0x1049b8b0>
当函数被传入,函数将在分配给DataFrame时进行计算。重点时,这是将数据过滤为Sepal 长度大于5的行。数据首先被过滤,然后再进行比例的计算。
assign方法的参数 **kwargs。key是列名,value是要插入的值(如Series或Numpy的 array),或者是含有一个参数的函数。调用后将返回,原有的DataFrame的副本加上新增的值。
警告:由于方法的参数为**kwargs,一个字典类型,所以产生的DataFrame中的新列的顺序不能保证与你传入的顺序相同。为了让结果可以预测,在DataFrame的末尾,数据条目将按字母顺序插入。
所有的表达式先行计算,再分配。因此,不能引用另一个在调用时分配的列。比如:
`In [74]: # 不用这样做, 无法引用‘C’列
df.assign(C = lambda x: x[‘A’] + x[‘B’],
D = lambda x: x[‘A’] + x[‘C’])
In [2]: # 更改为调用两次assign方法
(df.assign(C = lambda x: x[‘A’] + x[‘B’])
.assign(D = lambda x: x[‘A’] + x[‘C’]))’
索引/选择
索引的基本操作如下:
操作 | 语法 | 结果 |
---|---|---|
选择列 | df[col] | Series |
根据标签选择行 | df.loc[lable] | Series |
根据位置选择行 | df.iloc[loc] | Series |
行切片 | df[5:10] | DataFrame |
根据条件选择行 | df[bool_vec] | DataFrame |
行选择器,如,根据索引返回列的Series:
#沿用之前的df对象
In [63]: df
Out[63]:
one bar flag foo one_trunc
a 1 1 False bar 1
b 2 2 False bar 2
c 3 3 True bar NaN
d NaN NaN False bar NaN
#根据索引标签选择
In [64]: df.loc['b']
Out[64]:
one 2
bar 2
flag False
foo bar
one_trunc 2
Name: b, dtype: object
#根据位置选择
In [65]: df.iloc[2]
Out[65]:
one 3
bar 3
flag True
foo bar
one_trunc NaN
Name: c, dtype: object
数据对齐和运算
DataFrame对象之间的数据对齐会自动在列和索引(行标签)上对齐。同样,生产的对象将是列和行标签的并集。
In [69]: df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
In [70]: df
Out[70]:
A B C D
0 -1.081213 0.964799 -1.526936 0.857095
1 0.786559 -0.824999 0.373886 0.383198
2 -0.026515 -0.539306 0.987269 0.045101
3 -0.726887 -1.176843 -0.799625 -0.192155
4 -1.180493 2.145532 0.682645 0.317200
5 1.041298 -1.334093 0.346744 -0.222402
6 -0.553535 -1.031090 -1.738747 -0.404298
7 0.367074 -1.312607 0.811453 -0.829041
8 1.150281 -0.435246 0.686140 1.672713
9 -2.811454 -0.064040 -0.173748 0.156016
In [71]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])
In [72]: df2
Out[72]:
A B C
0 -0.116847 2.508202 -0.206053
1 -0.264634 -0.440654 0.355929
2 -0.805070 1.162288 0.637293
3 -0.423643 0.854117 -0.385428
4 0.790752 0.084708 -0.699494
5 2.139285 -0.546327 0.381495
6 -0.086828 1.019701 0.448619
In [73]: df + df2
Out[73]:
A B C D
0 -1.198059 3.473001 -1.732989 NaN
1 0.521925 -1.265653 0.729814 NaN
2 -0.831585 0.622982 1.624562 NaN
3 -1.150530 -0.322726 -1.185053 NaN
4 -0.389741 2.230240 -0.016849 NaN
5 3.180583 -1.880420 0.728239 NaN
6 -0.640363 -0.011389 -1.290128 NaN
7 NaN NaN NaN NaN
8 NaN NaN NaN NaN
9 NaN NaN NaN NaN
当在DataFrame和Series之间进行操作时,默认的行为是在DataFrame列上对其Series索引,然后安装行的宽度进行广播。例如:
In [74]: df.iloc[0]
Out[74]:
A -1.081213
B 0.964799
C -1.526936
D 0.857095
Name: 0, dtype: float64
In [75]: df - df.iloc[0]
Out[75]:
A B C D
0 0.000000 0.000000 0.000000 0.000000
1 1.867771 -1.789798 1.900821 -0.473897
2 1.054698 -1.504105 2.514205 -0.811994
3 0.354326 -2.141642 0.727311 -1.049250
4 -0.099280 1.180733 2.209580 -0.539896
5 2.122511 -2.298892 1.873680 -1.079498
6 0.527677 -1.995888 -0.211811 -1.261393
7 1.448286 -2.277405 2.338389 -1.686136
8 2.231494 -1.400045 2.213076 0.815618
9 -1.730241 -1.028839 1.353188 -0.701080
在处理时间Series数据的特殊情况下,并且DataFrame索引也包含日期,将根据列的宽度进行广播:
In [76]: index = pd.date_range('1/1/2000', periods=8)
In [77]: index
Out[77]:
DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-03', '2000-01-04',
'2000-01-05', '2000-01-06', '2000-01-07', '2000-01-08'],
dtype='datetime64[ns]', freq='D', tz=None)
In [78]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC'))
In [79]: df
Out[79]:
A B C
2000-01-01 1.658336 0.312690 0.434380
2000-01-02 1.588613 1.044227 -0.548043
2000-01-03 1.453697 0.634530 -1.125464
2000-01-04 1.799968 -0.579391 0.047857
2000-01-05 -0.053769 0.067639 0.195662
2000-01-06 -1.863360 0.772522 0.690778
2000-01-07 -0.157916 -0.605435 0.324758
2000-01-08 -1.265800 0.157990 0.022863
In [80]: type(df['A'])
Out[80]: pandas.core.series.Series
In [81]: df - df['A']
Out[81]:
2000-01-01 00:00:00 2000-01-02 00:00:00 2000-01-03 00:00:00 \
2000-01-01 NaN NaN NaN
2000-01-02 NaN NaN NaN
2000-01-03 NaN NaN NaN
2000-01-04 NaN NaN NaN
2000-01-05 NaN NaN NaN
2000-01-06 NaN NaN NaN
2000-01-07 NaN NaN NaN
2000-01-08 NaN NaN NaN
2000-01-04 00:00:00 ... 2000-01-08 00:00:00 A B C
2000-01-01 NaN ... NaN NaN NaN NaN
2000-01-02 NaN ... NaN NaN NaN NaN
2000-01-03 NaN ... NaN NaN NaN NaN
2000-01-04 NaN ... NaN NaN NaN NaN
2000-01-05 NaN ... NaN NaN NaN NaN
2000-01-06 NaN ... NaN NaN NaN NaN
2000-01-07 NaN ... NaN NaN NaN NaN
2000-01-08 NaN ... NaN NaN NaN NaN
[8 rows x 11 columns]
警告
df-df['A']
现在已被弃用,并将被删除。实现这种行为的最佳方式
df.sub(df['A'], axis=0)
标量的操作正如你期望的那样:
In [85]: df * 5 + 2
Out[85]:
A B C
2000-01-01 10.291678 3.563450 4.171900
2000-01-02 9.943063 7.221136 -0.740213
2000-01-03 9.268486 5.172651 -3.627319
2000-01-04 10.999838 -0.896953 2.239286
2000-01-05 1.731153 2.338194 2.978308
2000-01-06 -7.316800 5.862611 5.453892
2000-01-07 1.210420 -1.027175 3.623791
2000-01-08 -4.329001 2.789950 2.114315
In [86]: 1 / df
Out[86]:
A B C
2000-01-01 0.603014 3.198056 2.302132
2000-01-02 0.629480 0.957646 -1.824675
2000-01-03 0.687901 1.575969 -0.888523
2000-01-04 0.555566 -1.725951 20.895489
2000-01-05 -18.597913 14.784416 5.110867
2000-01-06 -0.536665 1.294461 1.447642
2000-01-07 -6.332484 -1.651705 3.079215
2000-01-08 -0.790014 6.329511 43.738650
In [87]: df ** 4
Out[87]:
A B C
2000-01-01 7.562925 0.009560 3.560239e-02
2000-01-02 6.369012 1.188995 9.021058e-02
2000-01-03 4.465763 0.162110 1.604450e+00
2000-01-04 10.496843 0.112690 5.245535e-06
2000-01-05 0.000008 0.000021 1.465621e-03
2000-01-06 12.055550 0.356159 2.276957e-01
2000-01-07 0.000622 0.134360 1.112346e-02
2000-01-08 2.567206 0.000623 2.732364e-07
布尔运算同样有效
In [88]: df1 = pd.DataFrame({'a': [1, 0, 1], 'b': [0, 1, 1]}, dtype=bool)
In [89]: df2 = pd.DataFrame({'a': [0, 1, 1], 'b': [1, 1, 0]}, dtype=bool)
In [90]: df1 & df2
Out[90]:
a b
0 False False
1 False True
2 True False
In [91]: df1 | df2
Out[91]:
a b
0 True True
1 True True
2 True True
In [92]: df1 ^ df2
Out[92]:
a b
0 True True
1 True False
2 False True
In [93]: -df1
Out[93]:
a b
0 False True
1 True False
2 False False
行列转换
对于行列转换,访问T属性(也就是transpose函数),类似于ndarray:
In [95]: df[:5]
Out[95]:
A B C
2000-01-01 1.658336 0.312690 0.434380
2000-01-02 1.588613 1.044227 -0.548043
2000-01-03 1.453697 0.634530 -1.125464
2000-01-04 1.799968 -0.579391 0.047857
2000-01-05 -0.053769 0.067639 0.195662
In [96]: df[:5].T
Out[96]:
2000-01-01 2000-01-02 2000-01-03 2000-01-04 2000-01-05
A 1.658336 1.588613 1.453697 1.799968 -0.053769
B 0.312690 1.044227 0.634530 -0.579391 0.067639
C 0.434380 -0.548043 -1.125464 0.047857 0.195662
DataFrame与Numpy函数的互操作
如果对象中的数据是数值型的,那么可以在DataFrame上使用Numpy的ufunc(log, exp, sqrt,…)和其他各种函数:
In [97]: np.exp(df)
Out[97]:
A B C
2000-01-01 5.250565 1.367098 1.544005
2000-01-02 4.896950 2.841202 0.578080
2000-01-03 4.278905 1.886136 0.324502
2000-01-04 6.049451 0.560240 1.049021
2000-01-05 0.947651 1.069979 1.216115
2000-01-06 0.155150 2.165220 1.995268
2000-01-07 0.853922 0.545837 1.383696
2000-01-08 0.282014 1.171155 1.023126
In [98]: np.asarray(df)
Out[98]:
array([[ 1.65833568, 0.31269 , 0.43437997],
[ 1.58861263, 1.04422716, -0.54804266],
[ 1.45369711, 0.63453025, -1.12546386],
[ 1.79996757, -0.57939064, 0.04785722],
[-0.05376948, 0.06763879, 0.19566152],
[-1.86335996, 0.77252217, 0.69077835],
[-0.15791591, -0.60543491, 0.3247581 ],
[-1.26580024, 0.15799008, 0.02286307]])
DataFrame上的dot方法,实现了矩阵的点积运算:
In [99]: df.T.dot(df)
Out[99]:
A B C
A 15.729075 0.509448 -3.078136
B 0.509448 2.919385 -0.824455
C -3.078136 -0.824455 2.379445
类似的,Series的dot方法也实现了点积运算:
In [101]: s1 = pd.Series(np.arange(5, 10))
In [102]: s1
Out[102]:
0 5
1 6
2 7
3 8
4 9
dtype: int32
In [103]: s1.dot(s1)
Out[103]: 255
控制台显示
非常大的DataFrame将在控制台中被截断显示。你可以使用info()来获取摘要信息。
In [108]: iris = pd.read_csv(u'data/iris.csv')
In [109]: print(iris)
SepalLength SepalWidth PetalLength PetalWidth Name
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
5 5.2 3.5 1.4 0.2 Iris-setosa
6 4.9 3.0 1.7 0.2 Iris-setosa
7 5.4 3.2 1.5 0.3 Iris-setosa
8 4.6 3.1 1.4 0.2 Iris-setosa
9 5.3 3.6 1.4 0.2 Iris-setosa
10 5.1 3.5 1.4 0.2 Iris-setosa
11 4.9 3.0 1.4 0.3 Iris-setosa
12 4.7 3.6 1.3 0.2 Iris-setosa
13 4.6 3.1 1.5 0.2 Iris-setosa
14 5.0 3.6 1.4 0.2 Iris-setosa
15 5.2 3.5 1.4 0.2 Iris-setosa
16 4.9 3.0 1.6 0.2 Iris-setosa
17 5.4 3.2 1.7 0.3 Iris-setosa
18 4.6 3.2 1.5 0.2 Iris-setosa
19 5.3 3.1 1.4 0.4 Iris-setosa
20 5.3 3.6 1.4 0.2 Iris-setosa
21 5.1 3.0 1.4 0.2 Iris-setosa
22 4.9 3.2 1.4 0.2 Iris-setosa
23 4.7 3.1 1.4 0.5 Iris-setosa
24 4.9 3.6 1.3 0.2 Iris-setosa
25 4.7 3.5 1.5 0.3 Iris-setosa
26 4.6 3.0 1.4 0.2 Iris-setosa
27 5.3 3.6 1.3 0.2 Iris-setosa
28 5.3 3.1 1.8 0.2 Iris-setosa
29 5.3 3.0 1.4 0.7 Iris-setosa
.. ... ... ... ... ...
573 5.1 3.5 1.4 0.2 Iris-setosa
574 4.9 3.0 1.4 0.3 Iris-setosa
575 4.7 3.6 1.3 0.2 Iris-setosa
576 4.6 3.1 1.5 0.2 Iris-setosa
577 5.0 3.6 1.4 0.2 Iris-setosa
578 5.2 3.5 1.4 0.2 Iris-setosa
579 4.9 3.0 1.6 0.2 Iris-setosa
580 5.4 3.2 1.7 0.3 Iris-setosa
581 4.6 3.2 1.5 0.2 Iris-setosa
582 5.3 3.1 1.4 0.4 Iris-setosa
583 5.3 3.6 1.4 0.2 Iris-setosa
584 5.1 3.0 1.4 0.2 Iris-setosa
585 4.9 3.2 1.4 0.2 Iris-setosa
586 4.7 3.1 1.4 0.5 Iris-setosa
587 4.9 3.6 1.3 0.2 Iris-setosa
588 4.7 3.5 1.5 0.3 Iris-setosa
589 4.6 3.0 1.4 0.2 Iris-setosa
590 5.3 3.6 1.3 0.2 Iris-setosa
591 5.3 3.1 1.8 0.2 Iris-setosa
592 5.3 3.0 1.4 0.7 Iris-setosa
593 5.1 3.2 1.4 0.2 Iris-setosa
594 4.9 3.1 1.4 0.2 Iris-setosa
595 4.7 3.6 1.3 0.2 Iris-setosa
596 5.1 3.1 1.5 0.2 Iris-setosa
597 4.9 3.6 1.4 0.3 Iris-setosa
598 5.2 3.5 1.4 0.2 Iris-setosa
599 4.9 3.0 1.6 0.2 Iris-setosa
600 5.4 3.2 1.4 0.2 Iris-setosa
601 4.6 3.1 1.3 0.7 Iris-setosa
602 5.3 3.6 1.8 0.2 Iris-setosa
[603 rows x 5 columns]
In [110]: iris.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 603 entries, 0 to 602
Data columns (total 5 columns):
SepalLength 603 non-null float64
SepalWidth 603 non-null float64
PetalLength 603 non-null float64
PetalWidth 603 non-null float64
Name 603 non-null object
dtypes: float64(4), object(1)
memory usage: 25.9+ KB
In [111]: iris = pd.read_csv(u'data/iris.csv')
In [112]: print(iris)
SepalLength SepalWidth PetalLength PetalWidth Name
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
.. ... ... ... ................
600 5.4 3.2 1.4 0.2 Iris-setosa
601 4.6 3.1 1.3 0.7 Iris-setosa
602 5.3 3.6 1.8 0.2 Iris-setosa
[603 rows x 5 columns]
In [113]: iris.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 603 entries, 0 to 602
Data columns (total 5 columns):
SepalLength 603 non-null float64
SepalWidth 603 non-null float64
PetalLength 603 non-null float64
PetalWidth 603 non-null float64
Name 603 non-null object
dtypes: float64(4), object(1)
memory usage: 25.9+ KB
然而,使用to_string将以字符串的形式显示DataFrame,虽然它并不总是适合于控制台的宽度
In [116]: print(iris.iloc[-20:, ].to_string())
SepalLength SepalWidth PetalLength PetalWidth Name
583 5.3 3.6 1.4 0.2 Iris-setosa
584 5.1 3.0 1.4 0.2 Iris-setosa
585 4.9 3.2 1.4 0.2 Iris-setosa
586 4.7 3.1 1.4 0.5 Iris-setosa
587 4.9 3.6 1.3 0.2 Iris-setosa
588 4.7 3.5 1.5 0.3 Iris-setosa
589 4.6 3.0 1.4 0.2 Iris-setosa
590 5.3 3.6 1.3 0.2 Iris-setosa
591 5.3 3.1 1.8 0.2 Iris-setosa
592 5.3 3.0 1.4 0.7 Iris-setosa
593 5.1 3.2 1.4 0.2 Iris-setosa
594 4.9 3.1 1.4 0.2 Iris-setosa
595 4.7 3.6 1.3 0.2 Iris-setosa
596 5.1 3.1 1.5 0.2 Iris-setosa
597 4.9 3.6 1.4 0.3 Iris-setosa
598 5.2 3.5 1.4 0.2 Iris-setosa
599 4.9 3.0 1.6 0.2 Iris-setosa
600 5.4 3.2 1.4 0.2 Iris-setosa
601 4.6 3.1 1.3 0.7 Iris-setosa
602 5.3 3.6 1.8 0.2 Iris-setosa
如果DataFrame有10列,他们超过控制台宽度的列默认将隔行显示:
In [117]: pd.DataFrame(np.random.randn(3, 12))
Out[117]:
0 1 2 3 4 5 6 \
0 -0.119700 0.086073 -1.741760 0.115469 -0.264237 -0.191981 -1.666531
1 -0.650737 0.689219 -1.286211 0.165378 1.189919 0.871147 -0.109856
2 0.507395 0.339061 -1.119778 1.177230 0.046027 0.339424 1.027529
7 8 9 10 11
0 0.156251 -0.839636 1.287716 0.354855 -0.288980
1 0.824025 0.533401 -0.438582 -0.217816 -1.026814
2 -0.302536 1.702276 0.560161 -1.123736 -0.498265
通过设置display.with选项,可以改变控制台每一行显示的列个数
In [118]: pd.set_option('display.width', 40) #默认为80
In [119]: pd.DataFrame(np.random.randn(3, 12))
Out[119]:
0 1 2 \
0 1.217084 -0.558679 -1.257722
1 -0.214153 1.239619 0.903324
2 -0.290766 0.698407 0.009694
3 4 5 \
0 0.308694 -0.057101 0.572316
1 0.475061 -1.139262 2.080925
2 1.201881 -0.486657 -1.642377
6 7 8 \
0 -0.547218 -1.311481 0.778549
1 -0.633969 0.764801 -1.325743
2 0.854663 1.932424 -0.146767
9 10 11
0 0.613871 1.603854 -0.992278
1 -0.848804 0.358653 -0.689318
2 0.204168 1.043873 -0.345096
可以通过设置display.max_colwidth来调整各个列的最大宽度
In [120]: datafile={'filename': ['filename_01','filename_02'],
...: .....: 'path': ["media/user_name/storage/folder_01/filename_01",
...: .....: "media/user_name/storage/folder_02/filename_02"]}
In [121]: pd.set_option('display.max_colwidth', 30)
In [122]: pd.DataFrame(datafile)
Out[122]:
filename \
0 filename_01
1 filename_02
path
0 media/user_name/storage/fo...
1 media/user_name/storage/fo...
In [123]: pd.set_option('display.max_colwidth', 100)
In [124]: pd.DataFrame(datafile)
Out[124]:
filename \
0 filename_01
1 filename_02
path
0 media/user_name/storage/folder_01/filename_01
1 media/user_name/storage/folder_02/filename_02
还可以通过扩展framerepr选项禁用此功能。这将在一个块中打印表。
DataFrame列属性访问
如果DataFrame的列标签是一个Python的变量名,那么列可以像属性一样访问:
In [125]: df = pd.DataFrame({'foo1': np.random.randn(5),
...: 'foo2': np.random.randn(5)})
In [126]: df
Out[126]:
foo1 foo2
0 1.095587 -0.135147
1 1.049579 -0.117808
2 1.029435 1.438757
3 0.177883 0.123195
4 -0.964396 -2.319625
In [127]: df.foo1
Out[127]:
0 1.095587
1 1.049579
2 1.029435
3 0.177883
4 -0.964396
Name: foo1, dtype: float64
Panel
Panel很少用到,但它仍然是三维数据的重要容器。术语panel data来源于计量经济学,它对pandas的命名有部分借鉴意义:pan(el)-da(ta)-s。三个轴的名称旨在为描述panel data的操作提供一些语义上的描述,尤其是,panel data的计量经济分析。但是,为了严格的分割DataFrame对象的集合,你可能会发现这些轴的名称有些随意:
- items: 轴0, 每个item相当于一个DataFrame包含在其中
- major_axis: 轴1,它是每个DataFrame的索引(行)
- minor_axis: 轴2, 它是每个DataFrame的列
Panel的构造方式和你想的一样:
从带有可选轴标签的3D ndarray构造
In [135]: wp = pd.Panel(np.random.randn(2, 5, 4), items=['item1', 'item2'],
...: major_axis=pd.date_range('1/1/2000', periods=5),
...: minor_axis=['A', 'B', 'C', 'D'])
In [136]: wp
Out[136]:
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: item1 to item2
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
Minor_axis axis: A to D
从包含DataFrame的字典类型构造
In [137]: data = {'Item1': pd.DataFrame(np.random.randn(4, 3)),
...: 'Item2': pd.DataFrame(np.random.randn(4, 2))}
In [138]: pd.Panel(data)
Out[138]:
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 4 (major_axis) x 3 (minor_axis)
Items axis: Item1 to Item2
Major_axis axis: 0 to 3
Minor_axis axis: 0 to 2
注意,字典中的Value只需要可以被转换为DataFrame。因此,他们可以是DataFrame的任何其他有效输入。
一个有效的工厂方法是Panel.from_dict,它使用了上面的DataFrame字典,以及下面的命名参数:
参数名 | 默认值 | 描述 |
---|---|---|
intersect | False | 删除索引没有对其的元素 |
orient | items | 使用minor来让DataFrame的列作为panel的item |
例如,与前面的构造方式比较:
In [139]: pd.Panel.from_dict(data, orient='minor')
Out[139]:
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 4 (major_axis) x 2 (minor_axis)
Items axis: 0 to 2
Major_axis axis: 0 to 3
Minor_axis axis: Item1 to Item2
orient对于混合类型的DataFrame尤其有帮助。如果你传递了一个含有混合类型列DataFrame的字典对象,所有的数据都将被显示为dtype=object,除非你将设置orient=’minor’:
In [140]: df = pd.DataFrame({'a': ['foo', 'bar', 'baz'],
...: 'b': np.random.randn(3)})
In [141]: df
Out[141]:
a b
0 foo 0.075889
1 bar -0.450220
2 baz 1.570256
In [142]: data = {'item1': df, 'item2': df}
In [143]: panel = pd.Panel.from_dict(data, orient='minor')
In [144]: panel['a']
Out[144]:
item1 item2
0 foo foo
1 bar bar
2 baz baz
In [145]: panel['b']
Out[145]:
item1 item2
0 0.075889 0.075889
1 -0.450220 -0.450220
2 1.570256 1.570256
In [146]: panel['b'].dtypes
Out[146]:
item1 float64
item2 float64
dtype: object
注意:不幸的是,panel相较于Series和DataFrame更少被使用,因为它在功能的完整性上被忽视了。DataFrame中的许多有效的方法和选项在panel中都不能使用。当然,这将在未来的版本中得到解决。
使用to_panel方法从DataFrame构造
这个方法是在v0.7中引进的,用于取代LongPanel.to_long,将一个带有双级索引的DataFrame转换为一个Panel。
In [149]: midx = pd.MultiIndex(levels=[['one', 'two'], ['x', 'y']], labels=[[1, 1, 0, 0], [1, 0, 1, 0]])
In [150]: df = pd.DataFrame({'A': [1, 2, 3, 4], 'B': [5, 6, 7, 8]}, index=midx)
In [151]: df
Out[151]:
A B
two y 1 5
x 2 6
one y 3 7
x 4 8
In [152]: df.to_panel()
Out[152]:
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 2 (major_axis) x 2 (minor_axis)
Items axis: A to B
Major_axis axis: one to two
Minor_axis axis: x to y
Item的选择/添加/删除
正如DataFrame的操作类似于含有字典类型的Series, 而Penel则类似于含有字典类型的DataFrame:
In [156]: wp['item1']
Out[156]:
A B C D
2000-01-01 -1.459119 -0.167279 -0.105919 -1.589155
2000-01-02 -1.224224 -0.500770 -0.169742 0.247558
2000-01-03 -1.064092 -1.832947 0.094414 -0.445864
2000-01-04 -0.092463 -1.629868 0.183957 -0.702327
2000-01-05 1.086481 -2.833691 0.010039 -0.101303
In [157]: wp['item3'] = wp['item1'] / wp['item2']
In [158]: wp
Out[158]:
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: item1 to item3
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
Minor_axis axis: A to D
插入和删除的API和DataFrame相同。同DataFrame一样,如果item是一个有效的标识符,那么可以将它作为属性来访问。
转换
Panel可以通过transpose方法来进行重新排列(除非数据是异构的,否则默认情况下不会生成副本)
In [160]: wp
Out[160]:
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: item1 to item3
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
Minor_axis axis: A to D
In [161]: wp.transpose(2, 0, 1)
Out[161]:
<class 'pandas.core.panel.Panel'>
Dimensions: 4 (items) x 3 (major_axis) x 5 (minor_axis)
Items axis: A to D
Major_axis axis: item1 to item3
Minor_axis axis: 2000-01-01 00:00:00 to 2000-01-05 00:00:00
索引和选择
操作 | 语法 | 结果 |
---|---|---|
选择item | wp[item] | DataFrame |
在major_axis标签上切片 | wp.major_xs(val) | DataFrame |
在minor_axis标签上切片 | wp.minor_xs(val) | DataFrame |
例如,使用前面的数据,我们可以这样做:
In [164]: wp['item1']
Out[164]:
A B C D
2000-01-01 -1.459119 -0.167279 -0.105919 -1.589155
2000-01-02 -1.224224 -0.500770 -0.169742 0.247558
2000-01-03 -1.064092 -1.832947 0.094414 -0.445864
2000-01-04 -0.092463 -1.629868 0.183957 -0.702327
2000-01-05 1.086481 -2.833691 0.010039 -0.101303
In [165]: wp.major_xs(wp.major_axis[2])
Out[165]:
item1 item2 item3
A -1.064092 0.813871 -1.307446
B -1.832947 -2.016451 0.908996
C 0.094414 -0.456631 -0.206762
D -0.445864 -0.417404 1.068182
In [166]: wp.minor_axis
Out[166]: Index([u'A', u'B', u'C', u'D'], dtype='object')
In [167]: wp.minor_xs('C')
Out[167]:
item1 item2 item3
2000-01-01 -0.105919 1.211176 -0.087451
2000-01-02 -0.169742 1.823682 -0.093077
2000-01-03 0.094414 -0.456631 -0.206762
2000-01-04 0.183957 0.585720 0.314070
2000-01-05 0.010039 0.622204 0.016134
压缩
另一种改变对象维数的方法是squeeze 一个一维的对象,类似于wp[‘item1’]
In [168]: wp.reindex(items=['item1']).squeeze()
Out[168]:
A B C D
2000-01-01 -1.459119 -0.167279 -0.105919 -1.589155
2000-01-02 -1.224224 -0.500770 -0.169742 0.247558
2000-01-03 -1.064092 -1.832947 0.094414 -0.445864
2000-01-04 -0.092463 -1.629868 0.183957 -0.702327
2000-01-05 1.086481 -2.833691 0.010039 -0.101303
In [169]: wp.reindex(items=['item1'], minor=['B']).squeeze()
Out[169]:
2000-01-01 -0.167279
2000-01-02 -0.500770
2000-01-03 -1.832947
2000-01-04 -1.629868
2000-01-05 -2.833691
Freq: D, Name: B, dtype: float64
转换为DataFrame
一个Panel可以用2D的形式表示一个分层索引的DataFrame.要将Panel对象转换为DataFrame,可以使用toframe方法:
In [171]: panel = pd.Panel(np.random.randn(3, 5, 4), items=['one', 'two', 'three'],
...: major_axis=pd.date_range('1/1/2000', periods=5),
...: minor_axis=['a', 'b', 'c', 'd'])
In [172]: panel.to_frame()
Out[172]:
one two three
major minor
2000-01-01 a -0.058576 -0.026163 0.289173
b 1.333492 1.514565 -0.233190
c -0.817197 2.144006 -0.126894
d -1.205514 -0.572632 -0.309679
2000-01-02 a 0.230310 2.712063 -0.311404
b -0.070901 -1.373851 1.284403
c 0.928690 2.084200 -0.151439
d 1.326599 -0.571143 -0.242354
2000-01-03 a -0.684355 1.875092 0.692001
b 1.542806 -1.416353 -0.555132
c 1.048785 0.873523 -0.186362
d 1.457121 -1.266636 -0.935350
2000-01-04 a -0.821748 -0.900330 -1.458795
b 0.964931 0.119338 -0.534120
c -0.778703 0.470976 0.913389
d -0.078422 0.308533 -0.128064
2000-01-05 a -0.075534 0.700830 0.292943
b 0.291545 0.636898 -0.927162
c 1.103668 -0.697442 -1.354032
d -1.209118 0.258653 -0.119056