如何将散点图与折线图组合以显示回归线? JavaFX的

时间:2014-11-07 14:25:16

标签: java charts javafx line scatter

我创建了一个包含两组数据的散点图;第一组是实际数据(x =年和y =便士),第二组产生相同的点但是对于回归线。然而,我遇到的问题是两组数据都显示为散点。我想将第一组显示为散点,并将第二组显示在同一图表上,但显示为一条线。我已经在这里待了很长时间,但我无法找到办法。

散点图代码显示在oracle上; http://docs.oracle.com/javafx/2/charts/scatter-chart.htm

例如,我一直试图这样做:

final ScatterChart<Number,Number> sc = new
        ScatterChart<Number,Number>(xAxis,yAxis);
final LineChart<Number,Number> lc = new
        LineChart<Number,Number>(xAxis,yAxis);

XYChart.Series series1 = new XYChart.Series();
    series1.setName("Equities");
    series1.getData().add(new XYChart.Data(4.2, 193.2));
    series1.getData().add(new XYChart.Data(2.8, 33.6));

XYChart.Series series2 = new XYChart.Series();
    series2.setName("Mutual funds");
    series2.getData().add(new XYChart.Data(5.2, 229.2));
    series2.getData().add(new XYChart.Data(2.4, 37.6));

    sc.getData().addAll(series1);
    lc.getData(0.addAll(series2);
    Scene scene  = new Scene(sc, 500, 400);
    stage.setScene(scene);
    stage.show();
}

public static void main(String[] args) {
    launch(args);
}

问题是场景只能设置为sc或lc,而不能同时设置为sc或lc。有什么我可以做或者只是不可能吗?

由于

4 个答案:

答案 0 :(得分:14)

虽然@Mailkov解决方案很好,但它有一些缺点(重叠的图例,工具提示......)。

只是在同一个图表中混合散乱图表和折线图,这是一种非常简单的方法,使用css。

我们用两个系列创建一个LineChart。假设第一个是散射,第二个是线,并且用css我们摆脱了第一个的线,而(这是可选的)我们取出第二个的符号,并保留两个传说。

使用此css(chart.css):

.default-color0.chart-series-line { -fx-stroke: transparent; }
.default-color1.chart-series-line { -fx-stroke: red; }

.default-color0.chart-line-symbol { 
    -fx-background-color: white, green; 
}
.default-color1.chart-line-symbol { 
    -fx-background-color: transparent, transparent; 
}

.default-color0.chart-legend-item-symbol{
    -fx-background-color: green;
 }
.default-color1.chart-legend-item-symbol{
    -fx-background-color: red;
 }

和这段代码:

@Override
public void start(Stage stage) {
    final LineChart<Number,Number> sc = new LineChart<>(new NumberAxis(),new NumberAxis());

    XYChart.Series series1 = new XYChart.Series();
    series1.setName("Equities");
    series1.getData().add(new XYChart.Data(4.2, 193.2));
    series1.getData().add(new XYChart.Data(2.8, 33.6));
    series1.getData().add(new XYChart.Data(6.8, 23.6));

    XYChart.Series series2 = new XYChart.Series();
    series2.setName("Mutual funds");
    series2.getData().add(new XYChart.Data(5.2, 229.2));
    series2.getData().add(new XYChart.Data(2.4, 37.6));
    series2.getData().add(new XYChart.Data(6.4, 15.6));

    sc.setAnimated(false);
    sc.setCreateSymbols(true);

    sc.getData().addAll(series1, series2);

    Scene scene  = new Scene(sc, 500, 400);
    scene.getStylesheets().add(getClass().getResource("root.css").toExternalForm());
    stage.setScene(scene);
    stage.show();
}

我们将有一个简单的图表,其中包含两个不同的系列:

scatter and line chart

答案 1 :(得分:0)

我向Pane添加了两个ScatterChart,我将Opacity设置为(0.5)试试。

我希望我有所帮助。

final ScatterChart<Number,Number> sc = new
    ScatterChart<Number,Number>(xAxis,yAxis);
final LineChart<Number,Number> lc = new
    LineChart<Number,Number>(xAxis,yAxis);

XYChart.Series series1 = new XYChart.Series();
series1.setName("Equities");
series1.getData().add(new XYChart.Data(4.2, 193.2));
series1.getData().add(new XYChart.Data(2.8, 33.6));

XYChart.Series series2 = new XYChart.Series();
series2.setName("Mutual funds");
series2.getData().add(new XYChart.Data(5.2, 229.2));
series2.getData().add(new XYChart.Data(2.4, 37.6));

sc.getData().addAll(series1);
lc.getData().addAll(series2);

Pane pane=new Pane();
pane.getChildren().add(sc);
pane.getChildren().add(lc);

lc.setOpacity(0.5);

Scene scene  = new Scene(pane, 500, 400);
stage.setScene(scene);
stage.show();
}

public static void main(String[] args) {
  launch(args);
}

答案 2 :(得分:0)

我没有足够的“声誉”在 Mailkov 的回答下添加评论,但我认为提出的答案存在问题。

1)事实上,散乱图表的完整橙色点不是它们应该的位置(x = 2.8和x = 4.2)。

enter image description here

import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class Exemple158bis_JavaFX_Overlaid_Stacked_Charts extends Application {

@Override
public void start(Stage stage) throws Exception {

    NumberAxis xAxis = new NumberAxis();
    NumberAxis yAxis = new NumberAxis();

    final ScatterChart<Number, Number> sc = new ScatterChart<>(xAxis, yAxis);
    final LineChart<Number, Number> lc = new LineChart<>(xAxis, yAxis);

    XYChart.Series series1 = new XYChart.Series();
    series1.setName("Equities");
    series1.getData().add(new XYChart.Data(4.2, 193.2));
    series1.getData().add(new XYChart.Data(2.8, 33.6));

    XYChart.Series series2 = new XYChart.Series();
    series2.setName("Mutual funds");
    series2.getData().add(new XYChart.Data(5.2, 229.2));
    series2.getData().add(new XYChart.Data(2.4, 37.6));

    sc.getData().addAll(series1);
    lc.getData().addAll(series2);

    Pane pane = new Pane();
    pane.getChildren().add(sc);
    pane.getChildren().add(lc);

    lc.setOpacity(0.5);

    Scene scene = new Scene(pane, 800, 600);
    stage.setScene(scene);
    stage.show();
}

public static void main(String[] args) {
    launch(args);
}

}

2)解决方案是从头开始设置轴的边界:

NumberAxis xAxis = new NumberAxis(0, 5.5, 0.5); 
NumberAxis yAxis = new NumberAxis(0, 240, 10); 

enter image description here

答案 3 :(得分:0)

更简单的方法可能是对所有系列使用LineChart,然后为不希望线条绘制的任何系列添加以下行。下面的行更改系列0没有行,但对于其他系列,您只需更改数字(默认颜色 XX )来定义哪个系列。

<?php

session_start();
ob_start();

$host = "myhostname"; // Host name 
$username = "myusername"; // Mysql username 
$password = ""; // Mysql password 
$db_name = "mydatabase"; // Database name 
$tbl_name = "booking"; // Table name 

// Connect to server and select databse.
$conn = mysqli_connect($host, $username, $password)or die("cannot connect"); 
mysqli_select_db($conn, $db_name)or die("cannot select DB");

$sql = "SELECT * FROM $tbl_name";

$result = mysqli_query($conn, $sql);

?>

<script>
    !function() {

  var today = moment();

  function Calendar(selector, events) {
    this.el = document.querySelector(selector);
    this.events = events;
    this.current = moment().date(1);
    this.events.forEach(function(ev) {
     ev.date = moment(ev.date);
    });
    this.draw();
    var current = document.querySelector('.today');
    if(current) {
      var self = this;
      window.setTimeout(function() {
        self.openDay(current);
      }, 500);
    }

  }

  Calendar.prototype.draw = function() {
    //Create Header
    this.drawHeader();

    //Draw Month
    this.drawMonth();
  }

  Calendar.prototype.drawHeader = function() {
    var self = this;
    if(!this.header) {
      //Create the header elements
      this.header = createElement('div', 'header');
      this.header.className = 'header';

      this.title = createElement('h3');

      var right = createElement('div', 'right');
      right.addEventListener('click', function() { self.nextMonth(); });

      var left = createElement('div', 'left');
      left.addEventListener('click', function() { self.prevMonth(); });

      document.addEventListener("keydown", function(event) {
            if (event.keyCode === 39) {
                event.preventDefault();
                self.nextMonth();
            } else if (event.keyCode === 37) {
                event.preventDefault();
                self.prevMonth();
            }
      });

      //Append the Elements
      this.header.appendChild(this.title); 
      this.header.appendChild(right);
      this.header.appendChild(left);
      this.el.appendChild(this.header);
    }

    this.title.innerHTML = this.current.format('MMMM YYYY');
  }

  Calendar.prototype.drawMonth = function() {
    var self = this;


    if(this.month) {
      this.oldMonth = this.month;
      this.oldMonth.className = 'month out ' + (self.next ? 'next' : 'prev');
      this.oldMonth.addEventListener('webkitAnimationEnd', function() {
        self.oldMonth.parentNode.removeChild(self.oldMonth);
        self.month = createElement('div', 'month');
        self.backFill();
        self.currentMonth();
        self.fowardFill();
        self.el.appendChild(self.month);
        window.setTimeout(function() {
          self.month.className = 'month in ' + (self.next ? 'next' : 'prev');
        }, 16);
      });
    } else {
        this.month = createElement('div', 'month');
        this.el.appendChild(this.month);
        this.backFill();
        this.currentMonth();
        this.fowardFill();
        this.month.className = 'month new';
    }
  }

  Calendar.prototype.backFill = function() {
    var clone = this.current.clone();
    var dayOfWeek = clone.day();

    if(!dayOfWeek) { return; }

    clone.subtract('days', dayOfWeek);

    for(var i = dayOfWeek; i > 1 ; i--) {
      this.drawDay(clone.add('days', 1));
    }
  }

  Calendar.prototype.fowardFill = function() {
    var clone = this.current.clone().add('months', 1).subtract('days', 1);
    var dayOfWeek = clone.day();

    if(dayOfWeek === 7) { return; }

    for(var i = dayOfWeek; i < 7 ; i++) {
      this.drawDay(clone.add('days', 1));
    }
  }

  Calendar.prototype.currentMonth = function() {
    var clone = this.current.clone();

    while(clone.month() === this.current.month()) {
      this.drawDay(clone);
      clone.add('days', 1);
    }
  }

  Calendar.prototype.getWeek = function(day) {
    if(!this.week || day.day() === 1) {
      this.week = createElement('div', 'week');
      this.month.appendChild(this.week);
    }
  }

  Calendar.prototype.drawDay = function(day) {
    var self = this;
    this.getWeek(day);

    //Outer Day
    var outer = createElement('div', this.getDayClass(day));
    outer.addEventListener('click', function() {
      self.openDay(this);
    });

    //Day Name
    var name = createElement('div', 'day-name', day.format('ddd'));

    //Day Number
    var number = createElement('div', 'day-number', day.format('DD'));


    //Events
    var events = createElement('div', 'day-events');
    this.drawEvents(day, events);


    outer.appendChild(name);
    outer.appendChild(number);
    outer.appendChild(events);
    this.week.appendChild(outer);
  }

  Calendar.prototype.drawEvents = function(day, element) {
    if(day.month() === this.current.month()) {
      var todaysEvents = this.events.reduce(function(memo, ev) {
        if(ev.date.isSame(day, 'day')) {
          memo.push(ev);
        }
        return memo;
      }, []);

      todaysEvents.forEach(function(ev) {
        var evSpan = createElement('span', ev.color);
        element.appendChild(evSpan);
      });
    }
  }

  Calendar.prototype.getDayClass = function(day) {
    classes = ['day'];
    if(day.month() !== this.current.month()) {
      classes.push('other');
    } else if (today.isSame(day, 'day')) {
      classes.push('today');
    }
    return classes.join(' ');
  }

  Calendar.prototype.openDay = function(el) {
    var details, arrow;
    var dayNumber = +el.querySelectorAll('.day-number')[0].innerText || +el.querySelectorAll('.day-number')[0].textContent;
    var day = this.current.clone().date(dayNumber);

    var currentOpened = document.querySelector('.details');

    //Check to see if there is an open detais box on the current row
    if(currentOpened && currentOpened.parentNode === el.parentNode) {
      details = currentOpened;
      arrow = document.querySelector('.arrow');
    } else {
      //Close the open events on differnt week row
      //currentOpened && currentOpened.parentNode.removeChild(currentOpened);
      if(currentOpened) {
        currentOpened.addEventListener('webkitAnimationEnd', function() {
          currentOpened.parentNode.removeChild(currentOpened);
        });
        currentOpened.addEventListener('oanimationend', function() {
          currentOpened.parentNode.removeChild(currentOpened);
        });
        currentOpened.addEventListener('msAnimationEnd', function() {
          currentOpened.parentNode.removeChild(currentOpened);
        });
        currentOpened.addEventListener('animationend', function() {
          currentOpened.parentNode.removeChild(currentOpened);
        });
        currentOpened.className = 'details out';
      }

      //Create the Details Container
      details = createElement('div', 'details in');

      //Create the arrow
      var arrow = createElement('div', 'arrow');

      //Create the event wrapper

      details.appendChild(arrow);
      el.parentNode.appendChild(details);
    }

    var todaysEvents = this.events.reduce(function(memo, ev) {
      if(ev.date.isSame(day, 'day')) {
        memo.push(ev);
      }
      return memo;
    }, []);

    this.renderEvents(todaysEvents, details);

    arrow.style.left = el.offsetLeft - el.parentNode.offsetLeft + 27 + 'px';
  }

  Calendar.prototype.renderEvents = function(events, ele) {
    //Remove any events in the current details element
    var currentWrapper = ele.querySelector('.events');
    var wrapper = createElement('div', 'events in' + (currentWrapper ? ' new' : ''));

    events.forEach(function(ev) {
      var div = createElement('div', 'event');
      var span = createElement('span', '', ev.eventName);

      div.appendChild(span);
      wrapper.appendChild(div);
    });

    if(!events.length) {
      var div = createElement('div', 'event empty');
      var span = createElement('span', '', 'No Booking');

      div.appendChild(span);
      wrapper.appendChild(div);
    }

    if(currentWrapper) {
      currentWrapper.className = 'events out';
      currentWrapper.addEventListener('webkitAnimationEnd', function() {
        currentWrapper.parentNode.removeChild(currentWrapper);
        ele.appendChild(wrapper);
      });
      currentWrapper.addEventListener('oanimationend', function() {
        currentWrapper.parentNode.removeChild(currentWrapper);
        ele.appendChild(wrapper);
      });
      currentWrapper.addEventListener('msAnimationEnd', function() {
        currentWrapper.parentNode.removeChild(currentWrapper);
        ele.appendChild(wrapper);
      });
      currentWrapper.addEventListener('animationend', function() {
        currentWrapper.parentNode.removeChild(currentWrapper);
        ele.appendChild(wrapper);
      });
    } else {
      ele.appendChild(wrapper);
    }
  }

  Calendar.prototype.drawLegend = function() {
    var calendars = this.events.map(function(e) {
      return e.calendar + '|' + e.color;
    }).reduce(function(memo, e) {
      if(memo.indexOf(e) === -1) {
        memo.push(e);
      }
      return memo;
    }, []).forEach(function(e) {
      var parts = e.split('|');
      var entry = createElement('span', 'entry ' +  parts[1], parts[0]);
    });
  }

  Calendar.prototype.nextMonth = function() {
    this.current.add('months', 1);
    this.next = true;
    this.draw();
  }

  Calendar.prototype.prevMonth = function() {
    this.current.subtract('months', 1);
    this.next = false;
    this.draw();
  }

  window.Calendar = Calendar;

  function createElement(tagName, className, innerText) {
    var ele = document.createElement(tagName);
    if(className) {
      ele.className = className;
    }
    if(innerText) {
      ele.innderText = ele.textContent = innerText;
    }
    return ele;
  }
}();

!function() {
  var data = [
      <?php
      $colour = 'blue';
      while($rows = mysqli_fetch_array($result)) {
          if($rows['status'] == 'Confirmed') {
              $colour = 'red';
          } else {
              $colour = 'blue';
          }
          ?>
          { eventName: 'Booking Start', calendar: 'Booking <?php echo $rows['status']; ?>', color: '<?php echo $colour; ?>', date: '<?php echo $rows['arrive_date']?>'},
          <?php
          $date = date('Y-m-d', strtotime($rows['arrive_date']));
          while ($date < date('Y-m-d', strtotime($rows['depart_date']. '-1 day'))) {
              $date++;
              ?>
              { eventName: 'Booked', calendar: 'Booking <?php echo $rows['status']; ?>', color: '<?php echo $colour; ?>', date: '<?php echo $date;?>'},
              <?php
          }
          ?>


    { eventName: 'Booking End', calendar: 'Booking <?php echo $rows['status']; ?>', color: '<?php echo $colour; ?>', date: '<?php echo $rows['depart_date']; ?>'},

    <?php
      }
      ?>
  ];

  var calendar = new Calendar('#calendar', data);

}();
</script>

与@JoséPereda非常相似,但也许比搞乱CSS文件容易一些。