为ggplot/rpy2处理独特索引的pandas数据框的重塑
这个问题和在Python的pandas中对数据框进行拆分和合并,以便使用rpy2进行绘图有关。我正在处理pandas数据框,并进行各种“融化”(melting)和“解融”(unmelting)操作,以便能够用ggplot2进行绘图。我对如何在具有唯一索引的数据框上执行这些操作有点困惑。假设数据框df
有一个唯一的列runner_id
,它记录了每个跑步者在两场比赛(比赛A
和B
)中完成的时间和速度。每个跑步者都是独一无二的,因此数据框对于两个跑步者bob
和mary
可以是这样的:
df = pandas.DataFrame([{"runner_id": "bob", "time_A": 30,
"time_B": 25, "speed_A": 5, "speed_B": 10},
{"runner_id": "mary", "time_A": 29,
"time_B": 19, "speed_A": 8, "speed_B": 12}])
df
看起来是这样的:
runner_id speed_A speed_B time_A time_B
0 bob 5 10 30 25
1 mary 8 12 29 19
由于跑步者是独特的,因此用runner_id
来索引数据框非常方便。这也可以防止意外添加重复的条目,因为我们知道每个跑步者的信息应该保留在该跑步者的行中,不能有多个行对应同一个跑步者:
df = df.set_index("runner_id")
问题是,如果我们想要绘制两场比赛之间的时间或速度差异,ggplot需要使用列名time_A, time_B, speed_A, speed_B
中的信息。那么df
就需要看起来像这样:
runner_id race time speed
bob A ... ...
mary A
bob B
mary B
这样我们就可以进行:
ggplot2.ggplot(df) + \
ggplot2.geom_point(aes_string(x="time", y="speed", colour="race")) ...
虽然这违反了runner_id
条目的唯一性,因为跑步者需要被重复。一般来说,应该如何处理这个问题?有没有一种好的方式来保持df
,既能保持唯一索引,又能方便ggplot的融化表示?我发现在这两者之间来回转换非常困难和混乱。第一种表示方式是每场比赛有不同的时间/速度列,并以跑步者为索引,这种方式非常直观,而ggplot的融化表示则令人困惑,似乎有些浪费。
关于在这两者之间转换的任何想法,或者关于如何保持数据框的一般规则都会很有帮助。在使用ggplot时,答案是否是不进行索引(不调用set_index
)?对于这种类型的数据框,有没有推荐的格式?
一个潜在的解决方案是在进行解融/融化时始终对df
进行索引/取消索引,比如:
melted_df = pandas.melt(df.reset_index(), id_vars="runner_id")
但这似乎容易出错。例如,如果我想计算每个跑步者在A
比赛中的速度和时间的平均值,我可以尝试输出A
的条目:
# This is already complicated
a_entries = melted_df[map(lambda x: x.endswith("_A"), melted_df["variable"])]
我现在有了冗余的/融化的表示,所以很难进行不重复计算跑步者的操作,因为每个跑步者现在出现了两次:
runner_id variable value
0 bob speed_A 5
1 mary speed_A 8
4 bob time_A 30
5 mary time_A 29
2 个回答
这是对一个长问题的简短而迟来的回答:看起来你需要一些帮助来理解长格式的数据框。每个值都是独一无二的,因为每场比赛中只有一个特定名字的“跑者”。刚开始接触的时候可能会让你感到困惑,但这实际上非常强大,并且对于充分利用ggplot2的功能来说是必不可少的。Hadley Wickham在几篇文章中对此解释得很好,比如这篇:http://had.co.nz/reshape/paper-dsc2005.pdf
在R语言中,融化和铸造数据框是一个常见的操作。Hadley开发的reshape
包(还有reshape2
,原来的melt()
函数就在这里)之所以受欢迎,是有原因的。
使用ggplot2,你还可以以层的方式将数据添加到图表中。以你的例子为例:
import rpy2.robjects.pandas2ri
rpy2.robjects.pandas2ri.activate()
p = ggplot2.ggplot(rpy2.robjects.conversion.py2ri(df)) + \
ggplot2.geom_point(ggplot2.aes_string(x="time_A",y="speed_A"),colour="#ff0000") + \
ggplot2.geom_point(ggplot2.aes_string(x="time_B",y="speed_B"),colour="#0000ff") + \
ggplot2.scale_x_continuous("time") + \
ggplot2.scale_y_continuous("speed")
p.plot()