简体   繁体   English

R,ggplot2:如何通过固定坐标的plot贝塞尔曲线?

[英]R, ggplot2: How to plot bezier curves that pass through fixed coordinates?

I am helping someone translate hand-drawn economics supply and demand functions into image files that can be included in a Word document.我正在帮助某人将手绘的经济学供求函数翻译成可以包含在 Word 文档中的图像文件。 These have been going well using Hmisc::bezier and geom_path modeled after Andrew Heiss's recon plots and using his curve_intersect function.使用 Hmisc::bezier 和 geom_path 以 Andrew Heiss 的侦察图为模型并使用他的 curve_intersect function,这些进展顺利。 That is, until the author asked that one of the supply curves should pass through a specified set of coordinates.也就是说,直到作者要求其中一条供给曲线应该通过一组指定的坐标。 The Hmisc::bezier function only uses the first and last control point as absolute, and bends toward intermediate points so the specified intersection point does not match the curve. Hmisc::bezier function 仅使用第一个和最后一个控制点作为绝对点,并向中间点弯曲,因此指定的交点与曲线不匹配。 I tried creating a spline of 2 bezier curves with the bezier function from the bezier package (v1.1.2, https://cran.r-project.org/web/packages/bezier/bezier.pdf ), but this fails with "Error in FUN(X[[i]], ...): object 'x' not found", which I do not understand or know how to fix. I tried creating a spline of 2 bezier curves with the bezier function from the bezier package (v1.1.2, https://cran.r-project.org/web/packages/bezier/bezier.pdf ), but this fails with " FUN(X[[i]], ...) 中的错误:object 'x' not found”,我不明白或不知道如何解决。

Please let me know where I am going wrong or if there is a better method.请让我知道我哪里出错了,或者是否有更好的方法。 I will include the commented out attempts using various functions, Please excuse the amateurish code.我将使用各种功能包括注释掉的尝试,请原谅业余代码。 as I am a relative newb at R and ggplot2.因为我是 R 和 ggplot2 的相对新手。

This section not directly relevant to my question本节与我的问题没有直接关系

# Graph figures for physical economics, negative oil prices paper

library(reconPlots)
library(dplyr)
library(ggplot2)
library(patchwork)
library(ggrepel)
library(bezier)
library(ggforce)

options(ggrepel.max.time = 1)
options(ggrepel.max.iter = 20000)

#Set seed value for ggrepel
set.seed(52)

# panel (a) 

#Set values of curves using the bezier function, each pair of c() values
# is an xy coordinate, and the sets of coordinates control the shape of the
# curve
supply <- Hmisc::bezier(c(1, 5, 6), c(3, 4, 9)) %>%
  as_data_frame()

demand <- Hmisc::bezier(c(0, 9, 9), c(6, 6, 6)) %>%
  as_data_frame()

label_height <- Hmisc::bezier(c(0, 9, 9), c(8, 8, 8)) %>%
  as_data_frame()

# Calculate the intersections of the two curves
intersections <- bind_rows(curve_intersect(supply, demand))

# Calculate point where the curve label(s) intersect a specified height
supply_label <- bind_rows(curve_intersect(supply, label_height))

labels <- data_frame(label = expression("PS"[CR]^DRL),
                     x = supply_label$x,
                     y = supply_label$y)                      

production <- ggplot(mapping = aes(x = x, y = y)) + 
  #Draw the supply curve. Demand is not drawn in this figure, but the
  # intersections of an imaginary demand curve are used to illustrate P0
  # and Q0, the intersection point, and the dotted lines
  geom_path(data = supply, color = "#0073D9", size = 1) + 
  geom_segment(data = intersections, 
               aes(x = x, y = 0, xend = x, yend = y), lty = "dotted") +
  geom_segment(data = intersections, 
               aes(x = 0, y = y, xend = x, yend = y), lty = "dotted") + 
  #Draw the supply curve label using the intersection calculated above, using
  # GGrepel so that the labels do not overlap the curve line
  geom_text_repel(data = labels
                  ,aes(x = x, y = y, label = label) 
                  ,parse = TRUE
                  ,direction = "x"
                  ,force = 3
                  ,force_pull = 0.1
                  ,hjust = 0
                  ,min.segment.length = 0
  ) +
  #Draw the intersection point based on intersection function between supply
  # and the phantom flat demand curve at height y=6
  geom_point(data = intersections, size = 3) +
  #Use scale functions to set y-axis label, axis intersection point labels,
  # and limits of the viewing area
  scale_x_continuous(expand = c(0, 0), breaks = intersections$x
                     ,labels = expression(Q[CR]^{DRL-PS})
                     ,limits=c(0,9)
  ) +
  scale_y_continuous(expand = c(0, 0), breaks = c(intersections$y, 9)
                     ,labels = c(expression(P[CR]==frac("$",brl))
                                 ,expression(P[CR]))
                     ,limits=c(0,9)
  ) +
  #Use labs function to set x-axis title and title of each graph using the
  # caption function so that it displays on the bottom
  labs(x = expression(frac(Barrels,Week)),
       caption = expression(atop("(a) Driller Production Supply", "of Crude Oil"))
  ) +
  #Set classic theme, x-axis title on right-hand side using larger font of
  # relative size 1.2, graph title on left-hand side using same larger font
  theme_classic() + 
  theme(axis.title.y = element_blank(),
        axis.title.x = element_text(hjust = 1), 
        axis.text = element_text(size=rel(1.2)),
        plot.caption = element_text(hjust = 0.5, size=rel(1.2))
  ) + 
  coord_equal()

# Save the intersections so we can set the same quantity, price for panel (c)
specified_intersections = intersections

# Panel (b)
supply <- Hmisc::bezier(c(3.99, 4), c(0, 9)) %>%
  as_data_frame()

demand <- Hmisc::bezier(c(2, 3, 4, 5), c(9, 6.5, 6, 5.5)) %>%
  as_data_frame()

demand_capacity <- Hmisc::bezier(c(5, 5), c(0, 5.5)) %>%
  as_data_frame()

supply_capacity <- Hmisc::bezier(c(4.999, 5), c(0, 9)) %>%
  as_data_frame()

supply_label_height <- Hmisc::bezier(c(0, 9), c(9, 9)) %>%
  as_data_frame()

demand_label_height <- Hmisc::bezier(c(0, 9), c(8, 8)) %>%
  as_data_frame()

capacity_label_height <- Hmisc::bezier(c(0, 9), c(9, 9)) %>%
  as_data_frame()

# Calculate the intersections of the two curves
intersections <- bind_rows(curve_intersect(supply, 
                                           demand))

supply_label <- bind_rows(curve_intersect(supply 
                                          ,supply_label_height))
demand_label <- bind_rows(curve_intersect(demand 
                                          ,demand_label_height))
capacity_label <- bind_rows(curve_intersect(supply_capacity 
                                            ,capacity_label_height))

labels <- data_frame(label = c(expression("OD"[CR]^DRL),expression("OS"[CR]^DRL)
                               ,expression("Q"[CR]^CAP)
),
x = c(demand_label$x, supply_label$x
      , capacity_label$x
),
y = c(demand_label$y, supply_label$y
      , capacity_label$y
)
) 

inventory <- ggplot(mapping = aes(x = x, y = y)) + 
  geom_path(data = supply, color = "#0073D9", size = 1) + 
  geom_path(data = demand, color = "#FF4036", size = 1) +
  geom_path(data = demand_capacity, color = "#FF4036", size = 1) +
  geom_path(data = supply_capacity, color = "#0073D9", size = 1, lty = "dashed") +
  geom_segment(data = intersections, 
               aes(x = 0, y = y, xend = x, yend = y), lty = "dotted") + 
  geom_text_repel(data = labels
                  ,aes(x = x, y = y, label = label) 
                  ,parse = TRUE
                  ,direction = "x"
                  ,force = 3
                  ,force_pull = 0.1
                  ,hjust = c(0, 0, 1)
                  ,min.segment.length = 0
  ) +
  geom_point(data = intersections, size = 3) +
  scale_x_continuous(expand = c(0, 0), breaks = c(intersections$x
                                                  , 5),
                     labels = c(expression(paste(Q[CR]^{DRL-OS},phantom(12345)))
                                ,expression(Q[CR]^CAP)
                     )
                     , limits=c(0,9)) +
  scale_y_continuous(expand = c(0, 0), breaks = c(intersections$y, 9),
                     labels = c(expression(P[CR]),expression(P[CR]))
                     , limits=c(0,9)) +
  labs(x = "Barrels",
       caption = expression(atop("(b) Driller Storage / Ownership", "of Crude Oil"))
  ) +
  theme_classic() + 
  theme(axis.title.y = element_blank(),
        axis.title.x = element_text(hjust = 1), 
        axis.text = element_text(size=rel(1.2)),
        plot.caption = element_text(hjust = 0.5, size=rel(1.2))
  ) + 
  coord_equal()  

Relevant section相关部分


# panel (c)

# ggforce package method
#supply <- list(c(1, 4, specified_intersections$x, 5, 7),
#                        c(3, 4, specified_intersections$y, 7, 9)) %>%
#  as_data_frame()

# bezier package method: Fails with "Error in FUN(X[[i]], ...) : object 'x' not found"
t <- seq(0, 2, length=10)
p <- list(c(1, 4, specified_intersections$x, 7, 8), 
          c(3, 4, specified_intersections$y, 6, 9))
#p <- matrix(c(1,3, 4,4, specified_intersections$x,specified_intersections$y,
#              7,6, 8,9), nrow=5, ncol=2, byrow=TRUE)
supply <- bezier(t=t, p=p) %>%
  as_data_frame()

# Original: Fails because it does not pass through the specified intersection
#supply <- Hmisc::bezier(c(1, specified_intersections$x, 8), 
#                        c(3, specified_intersections$y, 9)) %>%
#  as_data_frame()

# Hmisc method: Fails because there is no way to get the two curves to appear
# contiguous
#supply1 <- Hmisc::bezier(c(1, 4, specified_intersections$x), 
#                         c(3, 4, specified_intersections$y)) %>%
#  as_data_frame()
#supply2 <- Hmisc::bezier(c(specified_intersections$x, 6, 7), 
#                         c(specified_intersections$y, 8, 9)) %>%
#  as_data_frame()

#demand <- Hmisc::bezier(c(0, 9), c(specified_intersections$y, specified_intersections$y)) %>%
#  as_data_frame()

label_height <- Hmisc::bezier(c(0, 9), c(8, 8)) %>%
  as_data_frame()

# Calculate the intersections of the two curves
#intersections <- bind_rows(curve_intersect(supply, demand))

#supply_label <- bind_rows(curve_intersect(supply, 
#                                          label_height))

#labels <- data_frame(label = expression("SS"[CR]^DRL),
#                     x = supply_label$x,
#                     y = supply_label$y)                      

sales <- ggplot(mapping = aes(x = x, y = y)) + 
# ggforce package method
#  geom_bspline(data = supply, color = "#0073D9", size = 1) +
  
# Original geom_path method  
  geom_path(data = supply, color = "#0073D9", size = 1) + 
# Supply 1 and 2 for Hmisc method
#  geom_path(data = supply1, color = "#0073D9", size = 1) + 
#  geom_path(data = supply2, color = "#0073D9", size = 1) + 
  geom_segment(data = specified_intersections, 
               aes(x = x, y = 0, xend = x, yend = y), lty = "dotted") +
  geom_segment(data = specified_intersections, 
               aes(x = 0, y = y, xend = x, yend = y), lty = "dotted") + 
#  geom_text_repel(data = labels
#                  ,aes(x = x, y = y, label = label) 
#                  ,parse = TRUE
#                  ,direction = "x"
#                  ,force = 3
#                  ,force_pull = 0.1
#                  ,hjust = 0
#                  ,min.segment.length = 0
#  ) +
  geom_point(data = specified_intersections, size = 3) +
  scale_x_continuous(expand = c(0, 0), breaks = specified_intersections$x,
                     labels = expression(Q[CR]^{DRL-SS}), limits=c(0,9)) +
  scale_y_continuous(expand = c(0, 0), breaks = c(specified_intersections$y, 9),
                     labels = c(expression(P[CR]),expression(P[CR]))) +
  labs(x = expression(frac(Barrels,Week)),
       caption = expression(atop("(c) Driller Sales Supply", "of Crude Oil"))
  ) +
  theme_classic() + 
  theme(axis.title.y = element_blank(),
        axis.title.x = element_text(hjust = 1), 
        axis.text = element_text(size=rel(1.2)),
        plot.caption = element_text(hjust = 0.5, size=rel(1.2))
  ) + 
  coord_equal()  

patchwork <- (production | inventory | sales)
patchwork

Graphs before implementation of fixed coordinates.实施固定坐标之前的图表。 Need to move panel (c) intersection point to match panel (a)需要移动面板 (c) 的交点以匹配面板 (a)

I solved the "Error in FUN(X[[i]], ...): object 'x' not found" by printing the supply variable and noticing that the bezier function names its rows V1,V2 and not x,y.我通过打印供应变量并注意到贝塞尔曲线 function 将其行命名为 V1、V2 而不是 x、y 解决了“FUN(X [[i]],...)中的错误:object 'x' not found”。 I needed to set the aesthetics of the geom_path to the correct mapping.我需要将 geom_path 的美学设置为正确的映射。

Relevant Section, trimmed to only the bezier method相关部分,仅修剪为贝塞尔方法

# panel (c)

# bezier package method
t <- seq(0, 2, length = 100)
p <- matrix(c(1,3, 4,4, specified_intersections$x,specified_intersections$y,
              7,6, 8,9), nrow=5, ncol=2, byrow=TRUE)
supply <- bezier::bezier(t=t, p=p, deg=2) %>%
  as_data_frame()

sales <- ggplot(mapping = aes(x = x, y = y)) + 
  
# Original geom_path method  
  geom_path(data = supply, mapping = aes(x = V1, y = V2), 
            color = "#0073D9", size = 1, inherit.aes = FALSE) + 
  geom_segment(data = specified_intersections, 
               aes(x = x, y = 0, xend = x, yend = y), lty = "dotted") +
  geom_segment(data = specified_intersections, 
               aes(x = 0, y = y, xend = x, yend = y), lty = "dotted") + 

  geom_point(data = specified_intersections, size = 3) +
  scale_x_continuous(expand = c(0, 0), breaks = specified_intersections$x,
                     labels = expression(Q[CR]^{DRL-SS}), limits=c(0,9)) +
  scale_y_continuous(expand = c(0, 0), breaks = c(specified_intersections$y, 9),
                     labels = c(expression(P[CR]),expression(P[CR]))) +
  labs(x = expression(frac(Barrels,Week)),
       caption = expression(atop("(c) Driller Sales Supply", "of Crude Oil"))
  ) +
  theme_classic() + 
  theme(axis.title.y = element_blank(),
        axis.title.x = element_text(hjust = 1), 
        axis.text = element_text(size=rel(1.2)),
        plot.caption = element_text(hjust = 0.5, size=rel(1.2))
  ) + 
  coord_equal()  

patchwork <- (production | inventory | sales)
patchwork

This does not solve my larger problem of needing a smooth curve that passes through a specified set of coordinates, as it produces two bezier curves that do not match.这并不能解决我需要一条通过指定坐标集的平滑曲线的更大问题,因为它会产生两条不匹配的贝塞尔曲线。

I will do some research on using functions to specify bezier curves and find out if there is some mathematical or programmatic way to specify a bezier curve that passes through a set of fixed coordinates.我将对使用函数指定贝塞尔曲线进行一些研究,并找出是否有一些数学或编程方式来指定通过一组固定坐标的贝塞尔曲线。 If I find one, I'll edit this answer.如果我找到一个,我会编辑这个答案。

If anyone knows how to accomplish this, I would appreciate any help!如果有人知道如何做到这一点,我将不胜感激!

Kinked bezier curves弯曲的贝塞尔曲线

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在R / ggplot2中以多面的方式绘制两条分布曲线? - How to plot two distribution curves in a faceted way in R / ggplot2? 如何在绘图下方绘制坐标 - ggplot2? - how to graph coordinates below a plot - ggplot2? 如何将 plot 图表按 ggplot2 固定刻度? - How to plot chart by ggplot2 with fixed scale? Using R ggplot2: How to induce BROKEN Y-AXIS plot using ggplot2: Y axis coordinates 0:1000 then 15000: 31000 - Using R ggplot2: How to induce BROKEN Y-AXIS plot using ggplot2: Y axis coordinates 0:1000 then 15000: 31000 如何测量R / ggplot2中2条分布曲线之间的面积 - How to measure area between 2 distribution curves in R / ggplot2 使用ggplot2绘制多条曲线 - Use ggplot2 to plot multiple curves 如何使用ggplot2在R中的散点极坐标图中获得一条垂直线 - How to get a vertical line in a scatter polar coordinates plot in R using ggplot2 如何使用R中的npc坐标在ggplot2 plot之外添加文本 - How to add text outside a ggplot2 plot using npc coordinates in R 如何使用不会重叠的ggplot2和R将一系列坐标绘制为矩形? - How to plot a series of coordinates as rectangles using ggplot2 and R that that won't overlap? 如何使用ggplot2绘制带有(x,y,r,g,b)坐标的图像? - How can I plot a image with (x,y,r,g,b) coordinates using ggplot2?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM