From f8eb546519645f44cd694ce67b3e42119ff93610 Mon Sep 17 00:00:00 2001 From: Benedikt Zoennchen Date: Tue, 28 May 2019 11:31:21 +0200 Subject: [PATCH] update TrajectoryMetric-notebook: computation of the velocity, densities and the fundamental diagram. Cutting of trajectories. --- Tools/Notebooks/TrajectoryMetric.ipynb | 344 +++++++++++++++++++++---- 1 file changed, 293 insertions(+), 51 deletions(-) diff --git a/Tools/Notebooks/TrajectoryMetric.ipynb b/Tools/Notebooks/TrajectoryMetric.ipynb index 336524dda..48a0bc846 100644 --- a/Tools/Notebooks/TrajectoryMetric.ipynb +++ b/Tools/Notebooks/TrajectoryMetric.ipynb @@ -11,8 +11,11 @@ "import numpy as np\n", "import pandas as pd\n", "import math\n", + "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "from matplotlib.lines import Line2D\n", + "import seaborn as sns\n", + "sns.set(style=\"ticks\")\n", "\n", "from IPython.core.display import display, HTML\n", "display(HTML(''))" @@ -43,6 +46,8 @@ " for pedId in trajectories:\n", " trajectory_append(pedId, trajectories[pedId], llist)\n", " dataframe = pd.DataFrame(llist, columns=['pedestrianId','startX','startY','startTime','endX','endY','endTime'])\n", + " dataframe[\"distance\"] = np.sqrt(np.square(dataframe[\"endX\"] - dataframe[\"startX\"]) + np.square(dataframe[\"endY\"] - dataframe[\"startY\"]))\n", + " dataframe[\"velocity\"] = dataframe[\"distance\"] / (dataframe[\"endTime\"] - dataframe[\"startTime\"])\n", " return dataframe" ] }, @@ -82,7 +87,6 @@ "source": [ "def load_experiment(file):\n", " fps = 16\n", - " pad = pd.DataFrame([[np.nan, np.nan, np.nan, np.nan, np.nan]], columns=['pedestrianId', 'timeStep', 'x', 'y', 'e'])\n", " data = pd.read_csv(\n", " file, \n", " sep=' ', \n", @@ -90,17 +94,34 @@ " index_col=False, \n", " header=None, \n", " skiprows=0)\n", - " \n", - " cc = pd.concat([pad, data], ignore_index=True)\n", " \n", - " data['endX'] = data['x'] / 100 + 18.7\n", - " data['endY'] = data['y'] / 100 + 4.2\n", - " data['startX'] = cc['x'] / 100 + 18.7\n", - " data['startY'] = cc['y'] / 100 + 4.2\n", - " data['startTime'] = data['timeStep'] / fps - 1/fps\n", - " data['endTime'] = data['timeStep'] / fps\n", - " data = data.drop(columns=['timeStep','x','y','e'])\n", - " return data\n", + " rows = []\n", + " #print(trajectories)\n", + " last_ped_id = None\n", + " lastX = None\n", + " lastY = None\n", + " for row in data.itertuples():\n", + " endX = row.x / 100 + 18.7\n", + " endY = row.y / 100 + 4.2\n", + " startTime = row.timeStep / fps - 1/fps\n", + " endTime = row.timeStep / fps\n", + " if last_ped_id is None or last_ped_id != row.pedestrianId:\n", + " startX = np.nan\n", + " startY = np.nan\n", + " distance = np.nan\n", + " velocity = np.nan\n", + " else:\n", + " startX = lastX / 100 + 18.7\n", + " startY = lastY / 100 + 4.2\n", + " distance = np.sqrt(np.square(endX - startX) + np.square(endY - startY))\n", + " velocity = distance / (endTime - startTime)\n", + " last_ped_id = row.pedestrianId\n", + " lastX = row.x\n", + " lastY = row.y\n", + " \n", + " rows.append([row.pedestrianId, startX, startY, endX, endY, startTime, endTime, distance, velocity])\n", + " dataframe = pd.DataFrame(rows, columns=['pedestrianId', 'startX', 'startY', 'endX', 'endY','startTime','endTime','distance','velocity'])\n", + " return dataframe\n", " \n", "def to_trajectories(data):\n", " trajectories = dict({})\n", @@ -205,11 +226,33 @@ "trajectoriesReal[\"timeDelta\"] = trajectoriesReal[\"endTime\"] - trajectoriesReal[\"startTime\"]\n", "evacuation_time = trajectoriesReal.groupby([\"pedestrianId\"])[\"timeDelta\"].sum()\n", "\n", + "# trajectories starting from left\n", + "cut_minX = trajectoriesReal[trajectoriesReal[\"endX\"] < 15].groupby([\"pedestrianId\"])[\"endX\"].min().max()\n", + "\n", + "# trajectories starting from right\n", + "cut_maxX = trajectoriesReal[trajectoriesReal[\"endX\"] > 21].groupby([\"pedestrianId\"])[\"endX\"].max().min()\n", + "cut_maxY = trajectoriesReal.groupby([\"pedestrianId\"])[\"endY\"].max().min()\n", + "\n", "print(\"Evacuation time (real data)\")\n", "print(\"- mean: {:.2f} [s]\".format(evacuation_time.mean()))\n", "print(\"- std: {:.2f} [s]\".format(evacuation_time.std()))\n", "print(\"- min: {:.2f} [s]\".format(evacuation_time.min()))\n", - "print(\"- max: {:.2f} [s]\".format(evacuation_time.max()))" + "print(\"- max: {:.2f} [s]\".format(evacuation_time.max()))\n", + "print(\"- minX: {:.2f} [m]\".format(cut_minX))\n", + "print(\"- maxX: {:.2f} [m]\".format(cut_maxX))\n", + "print(\"- maxY: {:.2f} [m]\".format(cut_maxY))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rightX = trajectoriesReal[trajectoriesReal.endX < 16].groupby([\"pedestrianId\"])[\"endX\"].min().max()\n", + "leftX = trajectoriesReal[trajectoriesReal.endX > 16].groupby([\"pedestrianId\"])[\"endX\"].max().min()\n", + "topY = trajectoriesReal.groupby([\"pedestrianId\"])[\"endY\"].max().min()\n", + "topY" ] }, { @@ -244,9 +287,15 @@ " query = 'pedestrianId == ' + str(pedId)\n", " return trajectories.query(query)\n", "\n", + "def get_trajectories(t, trajectories):\n", + " return trajectories[np.logical_and(trajectories.startTime <= t, trajectories.endTime >= t)]\n", + "\n", "def get_pedestrianIds(trajectories):\n", " return trajectories['pedestrianId'].unique()\n", "\n", + "#def get_velocity(trajectories, t, dt):\n", + "# trajectories[np.logical_and(trajectory.endX >= xmax, trajectory.startX < xmax)]\n", + "\n", "def get_footstep(trajectory, i):\n", " \"\"\"returns the i-ths footstep.\"\"\"\n", " return trajectory.iloc[i];\n", @@ -287,7 +336,96 @@ "\n", "def cut_soft(trajectory, sTime, eTime):\n", " query = 'endTime > ' + str(sTime) + ' and startTime < ' + str(eTime)\n", - " return trajectory.query(query)" + " return trajectory.query(query)\n", + "\n", + "def cuthead_trajectory_by(trajectory, ymin, ymax):\n", + " i1 = trajectory[trajectory.endY >= ymax].index.min()\n", + " i2 = trajectory[trajectory.endY <= ymin].index.min()\n", + " #assert (i1 is np.nan and i2 is not np.nan) or (i1 is not np.nan and i2 is np.nan)\n", + " y = ymax if i2 is np.nan or (i1 is not np.nan and i1 < i2) else ymin\n", + " i = i1 if y == ymax else i2\n", + " #print(i)\n", + " # cut the footstep at the tail to exactly fit xmin or xmax\n", + " fs = trajectory.loc[i]\n", + " start = np.array([fs[\"startX\"], fs[\"startY\"]])\n", + " end = np.array([fs[\"endX\"], fs[\"endY\"]])\n", + " endTime = fs[\"endTime\"]\n", + " startTime = fs[\"startTime\"]\n", + " distance = fs[\"distance\"]\n", + " velocity = fs[\"velocity\"]\n", + " d = end - start\n", + " if abs(fs[\"endY\"] - fs[\"startY\"]) > 0.00001:\n", + " r = (y - fs[\"startY\"]) / (fs[\"endY\"] - fs[\"startY\"])\n", + " end = start + (d * r)\n", + " time = fs[\"endTime\"] - fs[\"startTime\"]\n", + " endTime = fs[\"startTime\"] + (time * r)\n", + " distance = np.linalg.norm(end - start)\n", + " velocity = distance / (endTime - startTime)\n", + " \n", + " df = trajectory.loc[:i-1]\n", + " llist = [[fs[\"pedestrianId\"],fs[\"startX\"],fs[\"startY\"],fs[\"startTime\"],end[0],end[1],endTime,distance,velocity]]\n", + " df_head = pd.DataFrame(llist, columns=['pedestrianId','startX','startY','startTime','endX','endY','endTime','distance','velocity'])\n", + " df = df.append(df_head, ignore_index=True)\n", + " return df\n", + "\n", + "def cuttail_trajectory_by(trajectory, xmin, xmax):\n", + " #i1 = trajectory[np.logical_and(trajectory.endX >= xmax, trajectory.startX < xmax)].index.max()\n", + " i1 = trajectory[trajectory.endX >= xmax].index.max()\n", + " i2 = trajectory[trajectory.endX <= xmin].index.max()\n", + " #assert (i1 is np.nan and i2 is not np.nan) or (i1 is not np.nan and i2 is np.nan)\n", + " x = xmax if i2 is np.nan or (i1 is not np.nan and i1 > i2) else xmin\n", + " i = i1 if x == xmax else i2\n", + " i = i+1\n", + " # cut the footstep at the tail to exactly fit xmin or xmax\n", + " fs = trajectory.loc[i]\n", + " start = np.array([fs[\"startX\"], fs[\"startY\"]])\n", + " end = np.array([fs[\"endX\"], fs[\"endY\"]])\n", + " startTime = fs[\"startTime\"]\n", + " endTime = fs[\"endTime\"]\n", + " distance = fs[\"distance\"]\n", + " velocity = fs[\"velocity\"]\n", + " d = end - start\n", + " if abs(fs[\"endX\"] - fs[\"startX\"]) > 0.00001:\n", + " r = (x - fs[\"startX\"]) / (fs[\"endX\"] - fs[\"startX\"])\n", + " end = start + (d * r)\n", + " time = fs[\"endTime\"] - fs[\"startTime\"]\n", + " endTime = fs[\"startTime\"] + (time * r)\n", + " distance = np.linalg.norm(end - start)\n", + " velocity = distance / (endTime - startTime)\n", + " \n", + " df = trajectory.loc[i+1:]\n", + " llist = [[fs[\"pedestrianId\"],fs[\"startX\"],fs[\"startY\"],fs[\"startTime\"],end[0],end[1],endTime,distance,velocity]]\n", + " df_tail = pd.DataFrame(llist, columns=['pedestrianId','startX','startY','startTime','endX','endY','endTime','distance','velocity'])\n", + " df_tail = df_tail.append(df, ignore_index=True)\n", + " return df_tail\n", + "\n", + "def cuthead_by(trajectories, ymin, ymax):\n", + " df = pd.DataFrame([], columns=['pedestrianId','startX','startY','startTime','endX','endY','endTime'])\n", + " pedIds = get_pedestrianIds(trajectories)\n", + " for pedId in pedIds:\n", + " df = df.append(cuthead_trajectory_by(get_trajectory(pedId, trajectories), ymin, ymax), ignore_index=True)\n", + " return df\n", + "\n", + "def cuttail_by(trajectories, xmin, xmax):\n", + " df = pd.DataFrame([], columns=['pedestrianId','startX','startY','startTime','endX','endY','endTime'])\n", + " pedIds = get_pedestrianIds(trajectories)\n", + " for pedId in pedIds:\n", + " df = df.append(cuttail_trajectory_by(get_trajectory(pedId, trajectories), xmin, xmax), ignore_index=True)\n", + " return df\n", + "\n", + "def cut(trajectories):\n", + " df = cuttail_by(trajectories, cut_minX, cut_maxX)\n", + " df = cuthead_by(df, -1000, cut_maxY)\n", + " return df\n", + "\n", + "traj = get_trajectory(2, trajectoriesReal)\n", + "ts = traj[traj.endX > 22].index.max()\n", + "ts\n", + "#cut(trajectoriesReal)\n", + "#cuttail_by(trajectoriesReal, 13, 22).head()\n", + "#cuthead_by(trajectoriesReal, 0, 4).tail()\n", + "#traj.loc[100]\n", + "#trajectoriesReal.tail()" ] }, { @@ -313,6 +451,9 @@ " dy = y1-y2;\n", " return np.sqrt(dx*dx + dy*dy);\n", "\n", + "def mean_velocity_at(t, trajectories):\n", + " return get_trajectories(t, trajectories)['velocity'].mean()\n", + "\n", "def trajectory_length(trajectory):\n", " \"\"\"Euclidean length of a trajectory.\"\"\"\n", " dx = trajectory['startX']-trajectory['endX']\n", @@ -458,7 +599,6 @@ "#foot_step_length(get_footstep(get_trajectory(1, ptrajectories), 0))\n", "#trajectory_length(get_trajectory(1, ptrajectories))\n", "#trajectory_speed(get_trajectory(1, ptrajectories))\n", - "cutTraj = cut(get_trajectory(1, ptrajectories), 0.0, 10.0)[['startTime', 'endTime']]\n", "#cutTraj.mask(cutTraj['startTime'] <= 4 and 4 > cutTraj['endTime'])\n", "#start_time(get_trajectory(1, ptrajectories))\n", "#trajectories_position(ptrajectories, [1,2,3,4]).head()\n", @@ -468,7 +608,11 @@ "trajPos2 = trajPos2[~np.isnan(trajPos2.x)]\n", "euclid_path_length(trajPos1, trajPos2)\n", "euclid_len(ptrajectories,0,10000)\n", - "#print(total_inter_agent(ptrajectories, ptrajectories, [1,2]))" + "#print(total_inter_agent(ptrajectories, ptrajectories, [1,2]))\n", + "t = 0.5\n", + "ttraj = ptrajectories[np.logical_and(ptrajectories.startTime <= t, ptrajectories.endTime >= t)]\n", + "#ptrajectories[\"velocity\"] = numpy.linalg.norm(\n", + "get_trajectories(0.5, ptrajectories).head()" ] }, { @@ -506,7 +650,26 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Plot trajectories" + "Here we cut all trajectory data such each left trajectory starts at the same $x$-coordinate and each right trajectory starts at the same $x$-coordinate. In addition each trajectory ends a the same $y$-coordinate." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "c_real_trajectories = cut(trajectoriesReal)\n", + "c_sim_trajecotories = cut(ptrajectories)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot trajectories\n", + "\n", + "Now we plot the cut trajectories." ] }, { @@ -517,13 +680,14 @@ "source": [ "def to_line(trajectory, xleft):\n", " \"\"\"Transforms a trajectory into a Line2D.\"\"\"\n", + " current_palette = sns.color_palette()\n", " x = trajectory['endX'].values\n", " y = trajectory['endY'].values\n", " if x[0] < xleft:\n", " c = current_palette[2]\n", " else:\n", " c = current_palette[0]\n", - " return x, y, Line2D(x, y, color=c, linewidth=0.2)\n", + " return x, y, Line2D(x, y, color=c, linewidth=0.3)\n", "\n", "def add_lines(trajectories, xleft, ax):\n", " grouped = trajectories.groupby(['pedestrianId'])\n", @@ -538,60 +702,83 @@ "metadata": {}, "outputs": [], "source": [ - "import seaborn as sns\n", - "sns.set(style=\"ticks\")\n", - "\n", - "current_palette = sns.color_palette()\n", - "\n", "x_vcenter = 17.5\n", "y_vcenter = 5.2\n", "\n", - "fig1 = plt.figure(figsize=(10,10))\n", - "ax1 = fig1.add_subplot(111)\n", - "add_lines(trajectoriesReal, 14, ax1)\n", - "ax1.set_xlim(x_vcenter-5, x_vcenter+5)\n", - "ax1.set_ylim(y_vcenter-4, y_vcenter+4)\n", - "ax1.set_aspect(1)\n", + "fig_trajectories = plt.figure(figsize=(10,10))\n", + "ax1_trajectories = fig_trajectories.add_subplot(121)\n", + "add_lines(c_real_trajectories, 16, ax1_trajectories)\n", + "ax1_trajectories.set_xlim(x_vcenter-5, x_vcenter+6)\n", + "ax1_trajectories.set_ylim(y_vcenter-4, y_vcenter+4)\n", + "ax1_trajectories.set_aspect(1)\n", "\n", - "fig2 = plt.figure(figsize=(10,10))\n", - "ax2 = fig2.add_subplot(111)\n", - "add_lines(ptrajectories, 14, ax2)\n", - "ax2.set_xlim(x_vcenter-5, x_vcenter+5)\n", - "ax2.set_ylim(y_vcenter-4, y_vcenter+4)\n", - "ax2.set_aspect(1)\n", + "ax2_trajectories = fig_trajectories.add_subplot(122, sharey=ax1)\n", + "add_lines(c_sim_trajecotories, 16, ax2_trajectories)\n", + "plt.setp(ax2_trajectories.get_yticklabels(), visible=False)\n", + "ax2_trajectories.set_xlim(x_vcenter-5, x_vcenter+6)\n", + "ax2_trajectories.set_ylim(y_vcenter-4, y_vcenter+4)\n", + "ax2_trajectories.set_aspect(1)\n", "\n", "plt.show()" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "times = np.arange(0,80,2)\n", - "y = list(map(lambda t: inter_agent_d(trajectories, t), times))\n", - "plt.plot(times, y, 'o')" + "# Plot velocities\n", + "\n", + "The following code plots the mean (over all agents / pedestrians) velocity at $t = 0, 0.5, \\ldots 70$ and the corresponding standard deviation. " ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "start_time(trajectories[1])\n", - "print(max_start_time(trajectories))\n", - "print(min_end_time(trajectories))" + "times = np.arange(0, 70, 0.2)\n", + "velocity1 = list(map(lambda t: mean_velocity_at(t, c_real_trajectories), times))\n", + "std1 = list(map(lambda t: get_trajectories(t, c_real_trajectories)['velocity'].std(),times))\n", + "\n", + "velocity2 = list(map(lambda t: mean_velocity_at(t, c_sim_trajecotories), times))\n", + "std2 = list(map(lambda t: get_trajectories(t, c_sim_trajecotories)['velocity'].std(),times))\n", + "\n", + "#df1 = pd.DataFrame({'velocity':vel1, 'time':times})\n", + "#df2 = pd.DataFrame({'velocity':vel2, 'time':times})\n", + "\n", + "fig_velocities = plt.figure(figsize=(10,5))\n", + "ax1_velocities = fig_velocities.add_subplot(121)\n", + "ax1_velocities.set_xlim(min(times),max(times))\n", + "ax1_velocities.set_ylim(0,2)\n", + "ax1_velocities.set_xlabel(\"time\")\n", + "ax1_velocities.set_ylabel(\"velocity\")\n", + "plt.errorbar(times, velocity1, std1, ecolor=sns.color_palette()[2])\n", + "ax2_velocities = fig_velocities.add_subplot(122)\n", + "ax2_velocities.set_xlim(min(times),max(times))\n", + "ax2_velocities.set_ylim(0,2)\n", + "ax2_velocities.set_xlabel(\"time\")\n", + "ax2_velocities.set_ylabel(\"velocity\")\n", + "plt.errorbar(times, velocity2, std2, ecolor=sns.color_palette()[2])\n", + "plt.show()\n", + "#ax6 = fig2.add_subplot(212)\n", + "#sns.relplot(x=\"startTime\", y=\"velocity\", kind=\"line\", ci=\"sd\", data=c_real_trajectories, ax=ax) #linewidth=0.5\n", + "#sns.relplot(x=\"startTime\", y=\"velocity\", kind=\"line\", ci=\"sd\", data=c_sim_trajecotories, ax=ax6)\n", + "#fmri = sns.load_dataset(\"fmri\")\n", + "#fmri\n", + "#c_real_trajectories[c_real_trajectories.endTime == 5.0]\n", + "#c_real_trajectories" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "print(position(map(lambda traj: traj[\"startTime\"], trajectories)[1], 0))" + "# Plot densities\n", + "\n", + "The following code plots the density inside the measurement area at $t = 0, 0.5, \\ldots 70$ and the corresponding standard deviation." ] }, { @@ -600,7 +787,54 @@ "metadata": {}, "outputs": [], "source": [ - "pd.DataFrame([[1,2,3,4,5],[6,7,8,9,10]], columns=['pedestrianId','timeStep','x','y','time'])" + "def contains(x,y,rect):\n", + " #ma = mpl.patches.Rectangle((16.3,6.0), 2.4, 2.0)\n", + " return x >= rect.get_x() and y >= rect.get_y() and x <= rect.get_x() + rect.get_width() and y <= rect.get_y() + rect.get_height()\n", + "\n", + "def filter_by_time_and_place(t, rect, trajectories):\n", + " \"\"\"returns a subset of trajectories i.e. at most one footstep for each pedestrian / agent such that the footstep the position (x,y) is the position of the\n", + " agent at the time t contained in the rectanlge rect. Two new colums will be added for x and y.\"\"\"\n", + " traj = get_trajectories(t, trajectories)\n", + " traj.loc[:,'x'] = traj['startX'] + (traj['endX'] - traj['startX']) * (t - traj['startTime']) / (traj['endTime'] - traj['startTime'])\n", + " traj.loc[:,'y'] = traj['startY'] + (traj['endY'] - traj['startY']) * (t - traj['startTime']) / (traj['endTime'] - traj['startTime'])\n", + " traj = traj[traj.apply(lambda x: contains(x['x'], x['y'],rect), axis=1)]\n", + " return traj\n", + "\n", + "def density(t, rect, trajectories):\n", + " area = rect.get_width() * rect.get_height()\n", + " traj = filter_by_time_and_place(t, rect, trajectories)\n", + " number_of_peds = len(traj)\n", + " if number_of_peds == 0:\n", + " return 0\n", + " else:\n", + " return number_of_peds / area\n", + " \n", + "measurementArea = mpl.patches.Rectangle((16.3,6.0), 2.4, 2.0)\n", + "density1 = list(map(lambda t: density(t, measurementArea, c_real_trajectories), times))\n", + "density2 = list(map(lambda t: density(t, measurementArea, c_sim_trajecotories), times))\n", + "\n", + "fig_density = plt.figure(figsize=(10,5))\n", + "ax1_density = fig_density.add_subplot(121)\n", + "ax1_density.set_xlim(min(times),max(times))\n", + "ax1_density.set_ylim(0,6)\n", + "ax1_density.set_xlabel(\"time\")\n", + "ax1_density.set_ylabel(\"density\")\n", + "plt.plot(times, density1)\n", + "\n", + "ax2_density = fig_density.add_subplot(122)\n", + "ax2_density.set_xlim(min(times),max(times))\n", + "ax2_density.set_xlabel(\"time\")\n", + "ax2_density.set_ylabel(\"density\")\n", + "ax2_density.set_ylim(0,6)\n", + "plt.plot(times, density2)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot of fundamental diagrams using method c" ] }, { @@ -609,7 +843,15 @@ "metadata": {}, "outputs": [], "source": [ - "to_postVis(data)" + "fig_fundamental = plt.figure(figsize=(10,5))\n", + "ax1_fundamental = fig_fundamental.add_subplot(111)\n", + "#ax1_fundamental.set_xlim(min(times),max(times))\n", + "ax1_fundamental.set_ylim(0,6)\n", + "ax1_fundamental.set_xlabel(\"velocity\")\n", + "ax1_fundamental.set_ylabel(\"density\")\n", + "plt.plot(velocity1, density1, '*')\n", + "plt.plot(velocity2, density2, '*')\n", + "plt.show()" ] } ], @@ -629,7 +871,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.6.8" } }, "nbformat": 4, -- GitLab