根据另一列的掩码修改 Pandas 数据框列的符号?
Modify sign of Pandas dataframe's column based off another column's mask?
我正在处理从 NASA 的火球数据 API (https://cneos.jpl.nasa.gov/fireballs/)
中导入的 lat/lon 数据
lat
/lon
数据只有正值
- 它们的方向(N/S 和 E/W)在不同的列中,称为
lat-dir
/lon-dir
- 数据框如下。
- 现在我想:
- 如果 "lat-dir" == 'S'
,则将任何 lat
值转换为负数(乘以 -1)
- 如果 "lon-dir" == 'W'
,则将 lon
值转换为负数
下面是我创建数据框的大致方式:
import requests
import pandas as pd
response = requests.get('https://ssd-api.jpl.nasa.gov/fireball.api')
j = response.json()
df = pd.DataFrame.from_dict(j[u'data'])
print( j[u'fields'] )
[u'date', u'energy', u'impact-e', u'lat', u'lat-dir', u'lon', u'lon-dir', u'alt', u'vel']
print( df.head() )
0 1 2 3 4 5 6 7 8
0 2019-12-06 10:19:57 4.6 0.15 3.3 S 37.7 W 19.5 None
1 2019-12-03 06:46:27 4.2 0.14 5.6 N 52.2 W 61.5 None
2 2019-11-28 20:30:54 2.7 0.095 35.7 N 31.7 W 35 13.0
3 2019-11-28 13:22:10 2.6 0.092 None None None None None None
4 2019-11-28 11:55:02 2.5 0.089 22.1 S 25.7 E 22.5 24.7
我尝试过的代码行数:
尝试使用 df.apply()
- 尽管通过我的搜索,我认为您不能以这种方式轻松引用两列...
df['lat'] = df['lat'].apply(lambda x: x * -1 if (df['lat-dir'][x] == 'S'))
for i, row in df.iterrows():
if (row['lat-dir'] == 'S'):
df['lat'][i].apply(lambda x: x*-1)
为此,我得到 'numpy.float64' object has no attribute 'apply' ?
尝试使用掩码:
if( df['lon-dir'] == 'W'):
df['lon'] * -1
但坦率地说,关于敷面膜,我不知道下一步该怎么做。
编辑:
dfDate['lat'] = dfDate['lat'].apply(lambda row: row['lon'] * -1 , axis = 1 )
根据评论也尝试了此操作。
是,通过以下任一方式:
A) 使用矢量化掩码。 ==
未矢量化; .eq(...)
是。对于矢量化表达式,请使用 dfDate['lon-dir'].eq('W')
。然后否定这些行上的 'lon' 列。
B) 使用apply()
行:dfDate['lon'] = dfDate.apply(lambda row: ..., axis=1)
- 并在您的 lambda 中根据值 row['lon-dir']
选择性地否定 row['lon']
- 您的 apply
调用失败的原因是您需要应用到整个 column/Series,而不是单个条目。所以:df['lat'].apply(lambda: ..., axis=1)
lat-dir
/lon-dir
本质上是符号列,读入时可以将它们转换为+1/-1。
代码:
首先,您要修复的一些代码问题:
- 不要使用 u'...' 表示法。假设您正在使用 Python 3.x,不需要 u'...',现在 3.x 中的文本默认为 unicode。如果你不使用 Python 3.x,你真的应该现在切换,2.x 将于 2020 年 1 月 1 日停用。
- 将 JSON 列名传递到数据框上,让您的生活更轻松:
df.columns = j['fields']
- 通过将
response.json()
传递到 pd.DataFrame.from_dict()
来读取 JSON 是一种痛苦;您的数据框列变为 string/'object' 而不是将浮点数转换为浮点数。出于这个和其他方便的原因,理想情况下我们应该使用 pandas.read_json(..., dtype=...)
。
- 您将要转换数字列上的 dtypes(例如字符串 -> 浮点数),这也会自动转换 Python
None
-> pandas/numpy nan
(为了向量化代码,我们将优雅地编写处理 nan
而不是不断抛出烦人的 TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
)。您可以[使用 astype(...)
、pd.to_numeric()
或 df.fillna(value=pd.np.nan, inplace=True)
- 真的,由于下面列出的多种原因,这些 nan 条目会一直很痛苦(例如,整数不断被强制恢复为浮点数),所以 你可能想要放弃或至少暂时忽略nan rows 通过做:
df2 = df.dropna(how='any', inplace=False)
# 可能与 ..., inplace=True
无关。请注意,这会保留行索引,因此您始终可以在末尾将 prpcessing df2 的结果插入回 df 中。阅读 dropna
文档并找出您要删除 nan 的确切位置。
- 请注意,'vel' 列实际上还有其他我们想要忽略的 nan,您需要弄清楚,或者暂时忽略它们:例如做
df2 = df[['date','energy','impact-e','lat','lat-dir','lon','lon-dir']].dropna(how='any', inplace=False)
解决方案
将 lat/lon-dir 列转换为 +/-1 符号的几种方法:
A1) 如果你想要 'correct',nan 感知方式,它不会阻塞 nans...
df2['lat'] = df2['lat-dir'].map({'N': +1, 'S': -1})
df2['lon'] = df2['lon-dir'].map({'E': +1, 'W': -1})
A2) ...or a fast-and-dirty way:
(-1) ** df2['lat-dir'].eq('S')
(-1) ** df2['lon-dir'].eq('W')
B) 但你可以在一行中完成所有这些 apply()
函数:
def fixup_latlon_signs(row):
row['lat'] = row['lat'] * (-1) ** (row['lat-dir'] == 'S')
row['lon'] = row['lon'] * (-1) ** (row['lon-dir'] == 'W')
return row
df2.apply(fixup_latlon_signs, axis=1)
# Then insert the non-NA rows we processed back into the parent dataframe:
df.update(df2)
# Strictly we can drop 'lat-dir','lon-dir' now...
我正在处理从 NASA 的火球数据 API (https://cneos.jpl.nasa.gov/fireballs/)
中导入的 lat/lon 数据lat
/lon
数据只有正值- 它们的方向(N/S 和 E/W)在不同的列中,称为
lat-dir
/lon-dir
- 数据框如下。
- 现在我想:
- 如果 "lat-dir" == 'S' ,则将任何
- 如果 "lon-dir" == 'W' ,则将
lat
值转换为负数(乘以 -1)
lon
值转换为负数
下面是我创建数据框的大致方式:
import requests
import pandas as pd
response = requests.get('https://ssd-api.jpl.nasa.gov/fireball.api')
j = response.json()
df = pd.DataFrame.from_dict(j[u'data'])
print( j[u'fields'] )
[u'date', u'energy', u'impact-e', u'lat', u'lat-dir', u'lon', u'lon-dir', u'alt', u'vel']
print( df.head() )
0 1 2 3 4 5 6 7 8
0 2019-12-06 10:19:57 4.6 0.15 3.3 S 37.7 W 19.5 None
1 2019-12-03 06:46:27 4.2 0.14 5.6 N 52.2 W 61.5 None
2 2019-11-28 20:30:54 2.7 0.095 35.7 N 31.7 W 35 13.0
3 2019-11-28 13:22:10 2.6 0.092 None None None None None None
4 2019-11-28 11:55:02 2.5 0.089 22.1 S 25.7 E 22.5 24.7
我尝试过的代码行数:
尝试使用 df.apply()
- 尽管通过我的搜索,我认为您不能以这种方式轻松引用两列...
df['lat'] = df['lat'].apply(lambda x: x * -1 if (df['lat-dir'][x] == 'S'))
for i, row in df.iterrows():
if (row['lat-dir'] == 'S'):
df['lat'][i].apply(lambda x: x*-1)
为此,我得到 'numpy.float64' object has no attribute 'apply' ?
尝试使用掩码:
if( df['lon-dir'] == 'W'):
df['lon'] * -1
但坦率地说,关于敷面膜,我不知道下一步该怎么做。
编辑:
dfDate['lat'] = dfDate['lat'].apply(lambda row: row['lon'] * -1 , axis = 1 )
根据评论也尝试了此操作。
是,通过以下任一方式:
A) 使用矢量化掩码。 ==
未矢量化; .eq(...)
是。对于矢量化表达式,请使用 dfDate['lon-dir'].eq('W')
。然后否定这些行上的 'lon' 列。
B) 使用apply()
行:dfDate['lon'] = dfDate.apply(lambda row: ..., axis=1)
- 并在您的 lambda 中根据值 row['lon-dir']
选择性地否定 row['lon']
- 您的 apply
调用失败的原因是您需要应用到整个 column/Series,而不是单个条目。所以:df['lat'].apply(lambda: ..., axis=1)
lat-dir
/lon-dir
本质上是符号列,读入时可以将它们转换为+1/-1。
代码:
首先,您要修复的一些代码问题:
- 不要使用 u'...' 表示法。假设您正在使用 Python 3.x,不需要 u'...',现在 3.x 中的文本默认为 unicode。如果你不使用 Python 3.x,你真的应该现在切换,2.x 将于 2020 年 1 月 1 日停用。
- 将 JSON 列名传递到数据框上,让您的生活更轻松:
df.columns = j['fields']
- 通过将
response.json()
传递到pd.DataFrame.from_dict()
来读取 JSON 是一种痛苦;您的数据框列变为 string/'object' 而不是将浮点数转换为浮点数。出于这个和其他方便的原因,理想情况下我们应该使用pandas.read_json(..., dtype=...)
。 - 您将要转换数字列上的 dtypes(例如字符串 -> 浮点数),这也会自动转换 Python
None
-> pandas/numpynan
(为了向量化代码,我们将优雅地编写处理nan
而不是不断抛出烦人的TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
)。您可以[使用astype(...)
、pd.to_numeric()
或df.fillna(value=pd.np.nan, inplace=True)
- 真的,由于下面列出的多种原因,这些 nan 条目会一直很痛苦(例如,整数不断被强制恢复为浮点数),所以 你可能想要放弃或至少暂时忽略nan rows 通过做:
df2 = df.dropna(how='any', inplace=False)
# 可能与..., inplace=True
无关。请注意,这会保留行索引,因此您始终可以在末尾将 prpcessing df2 的结果插入回 df 中。阅读dropna
文档并找出您要删除 nan 的确切位置。- 请注意,'vel' 列实际上还有其他我们想要忽略的 nan,您需要弄清楚,或者暂时忽略它们:例如做
df2 = df[['date','energy','impact-e','lat','lat-dir','lon','lon-dir']].dropna(how='any', inplace=False)
解决方案
将 lat/lon-dir 列转换为 +/-1 符号的几种方法:
A1) 如果你想要 'correct',nan 感知方式,它不会阻塞 nans...
df2['lat'] = df2['lat-dir'].map({'N': +1, 'S': -1})
df2['lon'] = df2['lon-dir'].map({'E': +1, 'W': -1})
A2) ...or a fast-and-dirty way:
(-1) ** df2['lat-dir'].eq('S')
(-1) ** df2['lon-dir'].eq('W')
B) 但你可以在一行中完成所有这些 apply()
函数:
def fixup_latlon_signs(row):
row['lat'] = row['lat'] * (-1) ** (row['lat-dir'] == 'S')
row['lon'] = row['lon'] * (-1) ** (row['lon-dir'] == 'W')
return row
df2.apply(fixup_latlon_signs, axis=1)
# Then insert the non-NA rows we processed back into the parent dataframe:
df.update(df2)
# Strictly we can drop 'lat-dir','lon-dir' now...